LCOV - code coverage report
Current view: top level - src/netbuild - NBEdge.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.3 % 2311 2134
Test Date: 2026-04-16 16:39:47 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-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    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       571805 : NBEdge::Connection::getInternalLaneID() const {
      93      1143610 :     return id + "_" + toString(internalLaneIndex);
      94              : }
      95              : 
      96              : 
      97              : std::string
      98        86385 : NBEdge::Connection::getInternalViaLaneID() const {
      99       172770 :     return viaID + "_" + toString(internalViaLaneIndex);
     100              : }
     101              : 
     102              : 
     103              : std::string
     104          351 : NBEdge::Connection::getDescription(const NBEdge* parent) const {
     105         2106 :     return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
     106         1053 :             + (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
     107              : }
     108              : 
     109              : 
     110       787253 : NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
     111       787253 :     fromLane(fromLane_),
     112       787253 :     toEdge(toEdge_),
     113       787253 :     toLane(toLane_),
     114       787253 :     mayDefinitelyPass(mayDefinitelyPass_),
     115       787253 :     customLength(myDefaultConnectionLength),
     116      1574401 :     id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
     117       787253 : }
     118              : 
     119              : 
     120       174684 : NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
     121       174684 :     speed(e->getSpeed()),
     122       174684 :     friction(e->getFriction()),
     123       174684 :     permissions(SVCAll),
     124       174684 :     preferred(0),
     125       174684 :     changeLeft(SVCAll),
     126       174684 :     changeRight(SVCAll),
     127       174684 :     endOffset(e->getEndOffset()),
     128       174684 :     laneStopOffset(e->getEdgeStopOffset()),
     129       174684 :     width(e->getLaneWidth()),
     130       174684 :     accelRamp(false),
     131       349368 :     connectionsDone(false) {
     132       174684 :     if (origID_ != "") {
     133        33993 :         setParameter(SUMO_PARAM_ORIGID, origID_);
     134              :     }
     135       174684 : }
     136              : 
     137              : 
     138              : /* -------------------------------------------------------------------------
     139              :  * NBEdge::ToEdgeConnectionsAdder-methods
     140              :  * ----------------------------------------------------------------------- */
     141              : void
     142       429349 : NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
     143              :     // check
     144              :     assert((int)myTransitions.size() > virtEdge);
     145              :     // get the approached edge
     146       429349 :     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       429349 :     if (i != myConnections.end()) {
     153              :         // if there were already lanes assigned, get them
     154       338591 :         lanes = (*i).second;
     155              :     }
     156              : 
     157              :     // check whether the current lane was already used to connect the currently
     158              :     //  regarded approached edge
     159       429349 :     std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
     160       429349 :     if (j == lanes.end()) {
     161              :         // if not, add it to the list
     162       101488 :         lanes.push_back(lane);
     163              :     }
     164              :     // set information about connecting lanes
     165       429349 :     myConnections[succEdge] = lanes;
     166       429349 : }
     167              : 
     168              : 
     169              : 
     170              : /* -------------------------------------------------------------------------
     171              :  * NBEdge::MainDirections-methods
     172              :  * ----------------------------------------------------------------------- */
     173        46408 : 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        46408 :     const NBEdge* straight = nullptr;
     176       137226 :     for (const NBEdge* const out : outgoing) {
     177        90818 :         const SVCPermissions outPerms = out->getPermissions();
     178        90999 :         for (const int l : availableLanes) {
     179        90885 :             if ((parent->myLanes[l].permissions & outPerms) != 0) {
     180        90704 :                 if (straight == nullptr || sorter(out, straight)) {
     181        68495 :                     straight = out;
     182              :                 }
     183              :                 break;
     184              :             }
     185              :         }
     186              :     }
     187        46408 :     if (straight == nullptr) {
     188         4113 :         return;
     189              :     }
     190        46360 :     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        46360 :     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        46360 :     if (NBNode::isTrafficLight(to->getType()) &&
     201         4368 :             (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
     202         4065 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     203         4065 :         return;
     204              :     }
     205        42295 :     if (outgoing[0]->getJunctionPriority(to) == 1) {
     206        22693 :         myDirs.push_back(MainDirections::Direction::RIGHTMOST);
     207              :     }
     208              :     // check whether the left turn has a higher priority
     209        42295 :     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        22920 :         if (outgoing.back()->getPriority() > straight->getPriority() ||
     214              :                 outgoing.back()->getNumLanes() > straight->getNumLanes()) {
     215         1228 :             myDirs.push_back(MainDirections::Direction::LEFTMOST);
     216              :         }
     217              :     }
     218              :     // check whether the forward direction has a higher priority
     219              :     // check whether it has a higher priority and is going straight
     220        42295 :     if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
     221        20506 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     222              :     }
     223            0 : }
     224              : 
     225              : 
     226        46408 : NBEdge::MainDirections::~MainDirections() {}
     227              : 
     228              : 
     229              : bool
     230        46360 : NBEdge::MainDirections::empty() const {
     231        46360 :     return myDirs.empty();
     232              : }
     233              : 
     234              : 
     235              : bool
     236       173431 : NBEdge::MainDirections::includes(Direction d) const {
     237       173431 :     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       195503 : NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
     246       195503 :     if (c1.toEdge != c2.toEdge) {
     247       154619 :         return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
     248              :     }
     249        40884 :     return c1.toLane < c2.toLane;
     250              : }
     251              : 
     252              : 
     253              : /* -------------------------------------------------------------------------
     254              :  * NBEdge-methods
     255              :  * ----------------------------------------------------------------------- */
     256        15646 : 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        15646 :                LaneSpreadFunction spread, const std::string& streetName) :
     260        15646 :     Named(StringUtils::convertUmlaute(id)),
     261        15646 :     myStep(EdgeBuildingStep::INIT),
     262        15646 :     myType(StringUtils::convertUmlaute(type)),
     263        15646 :     myFrom(from), myTo(to),
     264        15646 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     265        15646 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     266        15646 :     myDistance(0),
     267        15646 :     myTurnDestination(nullptr),
     268        15646 :     myPossibleTurnDestination(nullptr),
     269        15646 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     270        15646 :     myLaneSpreadFunction(spread), myEndOffset(endOffset),
     271        15646 :     myLaneWidth(laneWidth),
     272        15646 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     273        15646 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     274        15646 :     myStreetName(streetName),
     275        15646 :     mySignalPosition(Position::INVALID),
     276        15646 :     mySignalNode(nullptr),
     277        15646 :     myIsOffRamp(false),
     278        15646 :     myIsBidi(false),
     279        46938 :     myIndex(-1) {
     280        15646 :     init(nolanes, false, "");
     281        15646 : }
     282              : 
     283              : 
     284       117113 : 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       117113 :                bool tryIgnoreNodePositions) :
     292       117113 :     Named(StringUtils::convertUmlaute(id)),
     293       117113 :     myStep(EdgeBuildingStep::INIT),
     294       117114 :     myType(StringUtils::convertUmlaute(type)),
     295       117113 :     myFrom(from), myTo(to),
     296       117113 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     297       117113 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     298       117113 :     myDistance(0),
     299       117113 :     myTurnDestination(nullptr),
     300       117113 :     myPossibleTurnDestination(nullptr),
     301       117113 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     302       117113 :     myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
     303       117113 :     myLaneWidth(laneWidth),
     304       117113 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     305       117113 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     306       117113 :     myStreetName(streetName),
     307       117113 :     mySignalPosition(Position::INVALID),
     308       117113 :     mySignalNode(nullptr),
     309       117113 :     myIsOffRamp(false),
     310       117113 :     myIsBidi(false),
     311       351339 :     myIndex(-1) {
     312       117113 :     init(nolanes, tryIgnoreNodePositions, origID);
     313       117125 : }
     314              : 
     315              : 
     316         2712 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
     317         2712 :     Named(StringUtils::convertUmlaute(id)),
     318         2712 :     myStep(EdgeBuildingStep::INIT),
     319         2712 :     myType(tpl->getTypeID()),
     320         2712 :     myFrom(from), myTo(to),
     321         2712 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     322         2712 :     myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
     323         2712 :     myFriction(tpl->getFriction()),
     324         2712 :     myDistance(0),
     325         2712 :     myTurnDestination(nullptr),
     326         2712 :     myPossibleTurnDestination(nullptr),
     327         2712 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     328              :     myGeom(geom),
     329         2712 :     myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
     330         2712 :     myEndOffset(tpl->getEndOffset()),
     331         2712 :     myEdgeStopOffset(tpl->getEdgeStopOffset()),
     332         2712 :     myLaneWidth(tpl->getLaneWidth()),
     333         2712 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     334         2712 :     myAmInTLS(false),
     335         2712 :     myAmMacroscopicConnector(false),
     336         2712 :     myStreetName(tpl->getStreetName()),
     337         2712 :     mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
     338         2712 :     mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
     339         2712 :     myIsOffRamp(false),
     340         2712 :     myIsBidi(tpl->myIsBidi),
     341         5424 :     myIndex(-1) {
     342         3850 :     init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
     343         6635 :     for (int i = 0; i < getNumLanes(); i++) {
     344         3923 :         const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
     345         3923 :         setSpeed(i, tpl->getLaneSpeed(tplIndex));
     346         3923 :         setFriction(i, tpl->getLaneFriction(tplIndex));
     347         3923 :         setPermissions(tpl->getPermissions(tplIndex), i);
     348         3923 :         setLaneWidth(i, tpl->myLanes[tplIndex].width);
     349         3923 :         setLaneType(i, tpl->myLanes[tplIndex].type);
     350         3923 :         myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
     351         3923 :         if (to == tpl->myTo) {
     352         1472 :             setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
     353         1472 :             setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
     354              :         }
     355              :     }
     356         2712 :     if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
     357            3 :         myLoadedLength = tpl->myLoadedLength;
     358              :     }
     359         2712 :     updateParameters(tpl->getParametersMap());
     360         2712 : }
     361              : 
     362              : 
     363         2548 : NBEdge::NBEdge() :
     364              :     Named("DUMMY"),
     365         2548 :     myStep(EdgeBuildingStep::INIT),
     366         2548 :     myFrom(nullptr), myTo(nullptr),
     367         2548 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     368         2548 :     myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
     369         2548 :     myDistance(0),
     370         2548 :     myTurnDestination(nullptr),
     371         2548 :     myPossibleTurnDestination(nullptr),
     372         2548 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     373         2548 :     myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
     374         2548 :     myEndOffset(0),
     375         2548 :     myEdgeStopOffset(StopOffset()),
     376         2548 :     myLaneWidth(0),
     377         2548 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     378         2548 :     myAmInTLS(false),
     379         2548 :     myAmMacroscopicConnector(false),
     380         2548 :     mySignalPosition(Position::INVALID),
     381         7644 :     mySignalNode(nullptr) {
     382         2548 : }
     383              : 
     384              : 
     385              : void
     386         2251 : 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         2251 :     if (myFrom != from) {
     393           10 :         myFrom->removeEdge(this, false);
     394              :     }
     395         2251 :     if (myTo != to) {
     396           11 :         myTo->removeEdge(this, false);
     397              :     }
     398         2251 :     myType = StringUtils::convertUmlaute(type);
     399         2251 :     myFrom = from;
     400         2251 :     myTo = to;
     401         2251 :     myPriority = priority;
     402              :     //?myTurnDestination(0),
     403              :     //?myFromJunctionPriority(-1), myToJunctionPriority(-1),
     404              :     myGeom = geom;
     405         2251 :     myLaneSpreadFunction = spread;
     406         2251 :     myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
     407         2251 :     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         2251 :     const std::vector<Lane> oldLanes = myLanes;
     414         4502 :     init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
     415         4594 :     for (int i = 0; i < (int)nolanes; ++i) {
     416         2343 :         PositionVector newShape = myLanes[i].shape;
     417         2343 :         myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
     418              :         myLanes[i].shape = newShape;
     419         2343 :     }
     420              :     // however, if the new edge defaults are explicityly given, they override the old settings
     421         2251 :     if (endOffset != UNSPECIFIED_OFFSET) {
     422            3 :         setEndOffset(-1, endOffset);
     423              :     }
     424         2251 :     if (laneWidth != UNSPECIFIED_WIDTH) {
     425            2 :         setLaneWidth(-1, laneWidth);
     426              :     }
     427         2251 :     if (speed != UNSPECIFIED_SPEED) {
     428           25 :         setSpeed(-1, speed);
     429              :     }
     430         2251 :     if (friction != UNSPECIFIED_FRICTION) {
     431            0 :         setFriction(-1, friction);
     432              :     }
     433         2251 : }
     434              : 
     435              : 
     436              : void
     437         6741 : NBEdge::reinitNodes(NBNode* from, NBNode* to) {
     438              :     // connections may still be valid
     439         6741 :     if (from == nullptr || to == nullptr) {
     440            0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     441              :     }
     442         6741 :     if (myFrom != from) {
     443         3436 :         myFrom->removeEdge(this, false);
     444              :     }
     445         6741 :     if (myTo != to) {
     446         3267 :         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         6741 :     if (myFrom != from) {
     451         3436 :         myFrom = from;
     452         3436 :         myFrom->addOutgoingEdge(this);
     453              :     }
     454         6741 :     if (myTo != to) {
     455         3267 :         myTo = to;
     456         3267 :         myTo->addIncomingEdge(this);
     457              :     }
     458         6741 :     computeAngle();
     459         6741 : }
     460              : 
     461              : 
     462              : void
     463       137722 : NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
     464       137722 :     if (noLanes == 0) {
     465            0 :         throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
     466              :     }
     467       137722 :     if (myFrom == nullptr || myTo == nullptr) {
     468            0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     469              :     }
     470       137722 :     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       137721 :     if (myFrom->getID() < myTo->getID()) {
     479        71034 :         PositionVector reverse = myGeom.reverse();
     480        71034 :         reverse.removeDoublePoints(POSITION_EPS, true);
     481       142068 :         myGeom = reverse.reverse();
     482        71034 :     } else {
     483        66687 :         myGeom.removeDoublePoints(POSITION_EPS, true);
     484              :     }
     485              : 
     486       137721 :     if (!tryIgnoreNodePositions || myGeom.size() < 2) {
     487        43392 :         if (myGeom.size() == 0) {
     488        43085 :             myGeom.push_back(myFrom->getPosition());
     489        43085 :             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       137721 :     if (myGeom.size() < 2) {
     496              :         myGeom.clear();
     497            0 :         myGeom.push_back(myFrom->getPosition());
     498            0 :         myGeom.push_back(myTo->getPosition());
     499              :     }
     500       137721 :     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       137721 :     myGeom.ensureMinLength(gPrecision);
     507              :     //
     508       137721 :     myFrom->addOutgoingEdge(this);
     509       137721 :     myTo->addIncomingEdge(this);
     510              :     // prepare container
     511              :     assert(myGeom.size() >= 2);
     512       137721 :     myLength = myGeom.length();
     513       137721 :     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       308050 :     for (int i = 0; i < noLanes; i++) {
     528       340658 :         myLanes.push_back(Lane(this, origID));
     529              :     }
     530       137721 :     computeLaneShapes();
     531       137721 :     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       137721 : }
     545              : 
     546              : 
     547       273488 : NBEdge::~NBEdge() {}
     548              : 
     549              : 
     550              : // -----------  Applying offset
     551              : void
     552        69077 : NBEdge::reshiftPosition(double xoff, double yoff) {
     553        69077 :     myGeom.add(xoff, yoff, 0);
     554       158394 :     for (Lane& lane : myLanes) {
     555        89317 :         lane.customShape.add(xoff, yoff, 0);
     556              :     }
     557        69077 :     computeLaneShapes(); // old shapes are dubious if computed with large coordinates
     558        75540 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
     559         6463 :         (*i).customShape.add(xoff, yoff, 0);
     560              :     }
     561              :     if (mySignalPosition != Position::INVALID) {
     562              :         mySignalPosition.add(xoff, yoff);
     563              :     }
     564        69077 :     myFromBorder.add(xoff, yoff, 0);
     565        69077 :     myToBorder.add(xoff, yoff, 0);
     566        69077 :     computeEdgeShape();
     567        69077 :     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
     568        69077 : }
     569              : 
     570              : 
     571              : void
     572       111438 : NBEdge::roundGeometry() {
     573       111438 :     myGeom.round(gPrecision);
     574       254178 :     for (Lane& lane : myLanes) {
     575       142740 :         lane.customShape.round(gPrecision);
     576              :     }
     577       201271 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
     578        89833 :         (*i).customShape.round(gPrecision);
     579              :     }
     580       111438 : }
     581              : 
     582              : 
     583              : void
     584       111438 : NBEdge::roundSpeed() {
     585       111438 :     mySpeed = roundDecimalToEven(mySpeed, gPrecision);
     586              :     // lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
     587       254178 :     for (Lane& l : myLanes) {
     588       142740 :         l.speed = roundDecimalToEven(l.speed, gPrecision);
     589              :     }
     590       111438 : }
     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         4706 : NBEdge::getInnerGeometry() const {
     614         4706 :     return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
     615              : }
     616              : 
     617              : 
     618              : bool
     619       113253 : NBEdge::hasDefaultGeometry() const {
     620       113253 :     return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
     621              : }
     622              : 
     623              : 
     624              : bool
     625        65396 : NBEdge::hasDefaultGeometryEndpoints() const {
     626       122642 :     return myGeom.front().almostSame(myFrom->getPosition(), 0.01)  &&
     627        57246 :            myGeom.back().almostSame(myTo->getPosition(), 0.01);
     628              : }
     629              : 
     630              : 
     631              : bool
     632       190010 : NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
     633              :     // do not extend past the node position
     634       190010 :     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         3925 : NBEdge::getEndpointAtNode(const NBNode* node) const {
     644         3925 :     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         4477 : NBEdge::setGeometry(const PositionVector& s, bool inner) {
     661         4477 :     Position begin = myGeom.front(); // may differ from node position
     662         4477 :     Position end = myGeom.back(); // may differ from node position
     663              :     myGeom = s;
     664         4477 :     if (inner) {
     665            0 :         myGeom.insert(myGeom.begin(), begin);
     666            0 :         myGeom.push_back(end);
     667              :     }
     668              :     // ensure non-zero length (see ::init)
     669         4477 :     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         4477 :     computeLaneShapes();
     675         4477 :     computeAngle();
     676         4477 :     myLength = myGeom.length();
     677         4477 : }
     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       258022 : NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
     719       258022 :     PositionVector border;
     720       258022 :     if (rectangularCut) {
     721              :         const double extend = 100;
     722         3368 :         border = myGeom.getOrthogonal(p, extend, node == myTo);
     723              :     } else {
     724       256338 :         border.push_back(p);
     725       256338 :         border.push_back(p2);
     726              :     }
     727       258022 :     if (border.size() == 2) {
     728       258022 :         border.extrapolate2D(getTotalWidth());
     729       258022 :         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       258022 : }
     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        34729 : NBEdge::resetNodeBorder(const NBNode* node) {
     762        34729 :     if (node == myFrom) {
     763              :         myFromBorder.clear();
     764              :     } else {
     765              :         assert(node == myTo);
     766              :         myToBorder.clear();
     767              :     }
     768        34729 : }
     769              : 
     770              : 
     771              : bool
     772      5574648 : NBEdge::isBidiRail(bool ignoreSpread) const {
     773      5574648 :     return (isRailway(getPermissions())
     774       197158 :             && (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
     775       195769 :             && myPossibleTurnDestination != nullptr
     776       140641 :             && myPossibleTurnDestination->myPossibleTurnDestination == this
     777       133450 :             && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
     778       133379 :             && isRailway(myPossibleTurnDestination->getPermissions())
     779     11149296 :             && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
     780              : }
     781              : 
     782              : 
     783              : bool
     784      5193217 : NBEdge::isBidiEdge(bool checkPotential) const {
     785      5193217 :     return myPossibleTurnDestination != nullptr
     786      4785354 :            && myPossibleTurnDestination->myPossibleTurnDestination == this
     787      4545834 :            && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
     788         8908 :            && myPossibleTurnDestination->getToNode() == getFromNode()
     789         8908 :            && myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
     790              :            // geometry check a) full overlap geometry
     791      5202125 :            && ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
     792        14554 :                 && (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
     793            0 :                     || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
     794              :                // b) TWLT (Two-Way-Left-Turn-lane)
     795      5193217 :                || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
     796      5193217 :               );
     797              : 
     798              : }
     799              : 
     800              : 
     801              : bool
     802         2808 : NBEdge::isRailDeadEnd() const {
     803         2808 :     if (!isRailway(getPermissions())) {
     804              :         return false;
     805              :     }
     806         4360 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
     807         8311 :         if (isRailway(out->getPermissions()) &&
     808         4091 :                 out != getTurnDestination(true)) {
     809              :             return true;
     810              :         }
     811              :     }
     812              :     return true;
     813              : }
     814              : 
     815              : 
     816              : PositionVector
     817       349523 : NBEdge::cutAtIntersection(const PositionVector& old) const {
     818              :     PositionVector shape = old;
     819       699046 :     shape = startShapeAt(shape, myFrom, myFromBorder);
     820              : #ifdef DEBUG_CUT_LANES
     821              :     if (DEBUGCOND) {
     822              :         std::cout << getID() << " cutFrom=" << shape << "\n";
     823              :     }
     824              : #endif
     825       349523 :     if (shape.size() < 2) {
     826              :         // only keep the last snippet
     827          377 :         const double oldLength = old.length();
     828          754 :         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       699046 :     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       349523 :     if (shape.length() < POSITION_EPS) {
     843          412 :         if (old.length() < 2 * POSITION_EPS) {
     844              :             shape = old;
     845              :         } else {
     846          406 :             const double midpoint = old.length() / 2;
     847              :             // EPS*2 because otherwhise shape has only a single point
     848          812 :             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       349111 :         if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
     861              :             // eliminate intermediate points
     862        30126 :             PositionVector tmp;
     863        30126 :             tmp.push_back(shape[0]);
     864        30126 :             tmp.push_back(shape[-1]);
     865              :             shape = tmp;
     866        30126 :             if (tmp.length() < POSITION_EPS) {
     867              :                 // fall back to original shape
     868           78 :                 if (old.length() < 2 * POSITION_EPS) {
     869              :                     shape = old;
     870              :                 } else {
     871           78 :                     const double midpoint = old.length() / 2;
     872              :                     // EPS*2 because otherwhise shape has only a single point
     873          156 :                     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        30048 :                 const double midpoint = shape.length() / 2;
     884              :                 // cut to size and reverse
     885        60096 :                 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
     886        30048 :                 if (shape.length() < POSITION_EPS) {
     887              :                     assert(false);
     888              :                     // the shape has a sharp turn near the midpoint
     889              :                 }
     890        60096 :                 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        30126 :             const double z = (shape[0].z() + shape[1].z()) / 2;
     899        30126 :             shape[0].setz(z);
     900        30126 :             shape[1].setz(z);
     901        30126 :         }
     902              :     }
     903       349523 :     return shape;
     904            0 : }
     905              : 
     906              : 
     907              : void
     908       217368 : NBEdge::computeEdgeShape(double smoothElevationThreshold) {
     909       217368 :     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       498708 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     936       562680 :         myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
     937              :     }
     938              :     // recompute edge's length as the average of lane lengths
     939              :     double avgLength = 0;
     940       498708 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     941       281340 :         avgLength += myLanes[i].shape.length();
     942              :     }
     943       217368 :     myLength = avgLength / (double) myLanes.size();
     944       217368 :     computeAngle(); // update angles using the finalized node and lane shapes
     945       217368 : }
     946              : 
     947              : 
     948              : PositionVector
     949       699374 : NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
     950       699374 :     if (nodeShape.size() == 0) {
     951       190916 :         nodeShape = startNode->getShape();
     952       190916 :         nodeShape.closePolygon();
     953              :     }
     954              :     PositionVector lb = laneShape;
     955       699374 :     lb.extrapolate2D(100.0);
     956       699374 :     if (nodeShape.intersects(laneShape)) {
     957              :         // shape intersects directly
     958       508058 :         std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
     959              :         assert(pbv.size() > 0);
     960              :         // ensure that the subpart has at least two points
     961       508058 :         double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
     962       508058 :         if (pb < 0) {
     963              :             return laneShape;
     964              :         }
     965       508040 :         PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
     966              :         //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
     967       508040 :         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       508040 :         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       699374 :     } else if (nodeShape.intersects(lb)) {
     977              :         // extension of first segment intersects
     978        68819 :         std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
     979              :         assert(pbv.size() > 0);
     980              :         double pb = VectorHelper<double>::maxValue(pbv);
     981              :         assert(pb >= 0);
     982        68819 :         PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
     983        68819 :         Position np = lb.positionAtOffset2D(pb);
     984        68819 :         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        68819 :         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        68819 :         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        68819 :     } 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       699374 : }
    1006              : 
    1007              : 
    1008              : const PositionVector&
    1009      2495782 : NBEdge::getLaneShape(int i) const {
    1010      2495782 :     return myLanes[i].shape;
    1011              : }
    1012              : 
    1013              : 
    1014              : void
    1015         1390 : NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
    1016         1390 :     myLaneSpreadFunction = spread;
    1017         1390 : }
    1018              : 
    1019              : 
    1020              : LaneSpreadFunction
    1021       319090 : NBEdge::getLaneSpreadFunction() const {
    1022       319090 :     return myLaneSpreadFunction;
    1023              : }
    1024              : 
    1025              : 
    1026              : void
    1027          792 : NBEdge::addGeometryPoint(int index, const Position& p) {
    1028          792 :     if (index >= 0) {
    1029          396 :         myGeom.insert(myGeom.begin() + index, p);
    1030              :     } else {
    1031          396 :         myGeom.insert(myGeom.end() + index, p);
    1032              :     }
    1033          792 : }
    1034              : 
    1035              : 
    1036              : void
    1037          516 : NBEdge::reduceGeometry(const double minDist) {
    1038              :     // attempt symmetrical removal for forward and backward direction
    1039              :     // (very important for bidiRail)
    1040          516 :     if (myFrom->getID() < myTo->getID()) {
    1041          259 :         PositionVector reverse = myGeom.reverse();
    1042          259 :         reverse.removeDoublePoints(minDist, true, 0, 0, true);
    1043          518 :         myGeom = reverse.reverse();
    1044          702 :         for (Lane& lane : myLanes) {
    1045          886 :             reverse = lane.customShape.reverse();
    1046          443 :             reverse.removeDoublePoints(minDist, true, 0, 0, true);
    1047          886 :             lane.customShape = reverse.reverse();
    1048              :         }
    1049          259 :     } else {
    1050          257 :         myGeom.removeDoublePoints(minDist, true, 0, 0, true);
    1051          653 :         for (Lane& lane : myLanes) {
    1052          396 :             lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
    1053              :         }
    1054              :     }
    1055          516 : }
    1056              : 
    1057              : 
    1058              : void
    1059        79241 : NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
    1060        79241 :     if (myGeom.size() < 3) {
    1061        38035 :         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       277964 :     for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
    1067       236713 :         angles.push_back(myGeom.angleAt2D(i));
    1068              :         //std::cout << " " << angles.back();
    1069              :     }
    1070              :     //std::cout << "\n  relative angles: ";
    1071        41251 :     NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
    1072       236535 :     for (int i = 0; i < (int)angles.size() - 1; ++i) {
    1073       195329 :         const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
    1074              :         //std::cout << relAngle << " ";
    1075       195329 :         if (maxAngle > 0 && relAngle > maxAngle) {
    1076          131 :             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           45 :                 return;
    1085          130 :             } else if (!silent) {
    1086          390 :                 WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
    1087              :             }
    1088              :         }
    1089       195328 :         if (relAngle < DEG2RAD(1)) {
    1090        77729 :             continue;
    1091              :         }
    1092       117599 :         if (i == 0 || i == (int)angles.size() - 2) {
    1093              :             const bool start = i == 0;
    1094        44881 :             const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
    1095        44881 :             const double r = tan(0.5 * (M_PI - relAngle)) * dist;
    1096              :             //std::cout << (start ? "  start" : "  end") << " length=" << dist << " radius=" << r << "  ";
    1097        44881 :             if (minRadius > 0 && r < minRadius) {
    1098          633 :                 if (fix) {
    1099          132 :                     WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
    1100              :                                    toString(r), start ? TL("start") : TL("end"), getID());
    1101           44 :                     myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
    1102           44 :                     if (bidi != nullptr) {
    1103            0 :                         bidi->myGeom = myGeom.reverse();
    1104              :                     }
    1105           44 :                     checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
    1106           44 :                     return;
    1107          589 :                 } else if (!silent) {
    1108         1752 :                     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        41251 : }
    1116              : 
    1117              : 
    1118              : // ----------- Setting and getting connections
    1119              : bool
    1120       110773 : NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
    1121       110773 :     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       110773 :     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       110668 :     } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
    1135       194854 :         myConnections.push_back(Connection(-1, dest, -1));
    1136        97427 :         myConnections.back().permissions = permissions;
    1137              :     }
    1138       110773 :     if (overrideRemoval) {
    1139              :         // override earlier delete decision
    1140          901 :         for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
    1141          319 :             if (it->toEdge == dest) {
    1142            2 :                 it = myConnectionsToDelete.erase(it);
    1143              :             } else {
    1144              :                 it++;
    1145              :             }
    1146              :         }
    1147              :     }
    1148       110773 :     if (myStep < EdgeBuildingStep::EDGE2EDGES) {
    1149        50572 :         myStep = EdgeBuildingStep::EDGE2EDGES;
    1150              :     }
    1151              :     return true;
    1152              : }
    1153              : 
    1154              : 
    1155              : bool
    1156       110323 : 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       110323 :     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       110323 :     if (myTo != dest->myFrom) {
    1181              :         return false;
    1182              :     }
    1183       109579 :     if (!addEdge2EdgeConnection(dest)) {
    1184              :         return false;
    1185              :     }
    1186       109579 :     return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
    1187       109579 :                          customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
    1188              : }
    1189              : 
    1190              : 
    1191              : bool
    1192         1472 : NBEdge::addLane2LaneConnections(int fromLane,
    1193              :                                 NBEdge* dest, int toLane,
    1194              :                                 int no, Lane2LaneInfoType type,
    1195              :                                 bool invalidatePrevious,
    1196              :                                 bool mayDefinitelyPass) {
    1197         1472 :     if (invalidatePrevious) {
    1198          797 :         invalidateConnections(true);
    1199              :     }
    1200              :     bool ok = true;
    1201         3389 :     for (int i = 0; i < no && ok; i++) {
    1202         3834 :         ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
    1203              :     }
    1204         1472 :     return ok;
    1205              : }
    1206              : 
    1207              : 
    1208              : bool
    1209       256110 : 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       256110 :     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       256110 :     if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
    1240              :         return false;
    1241              :     }
    1242       244766 :     if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
    1243              :         return true;
    1244              :     }
    1245       244728 :     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       776548 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1251       531831 :         if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
    1252       193971 :             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       193909 :                 permissions = (*i).permissions;
    1256              :             }
    1257       193971 :             i = myConnections.erase(i);
    1258              :         } else {
    1259              :             ++i;
    1260              :         }
    1261              :     }
    1262       489434 :     myConnections.push_back(Connection(lane, destEdge, destLane));
    1263       244717 :     if (mayDefinitelyPass) {
    1264           27 :         myConnections.back().mayDefinitelyPass = true;
    1265              :     }
    1266       244717 :     myConnections.back().keepClear = keepClear;
    1267       244717 :     myConnections.back().contPos = contPos;
    1268       244717 :     myConnections.back().visibility = visibility;
    1269       244717 :     myConnections.back().permissions = permissions;
    1270       244717 :     myConnections.back().indirectLeft = indirectLeft;
    1271       244717 :     myConnections.back().edgeType = edgeType;
    1272       244717 :     myConnections.back().changeLeft = changeLeft;
    1273       244717 :     myConnections.back().changeRight = changeRight;
    1274       244717 :     myConnections.back().speed = speed;
    1275       244717 :     myConnections.back().friction = friction;
    1276       244717 :     myConnections.back().customLength = length;
    1277              :     myConnections.back().customShape = customShape;
    1278       244717 :     myConnections.back().uncontrolled = uncontrolled;
    1279       244717 :     if (type == Lane2LaneInfoType::USER) {
    1280         3074 :         myStep = EdgeBuildingStep::LANES2LANES_USER;
    1281              :     } else {
    1282              :         // check whether we have to take another look at it later
    1283       241643 :         if (type == Lane2LaneInfoType::COMPUTED) {
    1284              :             // yes, the connection was set using an algorithm which requires a recheck
    1285       104153 :             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       137490 :             if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
    1289       108027 :                 myStep = EdgeBuildingStep::LANES2LANES_DONE;
    1290              :             }
    1291              :         }
    1292              :     }
    1293       244717 :     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      3432096 : NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
    1311              :     std::vector<NBEdge::Connection> ret;
    1312     15676647 :     for (const Connection& c : myConnections) {
    1313     12244551 :         if ((lane < 0 || c.fromLane == lane)
    1314      7951065 :                 && (to == nullptr || to == c.toEdge)
    1315      7934879 :                 && (toLane < 0 || toLane == c.toLane)) {
    1316      7933684 :             ret.push_back(c);
    1317              :         }
    1318              :     }
    1319      3432096 :     return ret;
    1320            0 : }
    1321              : 
    1322              : 
    1323              : const NBEdge::Connection&
    1324        10823 : NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
    1325        36856 :     for (const Connection& c : myConnections) {
    1326        36856 :         if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
    1327        10823 :             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       269693 : NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
    1349       269693 :     return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
    1350              : }
    1351              : 
    1352              : 
    1353              : bool
    1354       968808 : NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
    1355       968808 :     if (!ignoreTurnaround && (e == myTurnDestination)) {
    1356              :         return true;
    1357              :     }
    1358              :     return
    1359       960786 :         find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
    1360              :         !=
    1361              :         myConnections.end();
    1362              : 
    1363              : }
    1364              : 
    1365              : 
    1366              : const EdgeVector*
    1367        67715 : NBEdge::getConnectedSorted() {
    1368              :     // check whether connections exist and if not, use edges from the node
    1369              :     EdgeVector outgoing;
    1370        67715 :     if (myConnections.size() == 0) {
    1371        22083 :         outgoing = myTo->getOutgoingEdges();
    1372              :     } else {
    1373       136164 :         for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1374        90532 :             if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
    1375        90221 :                 outgoing.push_back((*i).toEdge);
    1376              :             }
    1377              :         }
    1378              :     }
    1379        74891 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    1380         7176 :         if (it->fromLane < 0 && it->toLane < 0) {
    1381              :             // found an edge that shall not be connected
    1382         7176 :             EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
    1383         7176 :             if (forbidden != outgoing.end()) {
    1384              :                 outgoing.erase(forbidden);
    1385              :             }
    1386              :         }
    1387              :     }
    1388              :     // allocate the sorted container
    1389        67715 :     int size = (int) outgoing.size();
    1390        67715 :     EdgeVector* edges = new EdgeVector();
    1391        67715 :     edges->reserve(size);
    1392       186665 :     for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
    1393       118950 :         NBEdge* outedge = *i;
    1394       118950 :         if (outedge != nullptr && outedge != myTurnDestination) {
    1395       109157 :             edges->push_back(outedge);
    1396              :         }
    1397              :     }
    1398        67715 :     std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
    1399        67715 :     return edges;
    1400        67715 : }
    1401              : 
    1402              : 
    1403              : EdgeVector
    1404       191811 : NBEdge::getConnectedEdges() const {
    1405              :     EdgeVector ret;
    1406       629897 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1407       438086 :         if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
    1408       265443 :             ret.push_back((*i).toEdge);
    1409              :         }
    1410              :     }
    1411       191811 :     return ret;
    1412            0 : }
    1413              : 
    1414              : 
    1415              : EdgeVector
    1416         6728 : NBEdge::getIncomingEdges() const {
    1417              :     EdgeVector ret;
    1418         6728 :     const EdgeVector& candidates = myFrom->getIncomingEdges();
    1419        22850 :     for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
    1420        16122 :         if ((*i)->isConnectedTo(this)) {
    1421        14891 :             ret.push_back(*i);
    1422              :         }
    1423              :     }
    1424         6728 :     return ret;
    1425            0 : }
    1426              : 
    1427              : 
    1428              : std::vector<int>
    1429       554784 : NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
    1430              :     std::vector<int> ret;
    1431       554784 :     if (currentOutgoing != myTurnDestination) {
    1432      1677546 :         for (const Connection& c : myConnections) {
    1433      1170747 :             if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
    1434       447889 :                 ret.push_back(c.fromLane);
    1435              :             }
    1436              :         }
    1437              :     }
    1438       554784 :     return ret;
    1439            0 : }
    1440              : 
    1441              : 
    1442              : void
    1443       112304 : NBEdge::sortOutgoingConnectionsByAngle() {
    1444       112304 :     sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
    1445       112304 : }
    1446              : 
    1447              : 
    1448              : void
    1449       168274 : NBEdge::sortOutgoingConnectionsByIndex() {
    1450       168274 :     sort(myConnections.begin(), myConnections.end(), connections_sorter);
    1451       168274 : }
    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        81781 : NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
    1472              :                               const bool keepPossibleTurns) {
    1473              :     // remove from "myConnections"
    1474        81781 :     const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
    1475        81781 :     const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
    1476       162821 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1477              :         Connection& c = *i;
    1478        81040 :         if ((toEdge == nullptr || c.toEdge == toEdge)
    1479         8908 :                 && (fromLane < 0 || c.fromLane == fromLane)
    1480         8745 :                 && (toLane < 0 || c.toLane == toLane)) {
    1481         8608 :             if (myTo->isTLControlled()) {
    1482              :                 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
    1483          470 :                 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
    1484          235 :                     (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
    1485              :                 }
    1486              :             }
    1487         8608 :             i = myConnections.erase(i);
    1488              :             tryLater = false;
    1489         8608 :         } else {
    1490        72432 :             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        72432 :             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        81781 :     if (myTurnDestination == toEdge && fromLane < 0) {
    1514         2694 :         myTurnDestination = nullptr;
    1515              :     }
    1516        81781 :     if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
    1517         2191 :         myPossibleTurnDestination = nullptr;
    1518              :     }
    1519        81781 :     if (tryLater) {
    1520        14564 :         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        81781 : }
    1534              : 
    1535              : 
    1536              : bool
    1537           39 : NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
    1538              :     // iterate over connections
    1539           44 :     for (auto i = myConnections.begin(); i !=  myConnections.end(); i++) {
    1540           44 :         if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
    1541              :             // remove connection
    1542           39 :             myConnections.erase(i);
    1543              :             return true;
    1544              :         }
    1545              :     }
    1546              :     // assert(false);
    1547              :     return false;
    1548              : }
    1549              : 
    1550              : 
    1551              : void
    1552         3767 : NBEdge::invalidateConnections(bool reallowSetting) {
    1553         3767 :     myTurnDestination = nullptr;
    1554              :     myConnections.clear();
    1555         3767 :     if (reallowSetting) {
    1556         3647 :         myStep = EdgeBuildingStep::INIT;
    1557              :     } else {
    1558          120 :         myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
    1559              :     }
    1560         3767 : }
    1561              : 
    1562              : 
    1563              : void
    1564         1206 : NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
    1565              :     // replace in "_connectedEdges"
    1566         2511 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1567         1305 :         if ((*i).toEdge == which) {
    1568          761 :             (*i).toEdge = by;
    1569          761 :             (*i).toLane += laneOff;
    1570              :         }
    1571              :     }
    1572              :     // check whether it was the turn destination
    1573         1206 :     if (myTurnDestination == which) {
    1574           40 :         myTurnDestination = by;
    1575              :     }
    1576         1206 : }
    1577              : 
    1578              : void
    1579        46721 : 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        53643 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1586         6922 :         if ((*i).toEdge != which) {
    1587         6187 :             continue;
    1588              :         }
    1589              :         wasConnected = true;
    1590          735 :         if ((*i).fromLane != -1) {
    1591              :             int fromLane = (*i).fromLane;
    1592          705 :             laneMap[(*i).toLane] = fromLane;
    1593          705 :             if (minLane == -1 || minLane > fromLane) {
    1594              :                 minLane = fromLane;
    1595              :             }
    1596          705 :             if (maxLane == -1 || maxLane < fromLane) {
    1597              :                 maxLane = fromLane;
    1598              :             }
    1599              :         }
    1600              :     }
    1601        46721 :     if (!wasConnected) {
    1602              :         return;
    1603              :     }
    1604              :     // add new connections
    1605          517 :     std::vector<NBEdge::Connection> conns = origConns;
    1606          517 :     EdgeVector origTargets = getSuccessors();
    1607         2633 :     for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
    1608         2116 :         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         4174 :                 || 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          881 :             continue;
    1618              :         }
    1619         1238 :         if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
    1620              :             // do not set lane-level connections
    1621            3 :             replaceInConnections(which, (*i).toEdge, 0);
    1622            3 :             continue;
    1623              :         }
    1624         1235 :         int fromLane = (*i).fromLane;
    1625              :         int toUse = -1;
    1626         1235 :         if (laneMap.find(fromLane) == laneMap.end()) {
    1627          288 :             if (fromLane >= 0 && fromLane <= minLane) {
    1628              :                 toUse = minLane;
    1629              :                 // patch laneMap to avoid crossed-over connections
    1630          455 :                 for (auto& item : laneMap) {
    1631          249 :                     if (item.first < fromLane) {
    1632           35 :                         item.second = MIN2(item.second, minLane);
    1633              :                     }
    1634              :                 }
    1635              :             }
    1636          288 :             if (fromLane >= 0 && fromLane >= maxLane) {
    1637              :                 toUse = maxLane;
    1638              :                 // patch laneMap to avoid crossed-over connections
    1639          336 :                 for (auto& item : laneMap) {
    1640          185 :                     if (item.first > fromLane) {
    1641           68 :                         item.second = MAX2(item.second, maxLane);
    1642              :                     }
    1643              :                 }
    1644              :             }
    1645              :         } else {
    1646          947 :             toUse = laneMap[fromLane];
    1647              :         }
    1648         1235 :         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         2470 :         setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
    1659         1235 :                       i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
    1660              :     }
    1661              :     // remove the remapped edge from connections
    1662          517 :     removeFromConnections(which);
    1663          517 : }
    1664              : 
    1665              : 
    1666              : void
    1667          787 : NBEdge::copyConnectionsFrom(NBEdge* src) {
    1668          787 :     myStep = src->myStep;
    1669          787 :     myConnections = src->myConnections;
    1670          787 : }
    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        49132 : NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
    1718        49132 :     const OptionsCont& oc = OptionsCont::getOptions();
    1719        49132 :     const int numPoints = oc.getInt("junctions.internal-link-detail");
    1720        49132 :     const bool joinTurns = oc.getBool("junctions.join-turns");
    1721        49132 :     const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
    1722        49132 :     const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
    1723        49132 :     const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
    1724        49132 :     const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
    1725        49132 :     const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
    1726        49132 :     const bool higherSpeed = oc.getBool("junctions.higher-speed");
    1727        49132 :     const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
    1728        49132 :     const double defaultContPos = oc.getFloat("default.connection.cont-pos");
    1729        49132 :     const bool fromRail = isRailway(getPermissions());
    1730        49132 :     std::string innerID = ":" + n.getID();
    1731              :     NBEdge* toEdge = nullptr;
    1732        49132 :     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       145964 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1740              :         Connection& con = *i;
    1741        96832 :         con.haveVia = false; // reset first since this may be called multiple times
    1742        96832 :         if (con.toEdge == nullptr) {
    1743            0 :             continue;
    1744              :         }
    1745        96832 :         LinkDirection dir = n.getDirection(this, con.toEdge);
    1746        96832 :         const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
    1747        96832 :         const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
    1748              :         // put turning internal lanes on separate edges
    1749        96832 :         if (con.toEdge != toEdge) {
    1750              :             // skip indices to keep some correspondence between edge ids and link indices:
    1751              :             // internalEdgeIndex + internalLaneIndex = linkIndex
    1752        87241 :             edgeIndex = linkIndex;
    1753              :             toEdge = con.toEdge;
    1754              :             internalLaneIndex = 0;
    1755        87241 :             maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
    1756              :             numLanes = 0;
    1757              :             lengthSum = 0;
    1758              :         }
    1759        96832 :         averageLength = !isTurn || joinTurns; // legacy behavior
    1760        96832 :         SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
    1761        96832 :         const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
    1762        96832 :         PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
    1763              :         std::vector<int> foeInternalLinks;
    1764              : 
    1765        96832 :         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        96832 :         std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
    1771              :         std::set<std::string> tmpFoeIncomingLanes;
    1772        96832 :         if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
    1773        61526 :             int index = 0;
    1774              :             std::vector<PositionVector> otherShapes;
    1775        61526 :             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       270156 :             for (const NBEdge* i2 : n.getIncomingEdges()) {
    1778       971755 :                 for (const Connection& k2 : i2->getConnections()) {
    1779       763125 :                     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       763125 :                     double width2 = k2.toEdge->getLaneWidth(k2.toLane);
    1785       763125 :                     if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
    1786       750387 :                         width2 *= 0.5;
    1787              :                     }
    1788       763125 :                     const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
    1789       763125 :                     LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
    1790       763125 :                     bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
    1791       763125 :                     const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
    1792        14310 :                     bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
    1793              :                     int shapeFlag = 0;
    1794       763125 :                     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       762406 :                             && k2.customShape.size() == 0
    1798       762004 :                             && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane  && avoidIntersectCandidate))
    1799       766869 :                             && ((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         3164 :                         shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
    1804              :                         PositionVector origShape = shape;
    1805         6328 :                         shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
    1806         3164 :                         oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
    1807         3164 :                         if (oppositeLeftIntersect
    1808         2106 :                                 && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
    1809              :                             shape = origShape;
    1810              :                         } else {
    1811              :                             // recompute previously computed crossing positions
    1812         3116 :                             if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
    1813         1749 :                                     || avoidedIntersectingLeftOriginLane < con.fromLane) {
    1814         3129 :                                 for (const PositionVector& otherShape : otherShapes) {
    1815         1723 :                                     const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1816         1723 :                                     const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
    1817         1723 :                                                                            "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1818         1723 :                                     if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1819              :                                         assert(minDV >= 0);
    1820         1608 :                                         if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1821          259 :                                             crossingPositions.first = minDV;
    1822              :                                         }
    1823              :                                     }
    1824              :                                 }
    1825              :                             }
    1826              :                             // make sure connections further to the left do not get a wider angle
    1827         3116 :                             avoidedIntersectingLeftOriginLane = con.fromLane;
    1828              :                         }
    1829         3164 :                     }
    1830       763125 :                     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       763125 :                     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       763125 :                     if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
    1836        32577 :                         crossingPositions.second.push_back(index);
    1837        32577 :                         const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
    1838        32577 :                         otherShapes.push_back(otherShape);
    1839        32577 :                         const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1840        32577 :                         const double minDV = firstIntersection(shape, otherShape, width1, width2,
    1841        32577 :                                                                "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1842        32577 :                         if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1843              :                             assert(minDV >= 0);
    1844        27823 :                             if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1845        27152 :                                 crossingPositions.first = minDV;
    1846              :                             }
    1847              :                         }
    1848        32577 :                     }
    1849       763125 :                     const bool rightTurnConflict = NBNode::rightTurnConflict(
    1850       763125 :                                                        this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
    1851       763125 :                     const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
    1852           52 :                                                      (con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
    1853       763125 :                     const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
    1854       763125 :                     const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
    1855       763125 :                     const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
    1856              :                     // compute foe internal lanes
    1857       763125 :                     if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
    1858       217355 :                         foeInternalLinks.push_back(index);
    1859              :                     }
    1860              :                     // only warn once per pair of intersecting turns
    1861         2713 :                     if (oppositeLeftIntersect && getID() > i2->getID()
    1862         1359 :                             && (getPermissions(con.fromLane) & warn) != 0
    1863         1143 :                             && (con.toEdge->getPermissions(con.toLane) & warn) != 0
    1864         1107 :                             && (i2->getPermissions(k2.fromLane) & warn) != 0
    1865         1039 :                             && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
    1866              :                             // do not warn for unregulated nodes
    1867       764151 :                             && n.getType() != SumoXMLNodeType::NOJUNCTION
    1868              :                        ) {
    1869          513 :                         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       763125 :                     const bool signalised = hasSignalisedConnectionTo(con.toEdge);
    1874      1395905 :                     if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
    1875       763762 :                             && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
    1876       232548 :                         tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
    1877              :                     }
    1878       763125 :                     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          328 :                         tmpFoeIncomingLanes.insert(":" + toString(index));
    1883              :                     }
    1884       763125 :                     index++;
    1885              :                 }
    1886              :             }
    1887        61526 :             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          221 :                 crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
    1891              :             }
    1892              :             // foe pedestrian crossings
    1893        61526 :             std::vector<NBNode::Crossing*> crossings = n.getCrossings();
    1894        72272 :             for (auto c : crossings) {
    1895              :                 const NBNode::Crossing& crossing = *c;
    1896        29635 :                 for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
    1897        18889 :                     const NBEdge* edge = *it_e;
    1898              :                     // compute foe internal lanes
    1899        18889 :                     if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
    1900         4854 :                         foeInternalLinks.push_back(index);
    1901         4854 :                         if (con.toEdge == edge &&
    1902         2425 :                                 ((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         1106 :                                 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1910          612 :                                     crossingPositions.first = minDV;
    1911              :                                 }
    1912              :                             }
    1913         4854 :                         } else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
    1914           20 :                             crossingPositions.first = 0;
    1915              :                         }
    1916              :                     }
    1917              :                 }
    1918        10746 :                 index++;
    1919              :             }
    1920              : 
    1921        61526 :         }
    1922        96832 :         if (con.contPos == UNSPECIFIED_CONTPOS) {
    1923        96802 :             con.contPos = defaultContPos;
    1924              :         }
    1925        96832 :         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        96832 :         if (con.speed == UNSPECIFIED_SPEED) {
    1946        96672 :             if (higherSpeed) {
    1947           44 :                 con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
    1948              :             } else {
    1949        96648 :                 con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
    1950              :             }
    1951        96672 :             if (limitTurnSpeed > 0) {
    1952              :                 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
    1953       182536 :                 const double angleRaw = fabs(GeomHelper::angleDiff(
    1954        91268 :                                                  getLaneShape(con.fromLane).angleAt2D(-2),
    1955        91268 :                                                  con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
    1956       179630 :                 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
    1957        91268 :                 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        91268 :                 if (angle > 0 && length > 1) {
    1961              :                     // permit higher turning speed on wide lanes
    1962        61451 :                     const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
    1963        61451 :                     const double limit = sqrt(limitTurnSpeed * radius);
    1964        61451 :                     const double reduction = con.vmax - limit;
    1965              :                     // always treat connctions at roundabout as turns when warning
    1966        61451 :                     const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
    1967              :                     const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
    1968        61141 :                     if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
    1969        61268 :                             || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
    1970          503 :                         std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
    1971          343 :                         if (atRoundabout) {
    1972              :                             dirType = "roundabout";
    1973              :                         }
    1974         1029 :                         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       115269 :                     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         5404 :             } else if (fromRail && dir == LinkDirection::TURN) {
    1987            0 :                 con.vmax = 0.01;
    1988              :             }
    1989              :         } else {
    1990          160 :             con.vmax = con.speed;
    1991              :         }
    1992        96832 :         if (con.friction == UNSPECIFIED_FRICTION) {
    1993        96832 :             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       193664 :         con.id = innerID + "_" + toString(edgeIndex);
    1999        96832 :         const double shapeLength = shape.length();
    2000              :         double firstLength = shapeLength;
    2001        96832 :         con.internalViaLaneIndex = 0; // reset to default for netedit
    2002        96832 :         if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
    2003        18288 :             std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
    2004              :             con.shape = split.first;
    2005        18288 :             con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
    2006        18288 :             con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
    2007        18288 :             if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia)  {
    2008          215 :                 --splitIndex;
    2009          215 :                 con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
    2010              :             }
    2011        36576 :             con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
    2012        18288 :             ++splitIndex;
    2013              :             con.viaShape = split.second;
    2014        18288 :             con.haveVia = true;
    2015        18288 :             firstLength = con.shape.length();
    2016        18288 :         } else {
    2017              :             con.shape = shape;
    2018              :         }
    2019        96832 :         con.internalLaneIndex = internalLaneIndex;
    2020        96832 :         ++internalLaneIndex;
    2021        96832 :         ++linkIndex;
    2022              :         ++numLanes;
    2023        96832 :         if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
    2024              :             // split length proportionally
    2025           30 :             lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
    2026              :         } else {
    2027        96802 :             lengthSum += firstLength;
    2028              :         }
    2029        96832 :     }
    2030        98264 :     return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
    2031              : }
    2032              : 
    2033              : 
    2034              : double
    2035       136373 : NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
    2036              :     // assign average length to all lanes of the same internal edge if averageLength is set
    2037              :     // the lengthSum only covers the part up to the first internal junction
    2038              :     // TODO This code assumes that either all connections in question have a via or none
    2039              :     double maxCross = 0.;
    2040              :     assert(i - myConnections.begin() >= numLanes);
    2041       233205 :     for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
    2042              :         //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
    2043              :         Connection& c = (*(i - prevIndex));
    2044        96832 :         const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
    2045        96832 :         c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
    2046        96832 :         if (c.haveVia) {
    2047        36576 :             c.viaLength = MAX2(minLength, c.viaShape.length());
    2048              :         }
    2049        96832 :         if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
    2050           30 :             if (c.haveVia) {
    2051              :                 // split length proportionally
    2052            4 :                 const double a = c.viaLength / (c.shape.length() + c.viaLength);
    2053            6 :                 c.viaLength = MAX2(minLength, a * c.customLength);
    2054              :             }
    2055           30 :             if (!averageLength) {
    2056           19 :                 c.length = MAX2(minLength, c.customLength - c.viaLength);
    2057              :             }
    2058              :         }
    2059        96832 :         if (c.haveVia) {
    2060              :             // we need to be able to leave from the internal junction by accelerating from 0
    2061        18288 :             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)
    2062              :         }
    2063              :         // we need to be able to cross the junction in one go but not if we have an indirect left turn
    2064        96832 :         if (c.indirectLeft) {
    2065           15 :             maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    2066              :         } else {
    2067       138416 :             maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    2068              :         }
    2069              :     }
    2070       136373 :     return maxCross;
    2071              : }
    2072              : 
    2073              : 
    2074              : double
    2075        74917 : NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
    2076              :     double intersect = std::numeric_limits<double>::max();
    2077        74917 :     if (v2.length() < POSITION_EPS) {
    2078              :         return intersect;
    2079              :     }
    2080              :     try {
    2081              :         PositionVector v1Right = v1;
    2082        74889 :         v1Right.move2side(width1);
    2083              : 
    2084              :         PositionVector v1Left = v1;
    2085        74889 :         v1Left.move2side(-width1);
    2086              : 
    2087              :         PositionVector v2Right = v2;
    2088        74889 :         v2Right.move2side(width2);
    2089              : 
    2090              :         PositionVector v2Left = v2;
    2091        74889 :         v2Left.move2side(-width2);
    2092              : 
    2093              :         // intersect all border combinations
    2094              :         bool skip = secondIntersection;
    2095        98659 :         for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
    2096        23770 :             if (skip) {
    2097              :                 skip = false;
    2098            8 :                 continue;
    2099              :             }
    2100              :             intersect = MIN2(intersect, cand);
    2101        74889 :         }
    2102              :         skip = secondIntersection;
    2103       111476 :         for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
    2104        36587 :             if (skip) {
    2105              :                 skip = false;
    2106           11 :                 continue;
    2107              :             }
    2108              :             intersect = MIN2(intersect, cand);
    2109        74889 :         }
    2110              :         skip = secondIntersection;
    2111       118013 :         for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
    2112        43124 :             if (skip) {
    2113              :                 skip = false;
    2114            9 :                 continue;
    2115              :             }
    2116              :             intersect = MIN2(intersect, cand);
    2117        74889 :         }
    2118              :         skip = secondIntersection;
    2119       112273 :         for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
    2120        37384 :             if (skip) {
    2121              :                 skip = false;
    2122           11 :                 continue;
    2123              :             }
    2124              :             intersect = MIN2(intersect, cand);
    2125        74889 :         }
    2126        74889 :     } catch (InvalidArgument&) {
    2127            0 :         if (error != "") {
    2128            0 :             WRITE_WARNING(error);
    2129              :         }
    2130            0 :     }
    2131              :     //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
    2132              :     //std::cout << "  intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
    2133              :     //std::cout << "  intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
    2134              :     return intersect;
    2135              : }
    2136              : 
    2137              : 
    2138              : bool
    2139       549217 : NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
    2140       549217 :     if (otherFrom == this) {
    2141              :         // not an opposite pair
    2142              :         return false;
    2143              :     }
    2144       334839 :     return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
    2145              : }
    2146              : 
    2147              : bool
    2148        17474 : NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
    2149              :                          double width1, double width2, int shapeFlag) const {
    2150        17474 :     const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
    2151        17474 :     const double minDV = firstIntersection(shape, otherShape, width1, width2);
    2152        34948 :     return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
    2153        17474 : }
    2154              : 
    2155              : 
    2156              : // -----------
    2157              : int
    2158     11433417 : NBEdge::getJunctionPriority(const NBNode* const node) const {
    2159     11433417 :     if (node == myFrom) {
    2160       281625 :         return myFromJunctionPriority;
    2161              :     } else {
    2162     11151792 :         return myToJunctionPriority;
    2163              :     }
    2164              : }
    2165              : 
    2166              : 
    2167              : void
    2168       322853 : NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
    2169       322853 :     if (node == myFrom) {
    2170       159362 :         myFromJunctionPriority = prio;
    2171              : #ifdef DEBUG_JUNCTIONPRIO
    2172              :         setParameter("fromPrio", toString(prio));
    2173              : #endif
    2174              :     } else {
    2175       163491 :         myToJunctionPriority = prio;
    2176              : #ifdef DEBUG_JUNCTIONPRIO
    2177              :         setParameter("toPrio", toString(prio));
    2178              : #endif
    2179              :     }
    2180       322853 : }
    2181              : 
    2182              : 
    2183              : double
    2184     19151111 : NBEdge::getAngleAtNode(const NBNode* const atNode) const {
    2185     19151111 :     if (atNode == myFrom) {
    2186      9408198 :         return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
    2187              :     }
    2188              :     assert(atNode == myTo);
    2189      9742913 :     return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2190              : }
    2191              : 
    2192              : 
    2193              : double
    2194      3497680 : NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
    2195              :     double res;
    2196      3497680 :     if (atNode == myFrom) {
    2197      1725762 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
    2198              :     } else {
    2199              :         assert(atNode == myTo);
    2200      1771918 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2201              :     }
    2202      3497680 :     if (res < 0) {
    2203      2524449 :         res += 360;
    2204              :     }
    2205      3497680 :     return res;
    2206              : }
    2207              : 
    2208              : 
    2209              : double
    2210      5529310 : NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
    2211      5529310 :     if (atNode == myFrom) {
    2212      2566514 :         double res = myStartAngle - 180;
    2213      2566514 :         if (res < 0) {
    2214      1110847 :             res += 360;
    2215              :         }
    2216      2566514 :         return res;
    2217              :     } else {
    2218              :         assert(atNode == myTo);
    2219      2962796 :         return myEndAngle;
    2220              :     }
    2221              : }
    2222              : 
    2223              : 
    2224              : void
    2225       652995 : NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
    2226       652995 :     if (!onlyPossible) {
    2227       598866 :         myTurnDestination = e;
    2228              :     }
    2229       652995 :     myPossibleTurnDestination = e;
    2230       652995 : }
    2231              : 
    2232              : 
    2233              : double
    2234        10702 : NBEdge::getLaneSpeed(int lane) const {
    2235        10702 :     return myLanes[lane].speed;
    2236              : }
    2237              : 
    2238              : 
    2239              : double
    2240         3923 : NBEdge::getLaneFriction(int lane) const {
    2241         3923 :     return myLanes[lane].friction;
    2242              : }
    2243              : 
    2244              : 
    2245              : void
    2246           80 : NBEdge::resetLaneShapes() {
    2247           80 :     computeLaneShapes();
    2248           80 : }
    2249              : 
    2250              : 
    2251              : void
    2252            6 : NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
    2253           24 :     for (Lane& lane : myLanes) {
    2254           18 :         if (lane.changeLeft != SVCAll) {
    2255            6 :             lane.changeLeft = ignoring;
    2256              :         }
    2257           18 :         if (lane.changeRight != SVCAll) {
    2258            6 :             lane.changeRight = ignoring;
    2259              :         }
    2260              :     }
    2261           15 :     for (Connection& con : myConnections) {
    2262            9 :         if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
    2263            0 :             con.changeLeft = ignoring;
    2264              :         }
    2265            9 :         if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
    2266            0 :             con.changeRight = ignoring;
    2267              :         }
    2268              :     }
    2269            6 : }
    2270              : 
    2271              : 
    2272              : void
    2273       360650 : NBEdge::computeLaneShapes() {
    2274              :     // vissim needs this
    2275       360650 :     if (myFrom == myTo) {
    2276            0 :         return;
    2277              :     }
    2278              :     // compute lane offset, first
    2279       360650 :     std::vector<double> offsets(myLanes.size(), 0.);
    2280              :     double offset = 0;
    2281       463232 :     for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
    2282       102582 :         offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
    2283       102582 :         offsets[i] = offset;
    2284              :     }
    2285       360650 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    2286              :         double width = 0;
    2287       351401 :         for (int i = 0; i < (int)myLanes.size(); ++i) {
    2288       199737 :             width += getLaneWidth(i);
    2289              :         }
    2290       151664 :         offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
    2291              :     } else {
    2292       208986 :         double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
    2293       208986 :         offset = laneWidth / 2.;
    2294              :     }
    2295       360650 :     if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
    2296         3560 :         for (NBEdge* e : myTo->getOutgoingEdges()) {
    2297         3071 :             if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
    2298         1321 :                 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
    2299         1321 :                 break;
    2300              :             }
    2301              :         }
    2302              :     }
    2303              : 
    2304       823882 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2305       463232 :         offsets[i] += offset;
    2306              :     }
    2307              : 
    2308              :     // build the shape of each lane
    2309       823882 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2310       463232 :         if (myLanes[i].customShape.size() != 0) {
    2311              :             myLanes[i].shape = myLanes[i].customShape;
    2312           26 :             continue;
    2313              :         }
    2314              :         try {
    2315       926412 :             myLanes[i].shape = computeLaneShape(i, offsets[i]);
    2316            0 :         } catch (InvalidArgument& e) {
    2317            0 :             WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
    2318              :             myLanes[i].shape = myGeom;
    2319            0 :         }
    2320              :     }
    2321       360650 : }
    2322              : 
    2323              : 
    2324              : PositionVector
    2325       463206 : NBEdge::computeLaneShape(int lane, double offset) const {
    2326              :     PositionVector shape = myGeom;
    2327              :     try {
    2328       463206 :         shape.move2side(offset);
    2329            0 :     } catch (InvalidArgument& e) {
    2330            0 :         WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
    2331            0 :     }
    2332       463206 :     return shape;
    2333            0 : }
    2334              : 
    2335              : 
    2336              : void
    2337       475968 : NBEdge::computeAngle() {
    2338              :     // taking the angle at the first might be unstable, thus we take the angle
    2339              :     // at a certain distance. (To compare two edges, additional geometry
    2340              :     // segments are considered to resolve ambiguities)
    2341       475968 :     const bool hasFromShape = myFrom->getShape().size() > 0;
    2342       475968 :     const bool hasToShape = myTo->getShape().size() > 0;
    2343       475968 :     Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
    2344       475968 :     Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
    2345              :     PositionVector shape = myGeom;
    2346       475968 :     if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
    2347       206480 :         if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
    2348       108333 :             shape = myLanes[getNumLanes() - 1].shape ;
    2349              :         } else {
    2350        98147 :             shape = myLanes[getNumLanes() / 2].shape;
    2351        98147 :             if (getNumLanes() % 2 == 0) {
    2352              :                 // there is no center lane. shift to get the center
    2353        14660 :                 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
    2354              :             }
    2355              :         }
    2356              :     }
    2357              : 
    2358              :     // if the junction shape is suspicious we cannot trust the angle to the centroid
    2359       475968 :     const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
    2360       175804 :                                      || myFrom->getShape().around(shape[-1])
    2361       175119 :                                      || !(myFrom->getShape().around(fromCenter)));
    2362       475968 :     const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
    2363       171460 :                                    || myTo->getShape().around(shape[0])
    2364       170886 :                                    || !(myTo->getShape().around(toCenter)));
    2365              : 
    2366       475968 :     const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
    2367       475968 :     const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
    2368       475968 :     const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
    2369              : 
    2370       475968 :     myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
    2371       475968 :     const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
    2372       475968 :     const double myStartAngle3 = getAngleAtNode(myFrom);
    2373       475968 :     myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
    2374       475968 :     const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
    2375       475968 :     const double myEndAngle3 = getAngleAtNode(myTo);
    2376              : 
    2377              : #ifdef DEBUG_ANGLES
    2378              :     if (DEBUGCOND) {
    2379              :         if (suspiciousFromShape) {
    2380              :             std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
    2381              :                       << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
    2382              :                       << " fromCenter=" << fromCenter
    2383              :                       << " fromPos=" << myFrom->getPosition()
    2384              :                       << " refStart=" << referencePosStart
    2385              :                       << "\n";
    2386              :         }
    2387              :         if (suspiciousToShape) {
    2388              :             std::cout << "suspiciousToShape len=" << shape.length() << "  endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
    2389              :                       << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
    2390              :                       << " toCenter=" << toCenter
    2391              :                       << " toPos=" << myTo->getPosition()
    2392              :                       << " refEnd=" << referencePosEnd
    2393              :                       << "\n";
    2394              :         }
    2395              :     }
    2396              : #endif
    2397              : 
    2398       475968 :     if (suspiciousFromShape && shape.length() > 1) {
    2399        17806 :         myStartAngle = myStartAngle2;
    2400        42242 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
    2401              :                // don't trust footpath angles
    2402       461963 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2403         2525 :         myStartAngle = myStartAngle3;
    2404         2525 :         if (myStartAngle < 0) {
    2405         1234 :             myStartAngle += 360;
    2406              :         }
    2407              :     }
    2408              : 
    2409       475968 :     if (suspiciousToShape && shape.length() > 1) {
    2410        22957 :         myEndAngle = myEndAngle2;
    2411        22055 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
    2412              :                // don't trust footpath angles
    2413       457813 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2414         2967 :         myEndAngle = myEndAngle3;
    2415         2967 :         if (myEndAngle < 0) {
    2416         1441 :             myEndAngle += 360;
    2417              :         }
    2418              :     }
    2419              : 
    2420       475968 :     myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
    2421              : #ifdef DEBUG_ANGLES
    2422              :     if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
    2423              :                                  << " fromCenter=" << fromCenter << " toCenter=" << toCenter
    2424              :                                  << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
    2425              :                                  << " hasFromShape=" << hasFromShape
    2426              :                                  << " hasToShape=" << hasToShape
    2427              :                                  << " numLanes=" << getNumLanes()
    2428              :                                  << " shapeLane=" << getNumLanes() / 2
    2429              :                                  << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
    2430              : #endif
    2431       475968 : }
    2432              : 
    2433              : 
    2434              : double
    2435       201140 : NBEdge::getShapeStartAngle() const {
    2436       201140 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2437       201140 :     const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
    2438       201140 :     return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
    2439              : }
    2440              : 
    2441              : 
    2442              : double
    2443        97629 : NBEdge::getShapeEndAngle() const {
    2444        97629 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2445        97629 :     const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
    2446        97629 :     return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
    2447              : }
    2448              : 
    2449              : 
    2450              : bool
    2451            0 : NBEdge::hasPermissions() const {
    2452            0 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2453            0 :         if ((*i).permissions != SVCAll) {
    2454              :             return true;
    2455              :         }
    2456              :     }
    2457              :     return false;
    2458              : }
    2459              : 
    2460              : 
    2461              : bool
    2462         4948 : NBEdge::hasLaneSpecificPermissions() const {
    2463              :     std::vector<Lane>::const_iterator i = myLanes.begin();
    2464         4948 :     SVCPermissions firstLanePermissions = i->permissions;
    2465              :     i++;
    2466         6546 :     for (; i != myLanes.end(); ++i) {
    2467         3133 :         if (i->permissions != firstLanePermissions) {
    2468              :             return true;
    2469              :         }
    2470              :     }
    2471              :     return false;
    2472              : }
    2473              : 
    2474              : 
    2475              : bool
    2476         4675 : NBEdge::hasLaneSpecificSpeed() const {
    2477        14352 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2478         9692 :         if (i->speed != getSpeed()) {
    2479              :             return true;
    2480              :         }
    2481              :     }
    2482              :     return false;
    2483              : }
    2484              : 
    2485              : bool
    2486         1888 : NBEdge::hasLaneSpecificFriction() const {
    2487         5163 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2488         3279 :         if (i->friction != myLanes.begin()->friction) {
    2489              :             return true;
    2490              :         }
    2491              :     }
    2492              :     return false;
    2493              : }
    2494              : 
    2495              : bool
    2496        50779 : NBEdge::hasLaneSpecificWidth() const {
    2497       111382 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2498        62379 :         if (i->width != myLanes.begin()->width) {
    2499              :             return true;
    2500              :         }
    2501              :     }
    2502              :     return false;
    2503              : }
    2504              : 
    2505              : 
    2506              : bool
    2507         1608 : NBEdge::hasLaneSpecificType() const {
    2508         3876 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2509         2270 :         if (i->type != myLanes.begin()->type) {
    2510              :             return true;
    2511              :         }
    2512              :     }
    2513              :     return false;
    2514              : }
    2515              : 
    2516              : 
    2517              : bool
    2518        50055 : NBEdge::hasLaneSpecificEndOffset() const {
    2519       111909 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2520        61871 :         if (i->endOffset != myLanes.begin()->endOffset) {
    2521              :             return true;
    2522              :         }
    2523              :     }
    2524              :     return false;
    2525              : }
    2526              : 
    2527              : 
    2528              : bool
    2529        51919 : NBEdge::hasLaneSpecificStopOffsets() const {
    2530       117005 :     for (const auto& lane : myLanes) {
    2531        65091 :         if (lane.laneStopOffset.isDefined()) {
    2532            5 :             if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
    2533              :                 return true;
    2534              :             }
    2535              :         }
    2536              :     }
    2537              :     return false;
    2538              : }
    2539              : 
    2540              : 
    2541              : bool
    2542         1600 : NBEdge::hasAccelLane() const {
    2543         3845 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2544         2246 :         if (i->accelRamp) {
    2545              :             return true;
    2546              :         }
    2547              :     }
    2548              :     return false;
    2549              : }
    2550              : 
    2551              : 
    2552              : bool
    2553         1599 : NBEdge::hasCustomLaneShape() const {
    2554         3834 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2555         2240 :         if (i->customShape.size() > 0) {
    2556              :             return true;
    2557              :         }
    2558              :     }
    2559              :     return false;
    2560              : }
    2561              : 
    2562              : 
    2563              : bool
    2564         1594 : NBEdge::hasLaneParams() const {
    2565         3825 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2566         2233 :         if (i->getParametersMap().size() > 0) {
    2567              :             return true;
    2568              :         }
    2569              :     }
    2570              :     return false;
    2571              : }
    2572              : 
    2573              : bool
    2574         1592 : NBEdge::prohibitsChanging() const {
    2575         3805 :     for (const Lane& lane : myLanes) {
    2576         2220 :         if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
    2577              :             return true;
    2578              :         }
    2579              :     }
    2580              :     return false;
    2581              : }
    2582              : 
    2583              : bool
    2584         1888 : NBEdge::needsLaneSpecificOutput() const {
    2585         1888 :     return (hasLaneSpecificPermissions()
    2586         1615 :             || hasLaneSpecificSpeed()
    2587         1612 :             || hasLaneSpecificWidth()
    2588         1608 :             || hasLaneSpecificType()
    2589         1606 :             || hasLaneSpecificEndOffset()
    2590         1600 :             || hasLaneSpecificStopOffsets()
    2591         1600 :             || hasAccelLane()
    2592         1599 :             || hasCustomLaneShape()
    2593         1594 :             || hasLaneParams()
    2594         1592 :             || prohibitsChanging()
    2595         3473 :             || (!myLanes.empty() && myLanes.back().oppositeID != ""));
    2596              : }
    2597              : 
    2598              : 
    2599              : 
    2600              : bool
    2601       111683 : NBEdge::computeEdge2Edges(bool noLeftMovers) {
    2602              : #ifdef DEBUG_CONNECTION_GUESSING
    2603              :     if (DEBUGCOND) {
    2604              :         std::cout << "computeEdge2Edges  edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
    2605              :         for (Connection& c : myConnections) {
    2606              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2607              :         }
    2608              :         for (Connection& c : myConnectionsToDelete) {
    2609              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2610              :         }
    2611              :     }
    2612              : #endif
    2613              :     // return if this relationship has been build in previous steps or
    2614              :     //  during the import
    2615       111683 :     if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
    2616              :         return true;
    2617              :     }
    2618        67249 :     const bool fromRail = isRailway(getPermissions());
    2619       233332 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
    2620       166083 :         if (noLeftMovers && myTo->isLeftMover(this, out)) {
    2621         1140 :             continue;
    2622              :         }
    2623              :         // avoid sharp railway turns
    2624       164943 :         if (fromRail && isRailway(out->getPermissions())) {
    2625         9701 :             const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
    2626         9701 :             if (angle > 150) {
    2627         3894 :                 continue;
    2628         5807 :             } else if (angle > 90) {
    2629              :                 // possibly the junction is large enough to achieve a plausible radius:
    2630          129 :                 const PositionVector& fromShape = myLanes.front().shape;
    2631          129 :                 const PositionVector& toShape = out->getLanes().front().shape;
    2632          129 :                 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
    2633          129 :                 const double radius = shape.length2D() / DEG2RAD(angle);
    2634          129 :                 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
    2635              :                 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
    2636          129 :                 if (radius < minRadius) {
    2637              :                     continue;
    2638              :                 }
    2639          129 :             }
    2640              :         }
    2641       160921 :         if (out == myTurnDestination) {
    2642              :             // will be added by appendTurnaround
    2643        42866 :             continue;
    2644              :         }
    2645       118055 :         if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
    2646              :             // no common permissions
    2647        29017 :             continue;
    2648              :         }
    2649       178076 :         myConnections.push_back(Connection(-1, out, -1));
    2650              :     }
    2651        67249 :     myStep = EdgeBuildingStep::EDGE2EDGES;
    2652        67249 :     return true;
    2653              : }
    2654              : 
    2655              : 
    2656              : bool
    2657       111683 : NBEdge::computeLanes2Edges() {
    2658              : #ifdef DEBUG_CONNECTION_GUESSING
    2659              :     if (DEBUGCOND) {
    2660              :         std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
    2661              :         for (Connection& c : myConnections) {
    2662              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2663              :         }
    2664              :         for (Connection& c : myConnectionsToDelete) {
    2665              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2666              :         }
    2667              :     }
    2668              : #endif
    2669              :     // return if this relationship has been build in previous steps or
    2670              :     //  during the import
    2671       111683 :     if (myStep >= EdgeBuildingStep::LANES2EDGES) {
    2672        43968 :         if (myStep == EdgeBuildingStep::LANES2LANES_USER && myConnections.size() > 1) {
    2673        95229 :             for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    2674        69977 :                 if ((*i).toEdge == nullptr) {
    2675            3 :                     WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
    2676            1 :                     i = myConnections.erase(i);
    2677              :                 } else {
    2678              :                     i++;
    2679              :                 }
    2680              :             }
    2681              :         }
    2682        43968 :         return true;
    2683              :     }
    2684              :     assert(myStep == EdgeBuildingStep::EDGE2EDGES);
    2685              :     // get list of possible outgoing edges sorted by direction clockwise
    2686              :     //  the edge in the backward direction (turnaround) is not in the list
    2687        67715 :     const EdgeVector* edges = getConnectedSorted();
    2688        67715 :     if (myConnections.size() != 0 && edges->size() == 0) {
    2689              :         // dead end per definition!?
    2690              :         myConnections.clear();
    2691              :     } else {
    2692              :         // divide the lanes on reachable edges
    2693        67693 :         divideOnEdges(edges);
    2694              :     }
    2695        67715 :     delete edges;
    2696        67715 :     myStep = EdgeBuildingStep::LANES2EDGES;
    2697        67715 :     return true;
    2698              : }
    2699              : 
    2700              : 
    2701              : std::vector<LinkDirection>
    2702          394 : NBEdge::decodeTurnSigns(int turnSigns, int shift) {
    2703              :     std::vector<LinkDirection> result;
    2704         3546 :     for (int i = 0; i < 8; i++) {
    2705              :         // see LinkDirection in SUMOXMLDefinitions.h
    2706         3152 :         if ((turnSigns & (1 << (i + shift))) != 0) {
    2707          323 :             result.push_back((LinkDirection)(1 << i));
    2708              :         }
    2709              :     }
    2710          394 :     return result;
    2711            0 : }
    2712              : 
    2713              : void
    2714          276 : NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
    2715          276 :     if (dirs.size() > 0) {
    2716           77 :         if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
    2717            2 :             perm &= ~spec;
    2718              :         } else {
    2719           75 :             perm |= spec;
    2720              :         }
    2721              :     }
    2722          276 : }
    2723              : 
    2724              : bool
    2725           34 : NBEdge::applyTurnSigns() {
    2726              : #ifdef DEBUG_TURNSIGNS
    2727              :     std::cout << "applyTurnSigns edge=" << getID() << "\n";
    2728              : #endif
    2729              :     // build a map of target edges and lanes
    2730              :     std::vector<const NBEdge*> targets;
    2731              :     std::map<const NBEdge*, std::vector<int> > toLaneMap;
    2732          144 :     for (const Connection& c : myConnections) {
    2733          110 :         if (myLanes[c.fromLane].turnSigns != 0) {
    2734          110 :             if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
    2735           90 :                 targets.push_back(c.toEdge);
    2736              :             }
    2737          110 :             toLaneMap[c.toEdge].push_back(c.toLane);
    2738              :         }
    2739              :     }
    2740              :     // might be unsorted due to bike lane connections
    2741          124 :     for (auto& item : toLaneMap) {
    2742           90 :         std::sort(item.second.begin(), item.second.end());
    2743              :     }
    2744              : 
    2745              :     // check number of distinct signed directions and count the number of signs for each direction
    2746              :     std::map<LinkDirection, int> signCons;
    2747              :     int allDirs = 0;
    2748          114 :     for (const Lane& lane : myLanes) {
    2749           80 :         allDirs |= lane.turnSigns;
    2750          179 :         for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
    2751           99 :             signCons[dir]++;
    2752           80 :         }
    2753              :     }
    2754           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
    2755           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
    2756           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
    2757              : 
    2758           34 :     if ((allDirs & (int)LinkDirection::NODIR) != 0) {
    2759            3 :         targets.push_back(nullptr); // dead end
    2760              :     }
    2761              : 
    2762              :     SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
    2763              :     // build a mapping from sign directions to targets
    2764           34 :     std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
    2765              :     std::map<LinkDirection, const NBEdge*> dirMap;
    2766              : #ifdef DEBUG_TURNSIGNS
    2767              :     std::cout << "  numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
    2768              : #endif
    2769           34 :     if (signedDirs.size() > targets.size()) {
    2770           18 :         WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
    2771            6 :         return false;
    2772           28 :     } else if (signedDirs.size() < targets.size()) {
    2773              :         // we need to drop some targets (i.e. turn-around)
    2774              :         // use sumo-directions as a guide
    2775              :         std::vector<LinkDirection> sumoDirs;
    2776           82 :         for (const NBEdge* to : targets) {
    2777           65 :             sumoDirs.push_back(myTo->getDirection(this, to));
    2778              :         }
    2779              :         // remove targets to the left
    2780              :         bool checkMore = true;
    2781           37 :         while (signedDirs.size() < targets.size() && checkMore) {
    2782              :             checkMore = false;
    2783              :             //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
    2784           20 :             if (sumoDirs.back() != signedDirs.back()) {
    2785              :                 targets.pop_back();
    2786              :                 sumoDirs.pop_back();
    2787              :                 checkMore = true;
    2788              :             }
    2789              :         }
    2790              :         // remove targets to the right
    2791              :         checkMore = true;
    2792           20 :         while (signedDirs.size() < targets.size() && checkMore) {
    2793              :             checkMore = false;
    2794            3 :             if (sumoDirs.front() != signedDirs.front()) {
    2795              :                 targets.erase(targets.begin());
    2796              :                 sumoDirs.erase(sumoDirs.begin());
    2797              :                 checkMore = true;
    2798              :             }
    2799              :         }
    2800              :         // remove targets by permissions
    2801              :         int i = 0;
    2802           25 :         while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
    2803            8 :             if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
    2804              :                 targets.erase(targets.begin() + i);
    2805              :                 sumoDirs.erase(sumoDirs.begin() + i);
    2806              :             } else {
    2807            8 :                 i++;
    2808              :             }
    2809              :         }
    2810           17 :         if (signedDirs.size() != targets.size()) {
    2811            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());
    2812              :             return false;
    2813              :         }
    2814           17 :     }
    2815              :     // directions and connections are both sorted from right to left
    2816           87 :     for (int i = 0; i < (int)signedDirs.size(); i++) {
    2817           62 :         dirMap[signedDirs[i]] = targets[i];
    2818              :     }
    2819              :     // check whether we have enough target lanes for a each signed direction
    2820           83 :     for (auto item : signCons) {
    2821           60 :         const LinkDirection dir = item.first;
    2822           60 :         if (dir == LinkDirection::NODIR) {
    2823            2 :             continue;
    2824              :         }
    2825           58 :         const NBEdge* to = dirMap[dir];
    2826           58 :         int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
    2827           58 :         if (candidates == 0) {
    2828            4 :             WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
    2829            2 :             return false;
    2830              :         }
    2831           57 :         std::vector<int>& knownTargets = toLaneMap[to];
    2832           57 :         if ((int)knownTargets.size() < item.second) {
    2833            4 :             if (candidates < item.second) {
    2834            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"),
    2835              :                                getID(), item.second, toString(dir), to->getID(), candidates);
    2836            1 :                 return false;
    2837              :             }
    2838              :             int i;
    2839              :             int iInc;
    2840              :             int iEnd;
    2841            3 :             if (dir > LinkDirection::STRAIGHT) {
    2842              :                 // set more targets on the left
    2843            1 :                 i = to->getNumLanes() - 1;
    2844              :                 iInc = -1;
    2845              :                 iEnd = -1;
    2846              :             } else {
    2847              :                 // set more targets on the right
    2848            2 :                 i = 0;
    2849              :                 iInc = 1;
    2850            2 :                 iEnd = to->getNumLanes();
    2851              :             }
    2852            8 :             while ((int)knownTargets.size() < item.second && i != iEnd) {
    2853            5 :                 if ((to->getPermissions(i) & defaultPermissions) != 0) {
    2854            5 :                     if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
    2855            3 :                         knownTargets.push_back(i);
    2856              :                     }
    2857              :                 }
    2858            5 :                 i += iInc;
    2859              :             }
    2860            3 :             if ((int)knownTargets.size() != item.second) {
    2861            0 :                 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
    2862            0 :                 return false;
    2863              :             }
    2864            3 :             std::sort(knownTargets.begin(), knownTargets.end());
    2865              :         }
    2866              :     }
    2867              :     std::map<const NBEdge*, int> toLaneIndex;
    2868           79 :     for (int i = 0; i < getNumLanes(); i++) {
    2869           56 :         const int turnSigns = myLanes[i].turnSigns;
    2870              :         // no turnSigns are given for bicycle lanes and sidewalks
    2871           56 :         if (turnSigns != 0) {
    2872              :             // clear existing connections
    2873          253 :             for (auto it = myConnections.begin(); it != myConnections.end();) {
    2874          197 :                 if (it->fromLane == i) {
    2875           80 :                     it = myConnections.erase(it);
    2876              :                 } else {
    2877              :                     it++;
    2878              :                 }
    2879              :             }
    2880              :             // add new connections
    2881           56 :             int allSigns = (turnSigns
    2882           56 :                             | turnSigns >> TURN_SIGN_SHIFT_BUS
    2883           56 :                             | turnSigns >> TURN_SIGN_SHIFT_TAXI
    2884           56 :                             | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
    2885           56 :             std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
    2886           56 :             std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
    2887           56 :             std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
    2888           56 :             std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
    2889              :             //std::cout << "  allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
    2890           56 :             SVCPermissions fromP = getPermissions(i);
    2891           56 :             if ((fromP & SVC_PASSENGER) != 0) {
    2892              :                 // if the source permits passenger traffic, the target should too
    2893              :                 fromP = SVC_PASSENGER;
    2894              :             }
    2895          125 :             for (LinkDirection dir : decodeTurnSigns(allSigns)) {
    2896           69 :                 SVCPermissions perm = 0;
    2897           69 :                 updateTurnPermissions(perm, dir, SVCAll, all);
    2898           69 :                 updateTurnPermissions(perm, dir, SVC_BUS, bus);
    2899           69 :                 updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
    2900           69 :                 updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
    2901           69 :                 if (perm == SVCAll) {
    2902           67 :                     perm = SVC_UNSPECIFIED;
    2903              :                 }
    2904              :                 //std::cout << "   lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
    2905           69 :                 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
    2906           69 :                 if (to != nullptr) {
    2907              :                     if (toLaneIndex.count(to) == 0) {
    2908              :                         // initialize to rightmost feasible lane
    2909           55 :                         int toLane = toLaneMap[to][0];
    2910           57 :                         while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
    2911              :                             toLane++;
    2912              :                             /*
    2913              :                             if (toLane == to->getNumLanes()) {
    2914              :                                 SOFT_ASSERT(false);
    2915              :                             #ifdef DEBUG_TURNSIGNS
    2916              :                                 std::cout << "  could not find passenger lane for target=" << to->getID() << "\n";
    2917              :                             #endif
    2918              :                                 return false;
    2919              :                             }
    2920              :                             */
    2921              :                         }
    2922              : #ifdef DEBUG_TURNSIGNS
    2923              :                         std::cout << "  target=" << to->getID() << " initial toLane=" << toLane << "\n";
    2924              : #endif
    2925           55 :                         toLaneIndex[to] = toLane;
    2926              :                     }
    2927              : #ifdef DEBUG_TURNSIGNS
    2928              :                     //std::cout << "  set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
    2929              : #endif
    2930           67 :                     setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
    2931              :                                   false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
    2932              :                                   UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
    2933              :                                   myDefaultConnectionLength, PositionVector::EMPTY,
    2934              :                                   UNSPECIFIED_CONNECTION_UNCONTROLLED,
    2935              :                                   perm);
    2936           67 :                     if (toLaneIndex[to] < to->getNumLanes() - 1
    2937           67 :                             && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
    2938           13 :                         toLaneIndex[to]++;
    2939           54 :                     } else if (toLaneIndex[to] < to->getNumLanes() - 2
    2940           54 :                                && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
    2941              :                         // skip forbidden lane
    2942            0 :                         toLaneIndex[to] += 2;
    2943              :                     }
    2944              :                 }
    2945           56 :             }
    2946           56 :         }
    2947              :     }
    2948           23 :     sortOutgoingConnectionsByAngle();
    2949           23 :     sortOutgoingConnectionsByIndex();
    2950              :     return true;
    2951           68 : }
    2952              : 
    2953              : 
    2954              : bool
    2955       111683 : NBEdge::recheckLanes() {
    2956              : #ifdef DEBUG_CONNECTION_GUESSING
    2957              :     if (DEBUGCOND) {
    2958              :         std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
    2959              :         for (Connection& c : myConnections) {
    2960              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2961              :         }
    2962              :         for (Connection& c : myConnectionsToDelete) {
    2963              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2964              :         }
    2965              :     }
    2966              : #endif
    2967              :     // check delayed removals
    2968       118874 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    2969         7191 :         removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
    2970              :     }
    2971       111683 :     std::vector<int> connNumbersPerLane(myLanes.size(), 0);
    2972       337399 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    2973       225716 :         if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
    2974          105 :             i = myConnections.erase(i);
    2975              :         } else {
    2976              :             if ((*i).fromLane >= 0) {
    2977       225611 :                 ++connNumbersPerLane[(*i).fromLane];
    2978              :             }
    2979              :             ++i;
    2980              :         }
    2981              :     }
    2982       111683 :     if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
    2983              : #ifdef DEBUG_TURNSIGNS
    2984              :         if (myLanes.back().turnSigns != 0) {
    2985              :             std::cout << getID() << " hasTurnSigns\n";
    2986              :             if (myTurnSignTarget != myTo->getID()) {
    2987              :                 std::cout << "   tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
    2988              :             }
    2989              :         }
    2990              : #endif
    2991        57789 :         if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
    2992              :             // check #1:
    2993              :             // If there is a lane with no connections and any neighbour lane has
    2994              :             //  more than one connections, try to move one of them.
    2995              :             // This check is only done for edges which connections were assigned
    2996              :             //  using the standard algorithm.
    2997       132813 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    2998        75047 :                 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
    2999              :                     // dead-end lane found
    3000              :                     bool hasDeadEnd = true;
    3001              :                     // find lane with two connections or more to the right of the current lane
    3002         3892 :                     for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
    3003         2364 :                         if (getPermissions(i) != getPermissions(i2)) {
    3004              :                             break;
    3005              :                         }
    3006         1598 :                         if (connNumbersPerLane[i2] > 1) {
    3007          356 :                             connNumbersPerLane[i2]--;
    3008          848 :                             for (int i3 = i2; i3 != i; i3++) {
    3009          492 :                                 moveConnectionToLeft(i3);
    3010          492 :                                 sortOutgoingConnectionsByAngle();
    3011          492 :                                 sortOutgoingConnectionsByIndex();
    3012              :                             }
    3013              :                             hasDeadEnd = false;
    3014              :                         }
    3015              :                     }
    3016         2294 :                     if (hasDeadEnd) {
    3017              :                         // find lane with two connections or more to the left of the current lane
    3018         3013 :                         for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
    3019         1162 :                             if (getPermissions(i) != getPermissions(i2)) {
    3020              :                                 break;
    3021              :                             }
    3022         1075 :                             if (connNumbersPerLane[i2] > 1) {
    3023           75 :                                 connNumbersPerLane[i2]--;
    3024          176 :                                 for (int i3 = i2; i3 != i; i3--) {
    3025          101 :                                     moveConnectionToRight(i3);
    3026          101 :                                     sortOutgoingConnectionsByAngle();
    3027          101 :                                     sortOutgoingConnectionsByIndex();
    3028              :                                 }
    3029              :                                 hasDeadEnd = false;
    3030              :                             }
    3031              :                         }
    3032              :                     }
    3033         1938 :                     if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
    3034              :                         int passengerLanes = 0;
    3035              :                         int passengerTargetLanes = 0;
    3036          908 :                         for (const Lane& lane : myLanes) {
    3037          696 :                             if ((lane.permissions & SVC_PASSENGER) != 0) {
    3038          569 :                                 passengerLanes++;
    3039              :                             }
    3040              :                         }
    3041          790 :                         for (const NBEdge* out : myTo->getOutgoingEdges()) {
    3042          578 :                             if (!isTurningDirectionAt(out)) {
    3043         1030 :                                 for (const Lane& lane : out->getLanes()) {
    3044          637 :                                     if ((lane.permissions & SVC_PASSENGER) != 0) {
    3045          515 :                                         passengerTargetLanes++;
    3046              :                                     }
    3047              :                                 }
    3048              :                             }
    3049              :                         }
    3050          212 :                         if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
    3051              :                             // no need for dead-ends
    3052           39 :                             if (i > 0) {
    3053              :                                 // check if a connection to the right has a usable target to the left of its target
    3054           28 :                                 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
    3055           28 :                                 if (rightCons.size() > 0) {
    3056              :                                     const Connection& rc = rightCons.back();
    3057           28 :                                     NBEdge* to = rc.toEdge;
    3058           28 :                                     int toLane = rc.toLane + 1;
    3059              :                                     if (toLane < to->getNumLanes()
    3060            0 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3061           28 :                                             && !hasConnectionTo(to, toLane)) {
    3062              : #ifdef DEBUG_CONNECTION_CHECKING
    3063              :                                         std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
    3064              : #endif
    3065            0 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    3066              :                                         hasDeadEnd = false;
    3067            0 :                                         sortOutgoingConnectionsByAngle();
    3068            0 :                                         sortOutgoingConnectionsByIndex();
    3069              :                                     }
    3070              :                                     if (hasDeadEnd) {
    3071              :                                         // check if a connection to the right has a usable target to the right of its target
    3072           28 :                                         toLane = rc.toLane - 1;
    3073              :                                         if (toLane >= 0
    3074            7 :                                                 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
    3075            7 :                                                 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3076           34 :                                                 && !hasConnectionTo(to, toLane)) {
    3077              :                                             // shift the right lane connection target right and connect the dead lane to the old target
    3078            1 :                                             getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
    3079              : #ifdef DEBUG_CONNECTION_CHECKING
    3080              :                                             std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
    3081              : #endif
    3082            1 :                                             setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
    3083              :                                             hasDeadEnd = false;
    3084            1 :                                             sortOutgoingConnectionsByAngle();
    3085            1 :                                             sortOutgoingConnectionsByIndex();
    3086              :                                         }
    3087              :                                     }
    3088              :                                 }
    3089           28 :                             }
    3090           39 :                             if (hasDeadEnd && i < getNumLanes() - 1) {
    3091              :                                 // check if a connection to the left has a usable target to the right of its target
    3092           30 :                                 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
    3093           30 :                                 if (leftCons.size() > 0) {
    3094           30 :                                     NBEdge* to = leftCons.front().toEdge;
    3095           30 :                                     int toLane = leftCons.front().toLane - 1;
    3096              :                                     if (toLane >= 0
    3097            4 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3098           34 :                                             && !hasConnectionTo(to, toLane)) {
    3099              : #ifdef DEBUG_CONNECTION_CHECKING
    3100              :                                         std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
    3101              : #endif
    3102            4 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    3103              :                                         hasDeadEnd = false;
    3104            4 :                                         sortOutgoingConnectionsByAngle();
    3105            4 :                                         sortOutgoingConnectionsByIndex();
    3106              :                                     }
    3107              :                                 }
    3108           30 :                             }
    3109              : #ifdef ADDITIONAL_WARNINGS
    3110              :                             if (hasDeadEnd) {
    3111              :                                 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
    3112              :                             }
    3113              : #endif
    3114              :                         }
    3115              :                     }
    3116              :                 }
    3117              :             }
    3118        57766 :             removeInvalidConnections();
    3119              :         }
    3120              :     }
    3121              :     // check involuntary dead end at "real" junctions
    3122       111683 :     if (getPermissions() != SVC_PEDESTRIAN) {
    3123        97736 :         if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    3124          228 :             WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
    3125              :         }
    3126        97736 :         const EdgeVector& incoming = myFrom->getIncomingEdges();
    3127        97736 :         if (incoming.size() > 1) {
    3128       162746 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    3129        90656 :                 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
    3130              :                     bool connected = false;
    3131       115709 :                     for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
    3132       115565 :                         if ((*in)->hasConnectionTo(this, i)) {
    3133              :                             connected = true;
    3134              :                             break;
    3135              :                         }
    3136              :                     }
    3137        86444 :                     if (!connected) {
    3138          432 :                         WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
    3139              :                     }
    3140              :                 }
    3141              :             }
    3142              :         }
    3143              :     }
    3144              :     // avoid deadend due to change prohibitions
    3145       111683 :     if (getNumLanes() > 1 && myConnections.size() > 0) {
    3146        69355 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    3147        49053 :             Lane& lane = myLanes[i];
    3148        42557 :             if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
    3149           53 :                                                 && getSuccessors(SVC_PASSENGER).size() > 1))
    3150        49068 :                     && getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
    3151         2342 :                 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
    3152         2342 :                 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
    3153         2342 :                 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
    3154            2 :                     lane.changeLeft = SVC_UNSPECIFIED;
    3155            4 :                     WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
    3156         2340 :                 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
    3157            1 :                     lane.changeRight = SVC_UNSPECIFIED;
    3158            2 :                     WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
    3159              :                 }
    3160              :             }
    3161              :         }
    3162              :     }
    3163              : #ifdef ADDITIONAL_WARNINGS
    3164              :     // check for connections with bad access permissions
    3165              :     for (const Connection& c : myConnections) {
    3166              :         SVCPermissions fromP = getPermissions(c.fromLane);
    3167              :         SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
    3168              :         if ((fromP & SVC_PASSENGER) != 0
    3169              :                 && toP == SVC_BICYCLE) {
    3170              :             bool hasAlternative = false;
    3171              :             for (const Connection& c2 : myConnections) {
    3172              :                 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
    3173              :                         && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
    3174              :                     hasAlternative = true;
    3175              :                 }
    3176              :             }
    3177              :             if (!hasAlternative) {
    3178              :                 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
    3179              :             }
    3180              :         }
    3181              :     }
    3182              : 
    3183              : #endif
    3184              : #ifdef DEBUG_CONNECTION_GUESSING
    3185              :     if (DEBUGCOND) {
    3186              :         std::cout << "recheckLanes (final) edge=" << getID() << "\n";
    3187              :         for (Connection& c : myConnections) {
    3188              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    3189              :         }
    3190              :     }
    3191              : #endif
    3192       111683 :     if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
    3193        70322 :         myStep = EdgeBuildingStep::LANES2LANES_DONE;
    3194              :     }
    3195       111683 :     return true;
    3196       111683 : }
    3197              : 
    3198              : 
    3199       111730 : void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
    3200       111730 :     if (getNumLanes() == 0) {
    3201            0 :         return;
    3202              :     }
    3203       111730 :     const int leftmostLane = getNumLanes() - 1;
    3204              :     // check oppositeID stored in other lanes
    3205       143734 :     for (int i = 0; i < leftmostLane; i++) {
    3206        32004 :         const std::string& oppositeID = getLanes()[i].oppositeID;
    3207        64008 :         NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
    3208        32004 :         if (oppositeID != "" && oppositeID != "-") {
    3209            6 :             if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
    3210            1 :                 getLaneStruct(leftmostLane).oppositeID = oppositeID;
    3211            3 :                 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
    3212              :             } else {
    3213           15 :                 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
    3214              :             }
    3215            6 :             getLaneStruct(i).oppositeID = "";
    3216              :         }
    3217              :     }
    3218       111730 :     const std::string& oppositeID = getLanes().back().oppositeID;
    3219       111730 :     if (oppositeID != "" && oppositeID != "-") {
    3220          112 :         NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
    3221          112 :         if (oppEdge == nullptr) {
    3222            3 :             WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
    3223            1 :             getLaneStruct(leftmostLane).oppositeID = "";
    3224              :         } else {
    3225          111 :             if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
    3226            3 :                 WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
    3227            1 :                 getLaneStruct(getNumLanes() - 1).oppositeID = "";
    3228              :             } else {
    3229          220 :                 if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
    3230            1 :                     const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
    3231            4 :                     WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
    3232            1 :                     getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
    3233              :                 }
    3234          110 :                 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
    3235          110 :                 const std::string leftmostID = getLaneID(leftmostLane);
    3236          110 :                 if (oppLane.oppositeID == "") {
    3237           15 :                     WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
    3238              :                     oppLane.oppositeID = leftmostID;
    3239          105 :                 } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
    3240            1 :                     const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
    3241            1 :                     NBEdge* oppOppEdge = ec.retrieve(oppOpp);
    3242            1 :                     if (oppOppEdge == nullptr) {
    3243            0 :                         WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
    3244              :                         oppLane.oppositeID = leftmostID;
    3245              :                     } else {
    3246            1 :                         if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
    3247            0 :                             WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
    3248              :                         } else {
    3249            5 :                             WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
    3250              :                         }
    3251              :                         oppLane.oppositeID = leftmostID;
    3252            1 :                         NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
    3253            1 :                         if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
    3254              :                             oppOppLane.oppositeID = "";
    3255              :                         }
    3256              :                     }
    3257              :                 }
    3258          110 :                 if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
    3259            3 :                     if (fixOppositeLengths) {
    3260            1 :                         const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
    3261            4 :                         WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
    3262              :                                        oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
    3263            1 :                         setLoadedLength(avgLength);
    3264            1 :                         oppEdge->setLoadedLength(avgLength);
    3265              :                     } else {
    3266           10 :                         WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
    3267              :                                     ") differs in length from edge '" + getID() + "' (length " +
    3268              :                                     toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
    3269            2 :                         getLaneStruct(getNumLanes() - 1).oppositeID = "";
    3270              :                     }
    3271              :                 }
    3272              :             }
    3273              :         }
    3274              :     }
    3275              :     // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
    3276       111730 :     const NBEdge* bidi = getBidiEdge();
    3277       111730 :     if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
    3278         6480 :         getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
    3279              :     }
    3280              :     // check for valid offset and speed
    3281       118200 :     const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
    3282              :     int i = 0;
    3283       255464 :     for (const NBEdge::Lane& l : getLanes()) {
    3284       143734 :         if (startOffset + l.endOffset > getLength()) {
    3285            4 :             WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
    3286              :                            toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
    3287       143732 :         } else if (l.speed < 0.) {
    3288            0 :             WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
    3289       143732 :         } else if (l.speed == 0.) {
    3290            0 :             WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
    3291              :         }
    3292       143734 :         i++;
    3293              :     }
    3294              : }
    3295              : 
    3296        57766 : void NBEdge::removeInvalidConnections() {
    3297              :     // check restrictions
    3298       190155 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3299              :         Connection& c = *i;
    3300       132389 :         const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
    3301       132389 :         if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
    3302              :             // these are computed in NBNode::buildWalkingAreas
    3303              : #ifdef DEBUG_CONNECTION_CHECKING
    3304              :             std::cout << " remove pedCon " << c.getDescription(this) << "\n";
    3305              : #endif
    3306          475 :             i = myConnections.erase(i);
    3307       131914 :         } else if (common == 0) {
    3308              :             // no common permissions.
    3309              :             // try to find a suitable target lane to the right
    3310           83 :             const int origToLane = c.toLane;
    3311           83 :             c.toLane = -1; // ignore this connection when calling hasConnectionTo
    3312              :             int toLane = origToLane;
    3313           83 :             while (toLane > 0
    3314           45 :                     && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3315          143 :                     && !hasConnectionTo(c.toEdge, toLane)
    3316              :                   ) {
    3317           30 :                 toLane--;
    3318              :             }
    3319           83 :             if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3320           83 :                     && !hasConnectionTo(c.toEdge, toLane)) {
    3321            4 :                 c.toLane = toLane;
    3322              :                 ++i;
    3323              :             } else {
    3324              :                 // try to find a suitable target lane to the left
    3325              :                 toLane = origToLane;
    3326          148 :                 while (toLane < (int)c.toEdge->getNumLanes() - 1
    3327           95 :                         && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3328          217 :                         && !hasConnectionTo(c.toEdge, toLane)
    3329              :                       ) {
    3330           69 :                     toLane++;
    3331              :                 }
    3332           79 :                 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3333           79 :                         && !hasConnectionTo(c.toEdge, toLane)) {
    3334            0 :                     c.toLane = toLane;
    3335              :                     ++i;
    3336              :                 } else {
    3337              :                     // no alternative target found
    3338              : #ifdef DEBUG_CONNECTION_CHECKING
    3339              :                     std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
    3340              : #endif
    3341           79 :                     i = myConnections.erase(i);
    3342              :                 }
    3343              :             }
    3344       140181 :         } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
    3345       140101 :                    && isTurningDirectionAt(c.toEdge))  {
    3346              :             // do not allow sharp rail turns
    3347              : #ifdef DEBUG_CONNECTION_CHECKING
    3348              :             std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
    3349              : #endif
    3350         2705 :             i = myConnections.erase(i);
    3351              :         } else {
    3352              :             ++i;
    3353              :         }
    3354              :     }
    3355        57766 : }
    3356              : 
    3357              : void
    3358        67693 : NBEdge::divideOnEdges(const EdgeVector* outgoing) {
    3359        67693 :     if (outgoing->size() == 0) {
    3360              :         // we have to do this, because the turnaround may have been added before
    3361              :         myConnections.clear();
    3362        11717 :         return;
    3363              :     }
    3364              : 
    3365              : #ifdef DEBUG_CONNECTION_GUESSING
    3366              :     if (DEBUGCOND) {
    3367              :         std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
    3368              :     }
    3369              : #endif
    3370              : 
    3371              :     // build connections for miv lanes
    3372              :     std::vector<int> availableLanes;
    3373       127639 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3374        71663 :         if ((getPermissions(i) & SVC_PASSENGER) != 0) {
    3375        46098 :             availableLanes.push_back(i);
    3376              :         }
    3377              :     }
    3378        55976 :     if (availableLanes.size() > 0) {
    3379        34311 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3380              :     }
    3381              :     // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
    3382              :     availableLanes.clear();
    3383       127639 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3384        71663 :         const SVCPermissions perms = getPermissions(i);
    3385        71663 :         if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
    3386        61399 :             continue;
    3387              :         }
    3388        10264 :         availableLanes.push_back(i);
    3389              :     }
    3390        55976 :     if (availableLanes.size() > 0) {
    3391        10192 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3392              :     }
    3393              :     // build connections for busses from lanes that were excluded in the previous step
    3394              :     availableLanes.clear();
    3395       127639 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3396        71663 :         const SVCPermissions perms = getPermissions(i);
    3397        71663 :         if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
    3398        71407 :             continue;
    3399              :         }
    3400          256 :         availableLanes.push_back(i);
    3401              :     }
    3402        55976 :     if (availableLanes.size() > 0) {
    3403          253 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3404              :     }
    3405              :     // build connections for bicycles (possibly combined with pedestrians)
    3406              :     availableLanes.clear();
    3407       127639 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3408        71663 :         const SVCPermissions perms = getPermissions(i);
    3409        71663 :         if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
    3410        69986 :             continue;
    3411              :         }
    3412         1677 :         availableLanes.push_back(i);
    3413              :     }
    3414        55976 :     if (availableLanes.size() > 0) {
    3415         1652 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3416              :     }
    3417              :     // clean up unassigned fromLanes
    3418              :     bool explicitTurnaround = false;
    3419        55976 :     SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
    3420       245579 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3421       189603 :         if ((*i).fromLane == -1) {
    3422        89886 :             if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
    3423              :                 explicitTurnaround = true;
    3424          109 :                 turnaroundPermissions = (*i).permissions;
    3425              :             }
    3426        89886 :             if ((*i).permissions != SVC_UNSPECIFIED) {
    3427         1472 :                 for (Connection& c : myConnections) {
    3428         1280 :                     if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
    3429              :                         // carry over loaded edge2edge permissions
    3430          345 :                         c.permissions = (*i).permissions;
    3431              :                     }
    3432              :                 }
    3433              :             }
    3434        89886 :             i = myConnections.erase(i);
    3435              :         } else {
    3436              :             ++i;
    3437              :         }
    3438              :     }
    3439        55976 :     if (explicitTurnaround) {
    3440          218 :         myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
    3441          109 :         myConnections.back().permissions = turnaroundPermissions;
    3442              :     }
    3443        55976 :     sortOutgoingConnectionsByIndex();
    3444        55976 : }
    3445              : 
    3446              : 
    3447              : void
    3448        46408 : NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3449        46408 :     const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
    3450        46408 :     if (priorities.empty()) {
    3451              :         return;
    3452              :     }
    3453              : #ifdef DEBUG_CONNECTION_GUESSING
    3454              :     if (DEBUGCOND) {
    3455              :         std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
    3456              :     }
    3457              : #endif
    3458              :     // compute the resulting number of lanes that should be used to reach the following edge
    3459        46360 :     const int numOutgoing = (int)outgoing->size();
    3460              :     std::vector<int> resultingLanesFactor;
    3461        46360 :     resultingLanesFactor.reserve(numOutgoing);
    3462              :     int minResulting = std::numeric_limits<int>::max();
    3463       137118 :     for (int i = 0; i < numOutgoing; i++) {
    3464              :         // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
    3465        90758 :         const int res = priorities[i] * (int)availableLanes.size();
    3466        90758 :         resultingLanesFactor.push_back(res);
    3467        90758 :         if (minResulting > res && res > 0) {
    3468              :             // prevent minResulting from becoming 0
    3469              :             minResulting = res;
    3470              :         }
    3471              :     }
    3472              :     // compute the number of virtual edges
    3473              :     //  a virtual edge is used as a replacement for a real edge from now on
    3474              :     //  it shall allow to divide the existing lanes on this structure without
    3475              :     //  regarding the structure of outgoing edges
    3476              :     int numVirtual = 0;
    3477              :     // compute the transition from virtual to real edges
    3478              :     EdgeVector transition;
    3479        46360 :     transition.reserve(numOutgoing);
    3480       137118 :     for (int i = 0; i < numOutgoing; i++) {
    3481              :         // tmpNum will be the number of connections from this edge to the next edge
    3482              :         assert(i < (int)resultingLanesFactor.size());
    3483        90758 :         const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
    3484        90758 :         numVirtual += tmpNum;
    3485       515793 :         for (int j = 0; j < tmpNum; j++) {
    3486       425035 :             transition.push_back((*outgoing)[i]);
    3487              :         }
    3488              :     }
    3489              : #ifdef DEBUG_CONNECTION_GUESSING
    3490              :     if (DEBUGCOND) {
    3491              :         std::cout << "   minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
    3492              :     }
    3493              : #endif
    3494              : 
    3495              :     // assign lanes to edges
    3496              :     //  (conversion from virtual to real edges is done)
    3497              :     ToEdgeConnectionsAdder adder(transition);
    3498        46360 :     Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
    3499              :     const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
    3500       137118 :     for (NBEdge* const target : *outgoing) {
    3501              :         assert(l2eConns.find(target) != l2eConns.end());
    3502       192246 :         for (const int j : l2eConns.find(target)->second) {
    3503       101488 :             const int fromIndex = availableLanes[j];
    3504       101488 :             if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
    3505              :                 // exclude connection if fromLane and toEdge have no common permissions
    3506           61 :                 continue;
    3507              :             }
    3508       101427 :             if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
    3509              :                 // exclude connection if the only commonly permitted class are pedestrians
    3510              :                 // these connections are later built in NBNode::buildWalkingAreas
    3511          249 :                 continue;
    3512              :             }
    3513              :             // avoid building more connections than the edge has viable lanes (earlier
    3514              :             // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
    3515              :             //    @todo To decide which target lanes are still available we need to do a
    3516              :             // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
    3517       101178 :             const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3518              :             int targetLanes = target->getNumLanes();
    3519       101178 :             if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3520         7204 :                 --targetLanes;
    3521              :             }
    3522       101178 :             if (numConsToTarget >= targetLanes) {
    3523         2619 :                 continue;
    3524              :             }
    3525        98559 :             if (myLanes[fromIndex].connectionsDone) {
    3526              :                 // we already have complete information about connections from
    3527              :                 // this lane. do not add anything else
    3528              : #ifdef DEBUG_CONNECTION_GUESSING
    3529              :                 if (DEBUGCOND) {
    3530              :                     std::cout << "     connectionsDone from " << getID() << "_" << fromIndex << ": ";
    3531              :                     for (const Connection& c : getConnectionsFromLane(fromIndex)) {
    3532              :                         std::cout << c.getDescription(this) << ", ";
    3533              :                     }
    3534              :                     std::cout << "\n";
    3535              :                 }
    3536              : #endif
    3537           38 :                 continue;
    3538              :             }
    3539       197042 :             myConnections.push_back(Connection(fromIndex, target, -1));
    3540              : #ifdef DEBUG_CONNECTION_GUESSING
    3541              :             if (DEBUGCOND) {
    3542              :                 std::cout << "     request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3543              :             }
    3544              : #endif
    3545              :         }
    3546              :     }
    3547              : 
    3548        46360 :     addStraightConnections(outgoing, availableLanes, priorities);
    3549        46408 : }
    3550              : 
    3551              : 
    3552              : void
    3553        46360 : NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
    3554              :     // ensure sufficient straight connections for the (highest-priority) straight target
    3555        46360 :     const int numOutgoing = (int) outgoing->size();
    3556              :     NBEdge* target = nullptr;
    3557              :     NBEdge* rightOfTarget = nullptr;
    3558              :     NBEdge* leftOfTarget = nullptr;
    3559              :     int maxPrio = 0;
    3560       137118 :     for (int i = 0; i < numOutgoing; i++) {
    3561        90758 :         if (maxPrio < priorities[i]) {
    3562        75867 :             const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
    3563        75867 :             if (dir == LinkDirection::STRAIGHT) {
    3564        38158 :                 maxPrio = priorities[i];
    3565        38158 :                 target = (*outgoing)[i];
    3566        38158 :                 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
    3567        38158 :                 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
    3568              :             }
    3569              :         }
    3570              :     }
    3571        46360 :     if (target == nullptr) {
    3572              :         return;
    3573              :     }
    3574        37992 :     int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3575              :     int targetLanes = (int)target->getNumLanes();
    3576        37992 :     if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3577         3213 :         --targetLanes;
    3578              :     }
    3579        37992 :     const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
    3580              : #ifdef DEBUG_CONNECTION_GUESSING
    3581              :     if (DEBUGCOND) {
    3582              :         std::cout << "  checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
    3583              :     }
    3584              : #endif
    3585              :     std::vector<int>::const_iterator it_avail = availableLanes.begin();
    3586        39087 :     while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
    3587         1095 :         const int fromIndex = *it_avail;
    3588              :         if (
    3589              :             // not yet connected
    3590         1095 :             (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
    3591              :             // matching permissions
    3592          650 :             && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
    3593              :             // more than pedestrians
    3594          648 :             && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
    3595              :             // lane not yet fully defined
    3596         1741 :             && !myLanes[fromIndex].connectionsDone
    3597              :         ) {
    3598              : #ifdef DEBUG_CONNECTION_GUESSING
    3599              :             if (DEBUGCOND) {
    3600              :                 std::cout << "    candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3601              :             }
    3602              : #endif
    3603              :             // prevent same-edge conflicts
    3604              :             if (
    3605              :                 // no outgoing connections to the right from further left
    3606          317 :                 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3607              :                 // no outgoing connections to the left from further right
    3608         1260 :                 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
    3609              : #ifdef DEBUG_CONNECTION_GUESSING
    3610              :                 if (DEBUGCOND) {
    3611              :                     std::cout << "     request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3612              :                 }
    3613              : #endif
    3614         1144 :                 myConnections.push_back(Connection(fromIndex, target, -1));
    3615          572 :                 numConsToTarget++;
    3616              :             } else {
    3617              : #ifdef DEBUG_CONNECTION_GUESSING
    3618              :                 if (DEBUGCOND) std::cout
    3619              :                             << "     fail check1="
    3620              :                             << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3621              :                             << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
    3622              :                             << " rightOfTarget=" << rightOfTarget->getID()
    3623              :                             << " leftOfTarget=" << leftOfTarget->getID()
    3624              :                             << "\n";
    3625              : #endif
    3626              : 
    3627              :             }
    3628              :         }
    3629              :         ++it_avail;
    3630              :     }
    3631              : }
    3632              : 
    3633              : 
    3634              : const std::vector<int>
    3635        46408 : NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3636              :     std::vector<int> priorities;
    3637        46408 :     MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
    3638              :     const int dist = mainDirections.getStraightest();
    3639        46408 :     if (dist == -1) {
    3640              :         return priorities;
    3641              :     }
    3642              :     // copy the priorities first
    3643        46360 :     priorities.reserve(outgoing->size());
    3644       137118 :     for (const NBEdge* const out : *outgoing) {
    3645        90758 :         int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
    3646              :         assert((prio + 1) * 2 > 0);
    3647        90758 :         prio = (prio + 1) * 2;
    3648        90758 :         priorities.push_back(prio);
    3649              :     }
    3650              :     // when the right turning direction has not a higher priority, divide
    3651              :     //  the importance by 2 due to the possibility to leave the junction
    3652              :     //  faster from this lane
    3653              : #ifdef DEBUG_CONNECTION_GUESSING
    3654              :     if (DEBUGCOND) std::cout << "  prepareEdgePriorities " << getID()
    3655              :                                  << " outgoing=" << toString(*outgoing)
    3656              :                                  << " priorities1=" << toString(priorities)
    3657              :                                  << " dist=" << dist
    3658              :                                  << "\n";
    3659              : #endif
    3660        46360 :     if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
    3661              :         assert(priorities.size() > 0);
    3662        14632 :         priorities[0] /= 2;
    3663              : #ifdef DEBUG_CONNECTION_GUESSING
    3664              :         if (DEBUGCOND) {
    3665              :             std::cout << "   priorities2=" << toString(priorities) << "\n";
    3666              :         }
    3667              : #endif
    3668              :     }
    3669              :     // HEURISTIC:
    3670              :     // when no higher priority exists, let the forward direction be
    3671              :     //  the main direction
    3672        46360 :     if (mainDirections.empty()) {
    3673              :         assert(dist < (int)priorities.size());
    3674        10383 :         priorities[dist] *= 2;
    3675              : #ifdef DEBUG_CONNECTION_GUESSING
    3676              :         if (DEBUGCOND) {
    3677              :             std::cout << "   priorities3=" << toString(priorities) << "\n";
    3678              :         }
    3679              : #endif
    3680              :     }
    3681        46360 :     if (NBNode::isTrafficLight(myTo->getType())) {
    3682         4368 :         priorities[dist] += 1;
    3683              :     } else {
    3684              :         // try to ensure separation of left turns
    3685        41992 :         if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
    3686          818 :             priorities[0] /= 4;
    3687          818 :             priorities[(int)priorities.size() - 1] /= 2;
    3688              : #ifdef DEBUG_CONNECTION_GUESSING
    3689              :             if (DEBUGCOND) {
    3690              :                 std::cout << "   priorities6=" << toString(priorities) << "\n";
    3691              :             }
    3692              : #endif
    3693        41174 :         } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
    3694        21645 :                    && outgoing->size() > 2
    3695         4449 :                    && availableLanes.size() == 2
    3696        41350 :                    && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
    3697          167 :             priorities[0] /= 4;
    3698          167 :             priorities.back() /= 2;
    3699              : #ifdef DEBUG_CONNECTION_GUESSING
    3700              :             if (DEBUGCOND) {
    3701              :                 std::cout << "   priorities7=" << toString(priorities) << "\n";
    3702              :             }
    3703              : #endif
    3704              :         }
    3705              :     }
    3706        46360 :     if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
    3707        24571 :         if (myLanes.size() > 2) {
    3708         2938 :             priorities[dist] *= 2;
    3709              : #ifdef DEBUG_CONNECTION_GUESSING
    3710              :             if (DEBUGCOND) {
    3711              :                 std::cout << "   priorities4=" << toString(priorities) << "\n";
    3712              :             }
    3713              : #endif
    3714              :         } else {
    3715        21633 :             priorities[dist] *= 3;
    3716              : #ifdef DEBUG_CONNECTION_GUESSING
    3717              :             if (DEBUGCOND) {
    3718              :                 std::cout << "   priorities5=" << toString(priorities) << "\n";
    3719              :             }
    3720              : #endif
    3721              :         }
    3722              :     }
    3723              :     return priorities;
    3724        46408 : }
    3725              : 
    3726              : 
    3727              : void
    3728        66741 : NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
    3729              :     // do nothing if no turnaround is known
    3730        66741 :     if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
    3731              :         return;
    3732              :     }
    3733              :     // do nothing if the destination node is controlled by a tls and no turnarounds
    3734              :     //  shall be appended for such junctions
    3735        44592 :     if (noTLSControlled && myTo->isTLControlled()) {
    3736              :         return;
    3737              :     }
    3738        44520 :     if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
    3739              :         return;
    3740              :     }
    3741              :     bool isDeadEnd = true;
    3742        46164 :     for (const Connection& c : myConnections) {
    3743        37049 :         if ((c.toEdge->getPermissions(c.toLane)
    3744        37049 :                 & getPermissions(c.fromLane)
    3745        37049 :                 & SVC_PASSENGER) != 0
    3746        37049 :                 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
    3747              :             isDeadEnd = false;
    3748              :             break;
    3749              :         }
    3750              :     }
    3751        44506 :     if (onlyDeadends && !isDeadEnd) {
    3752              :         return;
    3753              :     }
    3754        44307 :     const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3755        44307 :     if (onlyTurnlane) {
    3756           90 :         for (const Connection& c : getConnectionsFromLane(fromLane)) {
    3757           68 :             LinkDirection dir = myTo->getDirection(this, c.toEdge);
    3758           68 :             if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
    3759              :                 return;
    3760              :             }
    3761           82 :         }
    3762              :     }
    3763        44247 :     const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3764        44247 :     if (checkPermissions) {
    3765        44247 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
    3766              :             // exclude connection if fromLane and toEdge have no common permissions
    3767              :             return;
    3768              :         }
    3769        43974 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
    3770              :             // exclude connection if the only commonly permitted class are pedestrians
    3771              :             // these connections are later built in NBNode::buildWalkingAreas
    3772              :             return;
    3773              :         }
    3774              :     }
    3775              :     // avoid railway turn-arounds
    3776        41366 :     if (isRailway(getPermissions() & myTurnDestination->getPermissions())
    3777        41366 :             && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
    3778              :         // except at dead-ends on bidi-edges where they model a reversal in train direction
    3779              :         // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
    3780         3110 :         if (isBidiRail() && isRailDeadEnd()) {
    3781              :             // add a slow connection because direction-reversal implies stopping
    3782         2808 :             setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
    3783         2808 :             return;
    3784              :         } else {
    3785          302 :             return;
    3786              :         }
    3787              :     };
    3788        38256 :     if (noGeometryLike && !isDeadEnd) {
    3789              :         // ignore paths and service entrances if this edge is for passenger traffic
    3790        32074 :         if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
    3791        24234 :                                      && !onlyTurnlane
    3792        24226 :                                      && myTo->geometryLike(
    3793        56300 :                                          NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
    3794        56300 :                                          NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
    3795              :             // make sure the turnDestination has other incoming edges
    3796         6095 :             EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
    3797         6095 :             if (turnIncoming.size() > 1) {
    3798              :                 // this edge is always part of incoming
    3799              :                 return;
    3800              :             }
    3801         6095 :         }
    3802              :     }
    3803        64532 :     setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
    3804              : }
    3805              : 
    3806              : 
    3807              : bool
    3808     15181987 : NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
    3809              :     // maybe it was already set as the turning direction
    3810     15181987 :     if (edge == myTurnDestination) {
    3811              :         return true;
    3812     12004860 :     } else if (myTurnDestination != nullptr) {
    3813              :         // otherwise - it's not if a turning direction exists
    3814              :         return false;
    3815              :     }
    3816      4428574 :     return edge == myPossibleTurnDestination;
    3817              : }
    3818              : 
    3819              : 
    3820              : NBNode*
    3821            0 : NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
    3822              :     // return the from-node when the position is at the begin of the edge
    3823            0 :     if (pos < tolerance) {
    3824            0 :         return myFrom;
    3825              :     }
    3826              :     // return the to-node when the position is at the end of the edge
    3827            0 :     if (pos > myLength - tolerance) {
    3828            0 :         return myTo;
    3829              :     }
    3830              :     return nullptr;
    3831              : }
    3832              : 
    3833              : 
    3834              : void
    3835           22 : NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
    3836              :     int lanes = e->getNumLanes();
    3837           49 :     for (int i = 0; i < lanes; i++) {
    3838           68 :         for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
    3839              :             assert(el.tlID == "");
    3840           82 :             addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
    3841           27 :         }
    3842              :     }
    3843           22 : }
    3844              : 
    3845              : 
    3846              : bool
    3847           93 : NBEdge::lanesWereAssigned() const {
    3848           93 :     return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
    3849              : }
    3850              : 
    3851              : 
    3852              : double
    3853            0 : NBEdge::getMaxLaneOffset() {
    3854            0 :     return SUMO_const_laneWidth * (double)myLanes.size();
    3855              : }
    3856              : 
    3857              : 
    3858              : bool
    3859       241511 : NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
    3860      1181468 :     for (const Connection& c : myConnections) {
    3861       940514 :         if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
    3862              :             return false;
    3863              :         }
    3864              :     }
    3865              :     return true;
    3866              : }
    3867              : 
    3868              : 
    3869              : bool
    3870        36861 : NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
    3871        36861 :     const int fromLane = c.getFromLane();
    3872        36861 :     NBEdge* toEdge = c.getTo();
    3873        36861 :     const int toLane = c.getToLane();
    3874              :     const int tlIndex = c.getTLIndex();
    3875              :     const int tlIndex2 = c.getTLIndex2();
    3876              :     // check whether the connection was not set as not to be controled previously
    3877        36861 :     if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
    3878              :         return false;
    3879              :     }
    3880              : 
    3881              :     assert(fromLane < 0 || fromLane < (int) myLanes.size());
    3882              :     // try to use information about the connections if given
    3883        36861 :     if (fromLane >= 0 && toLane >= 0) {
    3884              :         // find the specified connection
    3885              :         std::vector<Connection>::iterator i =
    3886        36861 :             find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
    3887              :         // ok, we have to test this as on the removal of self-loop edges some connections
    3888              :         //  will be reassigned
    3889        36861 :         if (i != myConnections.end()) {
    3890              :             // get the connection
    3891              :             Connection& connection = *i;
    3892              :             // set the information about the tl
    3893        36861 :             connection.tlID = tlID;
    3894        36861 :             connection.tlLinkIndex = tlIndex;
    3895        36861 :             connection.tlLinkIndex2 = tlIndex2;
    3896              :             return true;
    3897              :         }
    3898              :     }
    3899              :     // if the original connection was not found, set the information for all
    3900              :     //  connections
    3901              :     int no = 0;
    3902              :     bool hadError = false;
    3903            0 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    3904            0 :         if ((*i).toEdge != toEdge) {
    3905            0 :             continue;
    3906              :         }
    3907            0 :         if (fromLane >= 0 && fromLane != (*i).fromLane) {
    3908            0 :             continue;
    3909              :         }
    3910            0 :         if (toLane >= 0 && toLane != (*i).toLane) {
    3911            0 :             continue;
    3912              :         }
    3913            0 :         if ((*i).tlID == "") {
    3914              :             (*i).tlID = tlID;
    3915            0 :             (*i).tlLinkIndex = tlIndex;
    3916            0 :             (*i).tlLinkIndex2 = tlIndex2;
    3917            0 :             no++;
    3918              :         } else {
    3919            0 :             if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
    3920            0 :                 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
    3921              :                 hadError = true;
    3922              :             }
    3923              :         }
    3924              :     }
    3925            0 :     if (hadError && no == 0) {
    3926            0 :         WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
    3927              :     }
    3928              :     return true;
    3929              : }
    3930              : 
    3931              : 
    3932              : void
    3933       111759 : NBEdge::clearControllingTLInformation() {
    3934       334338 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
    3935       222579 :         it->tlID = "";
    3936              :     }
    3937       111759 : }
    3938              : 
    3939              : 
    3940              : PositionVector
    3941       552162 : NBEdge::getCWBoundaryLine(const NBNode& n) const {
    3942       552162 :     PositionVector ret;
    3943              :     int lane;
    3944       552162 :     if (myFrom == (&n)) {
    3945              :         // outgoing
    3946       295082 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3947       295082 :         ret = myLanes[lane].shape;
    3948              :     } else {
    3949              :         // incoming
    3950       257080 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3951       514160 :         ret = myLanes[lane].shape.reverse();
    3952              :     }
    3953       552162 :     ret.move2side(getLaneWidth(lane) / 2.);
    3954       552162 :     return ret;
    3955            0 : }
    3956              : 
    3957              : 
    3958              : PositionVector
    3959       550252 : NBEdge::getCCWBoundaryLine(const NBNode& n) const {
    3960       550252 :     PositionVector ret;
    3961              :     int lane;
    3962       550252 :     if (myFrom == (&n)) {
    3963              :         // outgoing
    3964       257060 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3965       257060 :         ret = myLanes[lane].shape;
    3966              :     } else {
    3967              :         // incoming
    3968       293192 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3969       586384 :         ret = myLanes[lane].shape.reverse();
    3970              :     }
    3971       550252 :     ret.move2side(-getLaneWidth(lane) / 2.);
    3972       550252 :     return ret;
    3973            0 : }
    3974              : 
    3975              : 
    3976              : bool
    3977         8579 : NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
    3978              :     // ok, the number of lanes must match
    3979         8579 :     if (myLanes.size() != possContinuation->myLanes.size()) {
    3980              :         reason = "laneNumber";
    3981          327 :         return false;
    3982              :     }
    3983              :     // do not create self loops
    3984         8252 :     if (myFrom == possContinuation->myTo) {
    3985              :         reason = "loop";
    3986         1136 :         return false;
    3987              :     }
    3988              :     // conserve bidi-rails
    3989         7116 :     if (isBidiRail() != possContinuation->isBidiRail()) {
    3990              :         reason = "bidi-rail";
    3991            4 :         return false;
    3992              :     }
    3993              :     // also, check whether the connections - if any exit do allow to join
    3994              :     //  both edges
    3995              :     // This edge must have a one-to-one connection to the following lanes
    3996         7112 :     switch (myStep) {
    3997              :         case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
    3998              :             break;
    3999              :         case EdgeBuildingStep::INIT:
    4000              :             break;
    4001           14 :         case EdgeBuildingStep::EDGE2EDGES: {
    4002              :             // the following edge must be connected
    4003           14 :             const EdgeVector& conn = getConnectedEdges();
    4004           14 :             if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
    4005              :                 reason = "disconnected";
    4006              :                 return false;
    4007              :             }
    4008           14 :         }
    4009              :         break;
    4010           20 :         case EdgeBuildingStep::LANES2EDGES:
    4011              :         case EdgeBuildingStep::LANES2LANES_RECHECK:
    4012              :         case EdgeBuildingStep::LANES2LANES_DONE:
    4013              :         case EdgeBuildingStep::LANES2LANES_USER: {
    4014              :             // the possible continuation must be connected
    4015           20 :             if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
    4016              :                 reason = "disconnected";
    4017            0 :                 return false;
    4018              :             }
    4019              :             // all lanes must go to the possible continuation
    4020           20 :             std::vector<int> conns = getConnectionLanes(possContinuation);
    4021           20 :             const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
    4022           20 :             if (conns.size() < myLanes.size() - offset) {
    4023              :                 reason = "some lanes disconnected";
    4024              :                 return false;
    4025              :             }
    4026           20 :         }
    4027              :         break;
    4028              :         default:
    4029              :             break;
    4030              :     }
    4031         7112 :     const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
    4032         7113 :     if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
    4033              :         return true;
    4034              :     }
    4035         7111 :     const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
    4036         7111 :     if (maxJunctionSize >= 0) {
    4037            4 :         const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
    4038            4 :         if (junctionSize > maxJunctionSize + POSITION_EPS) {
    4039            8 :             reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
    4040            2 :             return false;
    4041              :         }
    4042              :     }
    4043              :     // the priority, too (?)
    4044         7109 :     if (getPriority() != possContinuation->getPriority()) {
    4045              :         reason = "priority";
    4046           70 :         return false;
    4047              :     }
    4048              :     // the speed allowed
    4049         7039 :     if (mySpeed != possContinuation->mySpeed) {
    4050              :         reason = "speed";
    4051         1065 :         return false;
    4052              :     }
    4053              :     // the routingType
    4054         5974 :     if (myRoutingType != possContinuation->myRoutingType) {
    4055              :         reason = "routingType";
    4056            2 :         return false;
    4057              :     }
    4058              :     // spreadtype should match or it will look ugly
    4059         5972 :     if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
    4060              :         reason = "spreadType";
    4061           78 :         return false;
    4062              :     }
    4063              :     // matching lanes must have identical properties
    4064        13775 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4065         7966 :         if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
    4066            0 :             reason = "lane " + toString(i) + " speed";
    4067           85 :             return false;
    4068         7966 :         } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
    4069          116 :             reason = "lane " + toString(i) + " permissions";
    4070           58 :             return false;
    4071         7908 :         } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
    4072           14 :             reason = "lane " + toString(i) + " change restrictions";
    4073            7 :             return false;
    4074         7980 :         } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
    4075         8059 :                    fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
    4076           40 :             reason = "lane " + toString(i) + " width";
    4077           20 :             return false;
    4078              :         }
    4079              :     }
    4080              :     // if given identically osm names
    4081        15941 :     if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
    4082         5918 :             && ((myStreetName != "" && possContinuation->getStreetName() != "")
    4083              :                 // only permit merging a short unnamed road with a longer named road
    4084           21 :                 || (myStreetName != "" && myLength <= possContinuation->getLength())
    4085           16 :                 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
    4086              :         return false;
    4087              :     }
    4088              : 
    4089              :     return true;
    4090              : }
    4091              : 
    4092              : 
    4093              : void
    4094         5512 : NBEdge::append(NBEdge* e) {
    4095              :     // append geometry
    4096         5512 :     myGeom.append(e->myGeom);
    4097        12957 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4098         7445 :         myLanes[i].customShape.append(e->myLanes[i].customShape);
    4099         9540 :         if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
    4100        16985 :                 || OptionsCont::getOptions().getBool("output.original-names")) {
    4101        16062 :             const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
    4102        16062 :             const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
    4103         5354 :             if (origID != origID2) {
    4104         9756 :                 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
    4105              :             }
    4106              :         }
    4107         7445 :         myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
    4108         7445 :         myLanes[i].turnSigns = e->myLanes[i].turnSigns;
    4109              :     }
    4110         5512 :     if (e->getLength() > myLength) {
    4111              :         // possibly some lane attributes differ (when using option geometry.remove.min-length)
    4112              :         // make sure to use the attributes from the longer edge
    4113         6069 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4114         3401 :             myLanes[i].width = e->myLanes[i].width;
    4115              :         }
    4116              :         // defined name prevails over undefined name of shorter road
    4117         2668 :         if (myStreetName == "") {
    4118         1261 :             myStreetName = e->myStreetName;
    4119              :         }
    4120              :     }
    4121              :     // recompute length
    4122         5512 :     myLength += e->myLength;
    4123         5512 :     if (myLoadedLength > 0 || e->myLoadedLength > 0) {
    4124            1 :         myLoadedLength = getFinalLength() + e->getFinalLength();
    4125              :     }
    4126              :     // copy the connections and the building step if given
    4127         5512 :     myStep = e->myStep;
    4128         5512 :     myConnections = e->myConnections;
    4129         5512 :     myTurnDestination = e->myTurnDestination;
    4130         5512 :     myPossibleTurnDestination = e->myPossibleTurnDestination;
    4131         5512 :     myConnectionsToDelete = e->myConnectionsToDelete;
    4132         5512 :     updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
    4133              :     // set the node
    4134         5512 :     myTo = e->myTo;
    4135         5512 :     myTurnSignTarget = e->myTurnSignTarget;
    4136              :     myToBorder = e->myToBorder;
    4137        11024 :     mergeParameters(e->getParametersMap());
    4138              :     if (e->mySignalPosition != Position::INVALID) {
    4139         1169 :         mySignalPosition = e->mySignalPosition;
    4140              :     }
    4141         5512 :     computeAngle(); // myEndAngle may be different now
    4142         5512 : }
    4143              : 
    4144              : 
    4145              : void
    4146         5519 : NBEdge::updateRemovedNodes(const std::string& removed) {
    4147        11038 :     std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
    4148         5519 :     if (!result.empty() && !removed.empty()) {
    4149              :         result += " ";
    4150              :     }
    4151              :     result += removed;
    4152         5519 :     if (!result.empty()) {
    4153            9 :         setParameter(SUMO_PARAM_REMOVED_NODES, result);
    4154              :     }
    4155         5519 : }
    4156              : 
    4157              : 
    4158              : bool
    4159       921413 : NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
    4160      3867419 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    4161      3252157 :         if ((*i).toEdge == e && (*i).tlID != "") {
    4162              :             return true;
    4163              :         }
    4164              :     }
    4165              :     return false;
    4166              : }
    4167              : 
    4168              : 
    4169              : NBEdge*
    4170       845495 : NBEdge::getTurnDestination(bool possibleDestination) const {
    4171       845495 :     if (myTurnDestination == nullptr && possibleDestination) {
    4172        69208 :         return myPossibleTurnDestination;
    4173              :     }
    4174              :     return myTurnDestination;
    4175              : }
    4176              : 
    4177              : 
    4178              : std::string
    4179       299653 : NBEdge::getLaneID(int lane) const {
    4180       599306 :     return myID + "_" + toString(lane);
    4181              : }
    4182              : 
    4183              : 
    4184              : bool
    4185           65 : NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
    4186           65 :     std::vector<double> distances = myGeom.distances(e->getGeometry());
    4187              :     assert(distances.size() > 0);
    4188          130 :     return VectorHelper<double>::maxValue(distances) < threshold;
    4189           65 : }
    4190              : 
    4191              : 
    4192              : void
    4193           67 : NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
    4194              :     assert(index <= (int)myLanes.size());
    4195          201 :     myLanes.insert(myLanes.begin() + index, Lane(this, ""));
    4196              :     // copy attributes
    4197           67 :     if (myLanes.size() > 1) {
    4198           67 :         int templateIndex = index > 0 ? index - 1 : index + 1;
    4199           67 :         myLanes[index].speed = myLanes[templateIndex].speed;
    4200           67 :         myLanes[index].friction = myLanes[templateIndex].friction;
    4201           67 :         myLanes[index].permissions = myLanes[templateIndex].permissions;
    4202           67 :         myLanes[index].preferred = myLanes[templateIndex].preferred;
    4203           67 :         myLanes[index].endOffset = myLanes[templateIndex].endOffset;
    4204           67 :         myLanes[index].width = myLanes[templateIndex].width;
    4205           67 :         myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
    4206              :     }
    4207           67 :     const EdgeVector& incs = myFrom->getIncomingEdges();
    4208           67 :     if (recomputeShape) {
    4209           51 :         computeLaneShapes();
    4210              :     }
    4211           67 :     if (recomputeConnections) {
    4212          136 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    4213           85 :             (*i)->invalidateConnections(true);
    4214              :         }
    4215           51 :         invalidateConnections(true);
    4216           16 :     } else if (shiftIndices) {
    4217              :         // shift outgoing connections above the added lane to the left
    4218            0 :         for (Connection& c : myConnections) {
    4219            0 :             if (c.fromLane >= index) {
    4220            0 :                 c.fromLane += 1;
    4221              :             }
    4222              :         }
    4223              :         // shift incoming connections above the added lane to the left
    4224            0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4225            0 :             for (Connection& c : inc->myConnections) {
    4226            0 :                 if (c.toEdge == this && c.toLane >= index) {
    4227            0 :                     c.toLane += 1;
    4228              :                 }
    4229              :             }
    4230              :         }
    4231            0 :         myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
    4232            0 :         myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
    4233              :     }
    4234           67 : }
    4235              : 
    4236              : void
    4237           54 : NBEdge::incLaneNo(int by) {
    4238           54 :     int newLaneNo = (int)myLanes.size() + by;
    4239          121 :     while ((int)myLanes.size() < newLaneNo) {
    4240              :         // recompute shapes on last addition
    4241           67 :         const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4242           67 :         addLane((int)myLanes.size(), recompute, recompute, false);
    4243              :     }
    4244           54 : }
    4245              : 
    4246              : 
    4247              : void
    4248           68 : NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
    4249              :     assert(index < (int)myLanes.size());
    4250           68 :     myLanes.erase(myLanes.begin() + index);
    4251           68 :     if (recompute) {
    4252           16 :         computeLaneShapes();
    4253           16 :         const EdgeVector& incs = myFrom->getIncomingEdges();
    4254           17 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    4255            1 :             (*i)->invalidateConnections(true);
    4256              :         }
    4257           16 :         invalidateConnections(true);
    4258           52 :     } else if (shiftIndices) {
    4259           44 :         removeFromConnections(nullptr, index, -1, false, true);
    4260          120 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4261           76 :             inc->removeFromConnections(this, -1, index, false, true);
    4262              :         }
    4263              :     }
    4264           68 : }
    4265              : 
    4266              : 
    4267              : void
    4268           42 : NBEdge::decLaneNo(int by) {
    4269           42 :     int newLaneNo = (int) myLanes.size() - by;
    4270              :     assert(newLaneNo > 0);
    4271           66 :     while ((int)myLanes.size() > newLaneNo) {
    4272              :         // recompute shapes on last removal
    4273           24 :         const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4274           24 :         deleteLane((int)myLanes.size() - 1, recompute, false);
    4275              :     }
    4276           42 : }
    4277              : 
    4278              : 
    4279              : void
    4280         5522 : NBEdge::markAsInLane2LaneState() {
    4281              :     assert(myTo->getOutgoingEdges().size() == 0);
    4282         5522 :     myStep = EdgeBuildingStep::LANES2LANES_DONE;
    4283         5522 : }
    4284              : 
    4285              : 
    4286              : void
    4287         7879 : NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4288         7879 :     if (lane < 0) { // all lanes are meant...
    4289         7818 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4290         5510 :             allowVehicleClass(i, vclass);
    4291              :         }
    4292              :     } else {
    4293              :         assert(lane < (int)myLanes.size());
    4294         5571 :         myLanes[lane].permissions |= vclass;
    4295              :     }
    4296         7879 : }
    4297              : 
    4298              : 
    4299              : void
    4300        26160 : NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4301        26160 :     if (lane < 0) { // all lanes are meant...
    4302        26160 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4303        16129 :             disallowVehicleClass((int) i, vclass);
    4304              :         }
    4305              :     } else {
    4306              :         assert(lane < (int)myLanes.size());
    4307        16129 :         myLanes[lane].permissions &= ~vclass;
    4308              :     }
    4309        26160 : }
    4310              : 
    4311              : 
    4312              : void
    4313           68 : NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
    4314           68 :     if (lane < 0) { // all lanes are meant...
    4315            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4316            0 :             preferVehicleClass(i, vclasses);
    4317              :         }
    4318              :     } else {
    4319              :         assert(lane < (int)myLanes.size());
    4320           68 :         myLanes[lane].permissions |= vclasses;
    4321           68 :         myLanes[lane].preferred |= vclasses;
    4322              :     }
    4323           68 : }
    4324              : 
    4325              : 
    4326              : void
    4327        71811 : NBEdge::setLaneWidth(int lane, double width) {
    4328        71811 :     if (lane < 0) {
    4329              :         // all lanes are meant...
    4330         2581 :         myLaneWidth = width;
    4331         5183 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4332              :             // ... do it for each lane
    4333         2602 :             setLaneWidth(i, width);
    4334              :         }
    4335              :         return;
    4336              :     }
    4337              :     assert(lane < (int)myLanes.size());
    4338        69230 :     myLanes[lane].width = width;
    4339              : }
    4340              : 
    4341              : void
    4342         3968 : NBEdge::setLaneType(int lane, const std::string& type) {
    4343         3968 :     if (lane < 0) {
    4344            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4345              :             // ... do it for each lane
    4346            0 :             setLaneType(i, type);
    4347              :         }
    4348              :         return;
    4349              :     }
    4350              :     assert(lane < (int)myLanes.size());
    4351         3968 :     myLanes[lane].type = type;
    4352              : }
    4353              : 
    4354              : 
    4355              : double
    4356      3967332 : NBEdge::getLaneWidth(int lane) const {
    4357      3967332 :     return myLanes[lane].width != UNSPECIFIED_WIDTH
    4358      3967332 :            ? myLanes[lane].width
    4359      3292990 :            : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
    4360              : }
    4361              : 
    4362              : double
    4363       109499 : NBEdge::getInternalLaneWidth(
    4364              :     const NBNode& node,
    4365              :     const NBEdge::Connection& connection,
    4366              :     const NBEdge::Lane& successor,
    4367              :     bool isVia) const {
    4368              : 
    4369       109499 :     if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
    4370            3 :         return getLaneWidth(connection.fromLane);
    4371              :     }
    4372              : 
    4373       109496 :     return (isBikepath(getPermissions(connection.fromLane)) && (
    4374       109496 :                 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
    4375         1017 :            myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
    4376              : }
    4377              : 
    4378              : double
    4379       944918 : NBEdge::getTotalWidth() const {
    4380              :     double result = 0;
    4381      2174653 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4382      1229735 :         result += getLaneWidth(i);
    4383              :     }
    4384       944918 :     return result;
    4385              : }
    4386              : 
    4387              : double
    4388        48649 : NBEdge::getEndOffset(int lane) const {
    4389        48649 :     return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
    4390              : }
    4391              : 
    4392              : 
    4393              : const StopOffset&
    4394       615047 : NBEdge::getEdgeStopOffset() const {
    4395       615047 :     return myEdgeStopOffset;
    4396              : }
    4397              : 
    4398              : 
    4399              : const StopOffset&
    4400           24 : NBEdge::getLaneStopOffset(int lane) const {
    4401           24 :     if (lane == -1) {
    4402           12 :         return myEdgeStopOffset;
    4403              :     } else {
    4404           12 :         return myLanes[lane].laneStopOffset;
    4405              :     }
    4406              : }
    4407              : 
    4408              : 
    4409              : void
    4410        61095 : NBEdge::setEndOffset(int lane, double offset) {
    4411        61095 :     if (lane < 0) {
    4412              :         // all lanes are meant...
    4413            3 :         myEndOffset = offset;
    4414            8 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4415              :             // ... do it for each lane
    4416            5 :             setEndOffset(i, offset);
    4417              :         }
    4418              :         return;
    4419              :     }
    4420              :     assert(lane < (int)myLanes.size());
    4421        61092 :     myLanes[lane].endOffset = offset;
    4422              : }
    4423              : 
    4424              : 
    4425              : bool
    4426       109518 : NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
    4427       109518 :     if (lane < 0) {
    4428        48443 :         if (!overwrite && myEdgeStopOffset.isDefined()) {
    4429              :             return false;
    4430              :         }
    4431              :         // all lanes are meant...
    4432        48439 :         if (offset.getOffset() < 0) {
    4433              :             // Edge length unknown at parsing time, thus check here.
    4434            3 :             WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
    4435            1 :             return false;
    4436              :         } else {
    4437        48438 :             myEdgeStopOffset = offset;
    4438              :         }
    4439        61075 :     } else if (lane < (int)myLanes.size()) {
    4440        61075 :         if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
    4441        61069 :             if (offset.getOffset() < 0) {
    4442              :                 // Edge length unknown at parsing time, thus check here.
    4443            0 :                 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
    4444              :             } else {
    4445        61069 :                 myLanes[lane].laneStopOffset = offset;
    4446              :             }
    4447              :         }
    4448              :     } else {
    4449            0 :         WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
    4450              :     }
    4451              :     return true;
    4452              : }
    4453              : 
    4454              : 
    4455              : void
    4456        65667 : NBEdge::setSpeed(int lane, double speed) {
    4457        65667 :     if (lane < 0) {
    4458              :         // all lanes are meant...
    4459          347 :         mySpeed = speed;
    4460         1094 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4461              :             // ... do it for each lane
    4462          747 :             setSpeed(i, speed);
    4463              :         }
    4464              :         return;
    4465              :     }
    4466              :     assert(lane < (int)myLanes.size());
    4467        65320 :     myLanes[lane].speed = speed;
    4468              : }
    4469              : 
    4470              : 
    4471              : void
    4472        65756 : NBEdge::setFriction(int lane, double friction) {
    4473        65756 :     if (lane < 0) {
    4474              :         // all lanes are meant...
    4475          787 :         myFriction = friction;
    4476         2259 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4477              :             // ... do it for each lane
    4478         1472 :             setFriction(i, friction);
    4479              :         }
    4480              :         return;
    4481              :     }
    4482              :     assert(lane < (int)myLanes.size());
    4483        64969 :     myLanes[lane].friction = friction;
    4484              : }
    4485              : 
    4486              : 
    4487              : void
    4488        59652 : NBEdge::setAcceleration(int lane, bool accelRamp) {
    4489              :     assert(lane >= 0);
    4490              :     assert(lane < (int)myLanes.size());
    4491        59652 :     myLanes[lane].accelRamp = accelRamp;
    4492        59652 : }
    4493              : 
    4494              : 
    4495              : void
    4496           77 : NBEdge::setLaneShape(int lane, const PositionVector& shape) {
    4497              :     assert(lane >= 0);
    4498              :     assert(lane < (int)myLanes.size());
    4499           77 :     myLanes[lane].customShape = shape;
    4500           77 : }
    4501              : 
    4502              : 
    4503              : void
    4504       257503 : NBEdge::setPermissions(SVCPermissions permissions, int lane) {
    4505       257503 :     if (lane < 0) {
    4506       186083 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4507              :             // ... do it for each lane
    4508       102970 :             setPermissions(permissions, i);
    4509              :         }
    4510              :     } else {
    4511              :         assert(lane < (int)myLanes.size());
    4512       174390 :         myLanes[lane].permissions = permissions;
    4513              :     }
    4514       257503 : }
    4515              : 
    4516              : 
    4517              : void
    4518            0 : NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
    4519            0 :     if (lane < 0) {
    4520            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4521              :             // ... do it for each lane
    4522            0 :             setPreferredVehicleClass(permissions, i);
    4523              :         }
    4524              :     } else {
    4525              :         assert(lane < (int)myLanes.size());
    4526            0 :         myLanes[lane].preferred = permissions;
    4527              :     }
    4528            0 : }
    4529              : 
    4530              : 
    4531              : void
    4532        59898 : NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
    4533              :     assert(lane >= 0);
    4534              :     assert(lane < (int)myLanes.size());
    4535        59898 :     myLanes[lane].changeLeft = changeLeft;
    4536        59898 :     myLanes[lane].changeRight = changeRight;
    4537        59898 : }
    4538              : 
    4539              : 
    4540              : SVCPermissions
    4541     85136961 : NBEdge::getPermissions(int lane) const {
    4542     85136961 :     if (lane < 0) {
    4543              :         SVCPermissions result = 0;
    4544     76881154 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4545     45803484 :             result |= getPermissions(i);
    4546              :         }
    4547     31077670 :         return result;
    4548              :     } else {
    4549              :         assert(lane < (int)myLanes.size());
    4550     54059291 :         return myLanes[lane].permissions;
    4551              :     }
    4552              : }
    4553              : 
    4554              : 
    4555              : void
    4556        67894 : NBEdge::setLoadedLength(double val) {
    4557        67894 :     myLoadedLength = val;
    4558        67894 : }
    4559              : 
    4560              : void
    4561           10 : NBEdge::setAverageLengthWithOpposite(double val) {
    4562           10 :     myLength = val;
    4563           10 : }
    4564              : 
    4565              : 
    4566              : void
    4567          200 : NBEdge::dismissVehicleClassInformation() {
    4568          647 :     for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    4569          447 :         (*i).permissions = SVCAll;
    4570          447 :         (*i).preferred = 0;
    4571              :     }
    4572          200 : }
    4573              : 
    4574              : 
    4575              : bool
    4576       373943 : NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
    4577       373943 :     if (c1.fromLane != c2.fromLane) {
    4578        89804 :         return c1.fromLane < c2.fromLane;
    4579              :     }
    4580       284139 :     if (c1.toEdge != c2.toEdge) {
    4581              :         return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
    4582              :     }
    4583         5970 :     return c1.toLane < c2.toLane;
    4584              : }
    4585              : 
    4586              : 
    4587              : double
    4588         9848 : NBEdge::getSignalOffset() const {
    4589              :     if (mySignalPosition == Position::INVALID) {
    4590              :         return UNSPECIFIED_SIGNAL_OFFSET;
    4591              :     } else {
    4592          689 :         Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
    4593          689 :                            myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
    4594              :         //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
    4595              :         return mySignalPosition.distanceTo2D(laneEnd);
    4596              :     }
    4597              : }
    4598              : 
    4599              : 
    4600              : int
    4601        30371 : NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
    4602              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4603        30371 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4604        30371 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4605        38950 :     for (int i = start; i != end; i += direction) {
    4606              :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4607              :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4608        26628 :         if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
    4609        38940 :                 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
    4610        24926 :             return i;
    4611              :         }
    4612              :     }
    4613              :     return -1;
    4614              : }
    4615              : 
    4616              : int
    4617        53426 : NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
    4618              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4619        53426 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4620        53426 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4621        69174 :     for (int i = start; i != end; i += direction) {
    4622              :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4623              :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4624        55320 :         SVCPermissions p = myLanes[i].permissions;
    4625        55320 :         if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
    4626        15748 :                 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
    4627        39572 :             return i;
    4628              :         }
    4629              :     }
    4630              :     return -1;
    4631              : }
    4632              : 
    4633              : int
    4634       669027 : NBEdge::getSpecialLane(SVCPermissions permissions) const {
    4635      1507314 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4636       841464 :         if (myLanes[i].permissions == permissions) {
    4637         3177 :             return i;
    4638              :         }
    4639              :     }
    4640              :     return -1;
    4641              : }
    4642              : 
    4643              : int
    4644      1190968 : NBEdge::getFirstAllowedLaneIndex(int direction) const {
    4645              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4646      1190968 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4647      1190968 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4648      1191392 :     for (int i = start; i != end; i += direction) {
    4649      1191203 :         if (myLanes[i].permissions != 0) {
    4650      1190779 :             return i;
    4651              :         }
    4652              :     }
    4653          189 :     return end - direction;
    4654              : }
    4655              : 
    4656              : 
    4657              : std::set<SVCPermissions>
    4658         4141 : NBEdge::getPermissionVariants(int iStart, int iEnd) const {
    4659              :     std::set<SVCPermissions> result;
    4660         4141 :     if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
    4661            0 :         throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
    4662              :     }
    4663         9268 :     for (int i = iStart; i < iEnd; ++i) {
    4664         5127 :         result.insert(getPermissions(i));
    4665              :     }
    4666         4141 :     return result;
    4667              : }
    4668              : 
    4669              : int
    4670      7194477 : NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
    4671              :     int result = 0;
    4672     18174438 :     for (const Lane& lane : myLanes) {
    4673     10979961 :         if ((allPermissions && (lane.permissions & permissions) == permissions)
    4674       485685 :                 || (!allPermissions && (lane.permissions & permissions) != 0)) {
    4675      9734259 :             result++;
    4676              :         }
    4677              :     }
    4678      7194477 :     return result;
    4679              : }
    4680              : 
    4681              : bool
    4682            0 : NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
    4683              :     assert(lane >= 0 && lane < getNumLanes());
    4684            0 :     return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
    4685              : }
    4686              : 
    4687              : bool
    4688            0 : NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
    4689              :     assert(lane >= 0 && lane < getNumLanes());
    4690            0 :     return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
    4691              : }
    4692              : 
    4693              : double
    4694         5769 : NBEdge::getCrossingAngle(NBNode* node) {
    4695         5769 :     double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
    4696         5769 :     if (angle < 0) {
    4697         1481 :         angle += 360.0;
    4698              :     }
    4699         5769 :     if (angle >= 360) {
    4700            0 :         angle -= 360.0;
    4701              :     }
    4702         5769 :     if (gDebugFlag1) {
    4703            0 :         std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
    4704              :     }
    4705         5769 :     return angle;
    4706              : }
    4707              : 
    4708              : 
    4709              : NBEdge::Lane
    4710            0 : NBEdge::getFirstNonPedestrianLane(int direction) const {
    4711            0 :     int index = getFirstNonPedestrianLaneIndex(direction);
    4712            0 :     if (index < 0) {
    4713            0 :         throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
    4714              :     }
    4715            0 :     return myLanes[index];
    4716              : }
    4717              : 
    4718              : std::string
    4719        11125 : NBEdge::getSidewalkID() {
    4720              :     // see IntermodalEdge::getSidewalk()
    4721        13635 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4722        11178 :         if (myLanes[i].permissions == SVC_PEDESTRIAN) {
    4723         8668 :             return getLaneID(i);
    4724              :         }
    4725              :     }
    4726         2457 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4727         2457 :         if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
    4728         2457 :             return getLaneID(i);
    4729              :         }
    4730              :     }
    4731            0 :     return getLaneID(0);
    4732              : }
    4733              : 
    4734              : void
    4735         3290 : NBEdge::addSidewalk(double width) {
    4736         3290 :     addRestrictedLane(width, SVC_PEDESTRIAN);
    4737         3290 : }
    4738              : 
    4739              : 
    4740              : void
    4741            0 : NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4742            0 :     restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
    4743            0 : }
    4744              : 
    4745              : 
    4746              : void
    4747          591 : NBEdge::addBikeLane(double width) {
    4748          591 :     addRestrictedLane(width, SVC_BICYCLE);
    4749          591 : }
    4750              : 
    4751              : 
    4752              : void
    4753            0 : NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4754            0 :     restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
    4755            0 : }
    4756              : 
    4757              : bool
    4758         4981 : NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
    4759        12480 :     for (const Lane& lane : myLanes) {
    4760         7716 :         if (lane.permissions == vclass) {
    4761              :             return true;
    4762              :         }
    4763              :     }
    4764              :     return false;
    4765              : }
    4766              : 
    4767              : 
    4768              : void
    4769         4292 : NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
    4770         4292 :     if (hasRestrictedLane(vclass)) {
    4771           12 :         WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
    4772            4 :         return;
    4773              :     }
    4774         4288 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    4775         1201 :         myGeom.move2side(width / 2);
    4776              :     }
    4777              :     // disallow the designated vclass on all "old" lanes
    4778         4288 :     disallowVehicleClass(-1, vclass);
    4779              :     // don't create a restricted vehicle lane to the right of a sidewalk
    4780         4288 :     const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
    4781              :     if (newIndex == 0) {
    4782              :         // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
    4783         4247 :         disallowVehicleClass(-1, SVC_PEDESTRIAN);
    4784              :     }
    4785              :     // add new lane
    4786        12864 :     myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
    4787         4288 :     myLanes[newIndex].permissions = vclass;
    4788         4288 :     myLanes[newIndex].width = fabs(width);
    4789              :     // shift outgoing connections to the left
    4790         4315 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4791              :         Connection& c = *it;
    4792           27 :         if (c.fromLane >= newIndex) {
    4793           27 :             c.fromLane += 1;
    4794              :         }
    4795              :     }
    4796              :     // shift incoming connections to the left
    4797         4288 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4798         9422 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4799         5134 :         (*it)->shiftToLanesToEdge(this, 1);
    4800              :     }
    4801         4288 :     myFrom->shiftTLConnectionLaneIndex(this, 1);
    4802         4288 :     myTo->shiftTLConnectionLaneIndex(this, 1);
    4803         4288 :     computeLaneShapes();
    4804              : }
    4805              : 
    4806              : 
    4807              : void
    4808            0 : NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4809              :     // check that previously lane was transformed
    4810            0 :     if (myLanes[0].permissions != vclass) {
    4811            0 :         WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
    4812            0 :         return;
    4813              :     }
    4814              :     // restore old values
    4815              :     myGeom = oldGeometry;
    4816            0 :     myLanes = oldLanes;
    4817            0 :     myConnections = oldConnections;
    4818              :     // shift incoming connections to the right
    4819            0 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4820            0 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4821            0 :         (*it)->shiftToLanesToEdge(this, 0);
    4822              :     }
    4823              :     // Shift TL conections
    4824            0 :     myFrom->shiftTLConnectionLaneIndex(this, 0);
    4825            0 :     myTo->shiftTLConnectionLaneIndex(this, 0);
    4826            0 :     computeLaneShapes();
    4827              : }
    4828              : 
    4829              : 
    4830              : void
    4831         5134 : NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
    4832              :     /// XXX could we repurpose the function replaceInConnections ?
    4833         5239 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4834          105 :         if ((*it).toEdge == to && (*it).toLane >= 0) {
    4835           27 :             (*it).toLane += laneOff;
    4836              :         }
    4837              :     }
    4838         5134 : }
    4839              : 
    4840              : 
    4841              : bool
    4842        67812 : NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
    4843        67812 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
    4844        17357 :             && !isRailway(getPermissions())
    4845        10743 :             && !isRailway(other->getPermissions())
    4846        78525 :             && getBidiEdge() == nullptr) {
    4847        10697 :         const int i = (node == myTo ? -1 : 0);
    4848        10697 :         const int i2 = (node == myTo ? 0 : -1);
    4849        10697 :         const double dist = myGeom[i].distanceTo2D(node->getPosition());
    4850        10697 :         const double neededOffset = getTotalWidth() / 2;
    4851        10697 :         const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
    4852        10697 :                                   other->getGeometry().distance2D(myGeom[i]));
    4853        10697 :         const double neededOffset2 = neededOffset + (other->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
    4854        10697 :                 ? (other->getTotalWidth()) / 2 : 0);
    4855        10697 :         const double missing = neededOffset - dist;
    4856        10697 :         const double missing2 = neededOffset2 - dist2;
    4857              :         double shift = 0;
    4858        10697 :         if (missing > 0 && missing2 > 0) {
    4859              :             shift = MIN2(missing, missing2);
    4860         2028 :         } else if (missing2) {
    4861              :             shift = missing2;
    4862              :         }
    4863        10697 :         if (shift > 0) {
    4864              :             PositionVector tmp = myGeom;
    4865              :             try {
    4866         8772 :                 tmp.move2side(shift);
    4867         8772 :                 tmp[i].round(gPrecision);
    4868         8772 :                 myGeom[i] = tmp[i];
    4869         8772 :                 computeAngle();
    4870              :                 return true;
    4871              :                 //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << "  by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
    4872            0 :             } catch (InvalidArgument&) {
    4873            0 :                 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
    4874            0 :             }
    4875         8772 :         }
    4876              :     }
    4877              :     return false;
    4878              : }
    4879              : 
    4880              : 
    4881              : Position
    4882           88 : NBEdge::geometryPositionAtOffset(double offset) const {
    4883           88 :     if (myLoadedLength > 0) {
    4884            2 :         return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
    4885              :     } else {
    4886           86 :         return myGeom.positionAtOffset(offset);
    4887              :     }
    4888              : }
    4889              : 
    4890              : 
    4891              : double
    4892       125866 : NBEdge::getFinalLength() const {
    4893              :     double result = getLoadedLength();
    4894       251732 :     if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
    4895              :         // use length to junction center even if a modified geometry was given
    4896        66315 :         PositionVector geom = cutAtIntersection(myGeom);
    4897        66315 :         geom.push_back_noDoublePos(getToNode()->getCenter());
    4898        66315 :         geom.push_front_noDoublePos(getFromNode()->getCenter());
    4899        66315 :         result = geom.length();
    4900        66315 :     }
    4901              :     double avgEndOffset = 0;
    4902       285406 :     for (const Lane& lane : myLanes) {
    4903       159540 :         avgEndOffset += lane.endOffset;
    4904              :     }
    4905       125866 :     if (isBidiRail()) {
    4906        13420 :         avgEndOffset += myPossibleTurnDestination->getEndOffset();
    4907              :     }
    4908       125866 :     avgEndOffset /= (double)myLanes.size();
    4909       125870 :     return MAX2(result - avgEndOffset, POSITION_EPS);
    4910              : }
    4911              : 
    4912              : 
    4913              : void
    4914        23655 : NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
    4915        23655 :     if (laneIdx == -1) {
    4916        23598 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4917        26344 :             setOrigID(origID, append, i);
    4918              :         }
    4919              :     } else {
    4920        13229 :         if (origID != "") {
    4921        13229 :             if (append) {
    4922          114 :                 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
    4923           57 :                 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
    4924           57 :                     oldIDs.push_back(origID);
    4925              :                 }
    4926           57 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
    4927           57 :             } else {
    4928        13172 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
    4929              :             }
    4930              :         } else {
    4931              :             // do not record empty origID parameter
    4932            0 :             myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
    4933              :         }
    4934              :     }
    4935        23655 : }
    4936              : 
    4937              : 
    4938              : const EdgeVector&
    4939          570 : NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
    4940              :     // @todo cache successors instead of recomputing them every time
    4941              :     mySuccessors.clear();
    4942              :     //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
    4943         3022 :     for (const Connection& con : myConnections) {
    4944         2414 :         if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
    4945          159 :                 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
    4946          159 :                                             & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
    4947         4866 :                 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
    4948         1642 :             mySuccessors.push_back(con.toEdge);
    4949              :             //std::cout << "   succ=" << con.toEdge->getID() << "\n";
    4950              :         }
    4951              :     }
    4952          570 :     return mySuccessors;
    4953              : }
    4954              : 
    4955              : 
    4956              : const ConstRouterEdgePairVector&
    4957         4283 : NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
    4958              :     // @todo cache successors instead of recomputing them every time
    4959              :     myViaSuccessors.clear();
    4960         9297 :     for (const Connection& con : myConnections) {
    4961              :         std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
    4962              :         // special case for Persons in Netedit
    4963         5014 :         if (vClass == SVC_PEDESTRIAN) {
    4964            0 :             myViaSuccessors.push_back(pair);    // Pedestrians have complete freedom of movement in all sucessors
    4965         5014 :         } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
    4966         5014 :                    (con.toEdge != nullptr) &&
    4967         5014 :                    ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
    4968              :             // ignore duplicates
    4969         4864 :             if (con.getLength() > 0) {
    4970              :                 pair.second = &con;
    4971              :             }
    4972         9728 :             myViaSuccessors.push_back(pair);
    4973              :         }
    4974              :     }
    4975         4283 :     return myViaSuccessors;
    4976              : }
    4977              : 
    4978              : 
    4979              : void
    4980            0 : NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
    4981            0 :     if (outgoing) {
    4982            0 :         for (const Connection& c : myConnections) {
    4983            0 :             std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4984              :         }
    4985              :     }
    4986            0 :     if (incoming) {
    4987            0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4988            0 :             for (Connection& c : inc->myConnections) {
    4989            0 :                 if (c.toEdge == this) {
    4990            0 :                     std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4991              :                 }
    4992              :             }
    4993              :         }
    4994              :     }
    4995            0 : }
    4996              : 
    4997              : 
    4998              : int
    4999           93 : NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
    5000          186 :     return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
    5001              : }
    5002              : 
    5003              : bool
    5004           36 : NBEdge::joinLanes(SVCPermissions perms) {
    5005              :     bool haveJoined = false;
    5006              :     int i = 0;
    5007          181 :     while (i < getNumLanes() - 1) {
    5008          145 :         if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
    5009           37 :             const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
    5010           37 :             const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
    5011           37 :             deleteLane(i, false, true);
    5012           37 :             setLaneWidth(i, newWidth);
    5013           37 :             setLaneType(i, newType);
    5014              :             haveJoined = true;
    5015              :         } else {
    5016          108 :             i++;
    5017              :         }
    5018              :     }
    5019           36 :     return haveJoined;
    5020              : }
    5021              : 
    5022              : 
    5023              : EdgeVector
    5024        62204 : NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
    5025              :     EdgeVector result;
    5026       267686 :     for (NBEdge* edge : edges) {
    5027       205482 :         if ((edge->getPermissions() & permissions) != 0) {
    5028       190337 :             result.push_back(edge);
    5029              :         }
    5030              :     }
    5031        62204 :     return result;
    5032            0 : }
    5033              : 
    5034              : NBEdge*
    5035         2916 : NBEdge::getStraightContinuation(SVCPermissions permissions) const {
    5036         2916 :     EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
    5037         2916 :     if (cands.size() == 0) {
    5038              :         return nullptr;
    5039              :     }
    5040         2912 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
    5041         2912 :     NBEdge* best = cands.front();
    5042         2912 :     if (isTurningDirectionAt(best)) {
    5043              :         return nullptr;
    5044              :     } else {
    5045              :         return best;
    5046              :     }
    5047         2916 : }
    5048              : 
    5049              : NBEdge*
    5050          186 : NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
    5051          186 :     EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
    5052          186 :     if (cands.size() == 0) {
    5053              :         return nullptr;
    5054              :     }
    5055          174 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
    5056          174 :     NBEdge* best = cands.front();
    5057          174 :     if (best->isTurningDirectionAt(this)) {
    5058              :         return nullptr;
    5059              :     } else {
    5060              :         return best;
    5061              :     }
    5062          186 : }
    5063              : 
    5064              : 
    5065              : NBEdge*
    5066           47 : NBEdge::guessOpposite(bool reguess) {
    5067              :     NBEdge* opposite = nullptr;
    5068           47 :     if (getNumLanes() > 0) {
    5069              :         NBEdge::Lane& lastLane = myLanes.back();
    5070           47 :         const double lastWidth = getLaneWidth(getNumLanes() - 1);
    5071           47 :         if (lastLane.oppositeID == "" || reguess) {
    5072           76 :             for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
    5073           44 :                 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
    5074              :                     const NBEdge::Lane& candLastLane = cand->getLanes().back();
    5075           48 :                     if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
    5076           30 :                         const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
    5077              :                         // in sharp corners, the difference may be higher
    5078              :                         // factor (sqrt(2) for 90 degree corners
    5079           30 :                         const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
    5080           60 :                         const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
    5081              :                         //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
    5082           30 :                         if (distance < threshold) {
    5083              :                             opposite = cand;
    5084              :                         }
    5085              :                     }
    5086              :                 }
    5087              :             }
    5088           32 :             if (opposite != nullptr) {
    5089           56 :                 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
    5090              :             }
    5091              :         }
    5092              :     }
    5093           47 :     return opposite;
    5094              : }
    5095              : 
    5096              : double
    5097            0 : NBEdge::getDistancAt(double pos) const {
    5098              :     // negative values of myDistances indicate descending kilometrage
    5099            0 :     return fabs(myDistance + pos);
    5100              : }
    5101              : 
    5102              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1