LCOV - code coverage report
Current view: top level - src/netbuild - NBEdge.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.3 % 2313 2136
Test Date: 2026-07-26 16:30:20 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       572200 : NBEdge::Connection::getInternalLaneID() const {
      93      1144400 :     return id + "_" + toString(internalLaneIndex);
      94              : }
      95              : 
      96              : 
      97              : std::string
      98        86470 : NBEdge::Connection::getInternalViaLaneID() const {
      99       172940 :     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       866032 : NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
     111       866032 :     fromLane(fromLane_),
     112       866032 :     toEdge(toEdge_),
     113       866032 :     toLane(toLane_),
     114       866032 :     mayDefinitelyPass(mayDefinitelyPass_),
     115       866032 :     customLength(myDefaultConnectionLength),
     116      1731959 :     id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
     117       866032 : }
     118              : 
     119              : 
     120       196500 : NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
     121       196500 :     speed(e->getSpeed()),
     122       196500 :     friction(e->getFriction()),
     123       196500 :     permissions(SVCAll),
     124       196500 :     preferred(0),
     125       196500 :     changeLeft(SVCAll),
     126       196500 :     changeRight(SVCAll),
     127       196500 :     endOffset(e->getEndOffset()),
     128       196500 :     laneStopOffset(e->getEdgeStopOffset()),
     129       196500 :     width(e->getLaneWidth()),
     130       196500 :     accelRamp(false),
     131       393000 :     connectionsDone(false) {
     132       196500 :     if (origID_ != "") {
     133        33993 :         setParameter(SUMO_PARAM_ORIGID, origID_);
     134              :     }
     135       196500 : }
     136              : 
     137              : 
     138              : /* -------------------------------------------------------------------------
     139              :  * NBEdge::ToEdgeConnectionsAdder-methods
     140              :  * ----------------------------------------------------------------------- */
     141              : void
     142       429618 : NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
     143              :     // check
     144              :     assert((int)myTransitions.size() > virtEdge);
     145              :     // get the approached edge
     146       429618 :     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       429618 :     if (i != myConnections.end()) {
     153              :         // if there were already lanes assigned, get them
     154       338762 :         lanes = (*i).second;
     155              :     }
     156              : 
     157              :     // check whether the current lane was already used to connect the currently
     158              :     //  regarded approached edge
     159       429618 :     std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
     160       429618 :     if (j == lanes.end()) {
     161              :         // if not, add it to the list
     162       101610 :         lanes.push_back(lane);
     163              :     }
     164              :     // set information about connecting lanes
     165       429618 :     myConnections[succEdge] = lanes;
     166       429618 : }
     167              : 
     168              : 
     169              : 
     170              : /* -------------------------------------------------------------------------
     171              :  * NBEdge::MainDirections-methods
     172              :  * ----------------------------------------------------------------------- */
     173        46449 : 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        46449 :     const NBEdge* straight = nullptr;
     176       137366 :     for (const NBEdge* const out : outgoing) {
     177        90917 :         const SVCPermissions outPerms = out->getPermissions();
     178        91099 :         for (const int l : availableLanes) {
     179        90984 :             if ((parent->myLanes[l].permissions & outPerms) != 0) {
     180        90802 :                 if (straight == nullptr || sorter(out, straight)) {
     181        68564 :                     straight = out;
     182              :                 }
     183              :                 break;
     184              :             }
     185              :         }
     186              :     }
     187        46449 :     if (straight == nullptr) {
     188         4097 :         return;
     189              :     }
     190        46400 :     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        46400 :     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        46400 :     if (NBNode::isTrafficLight(to->getType()) &&
     201         4350 :             (straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
     202         4048 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     203         4048 :         return;
     204              :     }
     205        42352 :     if (outgoing[0]->getJunctionPriority(to) == 1) {
     206        22753 :         myDirs.push_back(MainDirections::Direction::RIGHTMOST);
     207              :     }
     208              :     // check whether the left turn has a higher priority
     209        42352 :     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        22980 :         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        42352 :     if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
     221        20564 :         myDirs.push_back(MainDirections::Direction::FORWARD);
     222              :     }
     223            0 : }
     224              : 
     225              : 
     226        46449 : NBEdge::MainDirections::~MainDirections() {}
     227              : 
     228              : 
     229              : bool
     230        46400 : NBEdge::MainDirections::empty() const {
     231        46400 :     return myDirs.empty();
     232              : }
     233              : 
     234              : 
     235              : bool
     236       173676 : NBEdge::MainDirections::includes(Direction d) const {
     237       173676 :     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       228195 : NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
     246       228195 :     if (c1.toEdge != c2.toEdge) {
     247       180964 :         return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
     248              :     }
     249        47231 :     return c1.toLane < c2.toLane;
     250              : }
     251              : 
     252              : 
     253              : /* -------------------------------------------------------------------------
     254              :  * NBEdge-methods
     255              :  * ----------------------------------------------------------------------- */
     256        15692 : 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        15692 :                LaneSpreadFunction spread, const std::string& streetName) :
     260        15692 :     Named(StringUtils::convertUmlaute(id)),
     261        15692 :     myStep(EdgeBuildingStep::INIT),
     262        15692 :     myType(StringUtils::convertUmlaute(type)),
     263        15692 :     myFrom(from), myTo(to),
     264        15692 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     265        15692 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     266        15692 :     myDistance(0),
     267        15692 :     myTurnDestination(nullptr),
     268        15692 :     myPossibleTurnDestination(nullptr),
     269        15692 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     270        15692 :     myLaneSpreadFunction(spread), myEndOffset(endOffset),
     271        15692 :     myLaneWidth(laneWidth),
     272        15692 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     273        15692 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     274        15692 :     myStreetName(streetName),
     275        15692 :     mySignalPosition(Position::INVALID),
     276        15692 :     mySignalNode(nullptr),
     277        15692 :     myIsOffRamp(false),
     278        15692 :     myIsBidi(false),
     279        47076 :     myIndex(-1) {
     280        15692 :     init(nolanes, false, "");
     281        15692 : }
     282              : 
     283              : 
     284       133414 : 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       133414 :                bool tryIgnoreNodePositions) :
     292       133414 :     Named(StringUtils::convertUmlaute(id)),
     293       133414 :     myStep(EdgeBuildingStep::INIT),
     294       133415 :     myType(StringUtils::convertUmlaute(type)),
     295       133414 :     myFrom(from), myTo(to),
     296       133414 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     297       133414 :     myPriority(priority), mySpeed(speed), myFriction(friction),
     298       133414 :     myDistance(0),
     299       133414 :     myTurnDestination(nullptr),
     300       133414 :     myPossibleTurnDestination(nullptr),
     301       133414 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     302       133414 :     myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
     303       133414 :     myLaneWidth(laneWidth),
     304       133414 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     305       133414 :     myAmInTLS(false), myAmMacroscopicConnector(false),
     306       133414 :     myStreetName(streetName),
     307       133414 :     mySignalPosition(Position::INVALID),
     308       133414 :     mySignalNode(nullptr),
     309       133414 :     myIsOffRamp(false),
     310       133414 :     myIsBidi(false),
     311       400242 :     myIndex(-1) {
     312       133414 :     init(nolanes, tryIgnoreNodePositions, origID);
     313       133426 : }
     314              : 
     315              : 
     316         2716 : NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
     317         2716 :     Named(StringUtils::convertUmlaute(id)),
     318         2716 :     myStep(EdgeBuildingStep::INIT),
     319         2716 :     myType(tpl->getTypeID()),
     320         2716 :     myFrom(from), myTo(to),
     321         2716 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     322         2716 :     myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
     323         2716 :     myFriction(tpl->getFriction()),
     324         2716 :     myDistance(0),
     325         2716 :     myTurnDestination(nullptr),
     326         2716 :     myPossibleTurnDestination(nullptr),
     327         2716 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     328              :     myGeom(geom),
     329         2716 :     myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
     330         2716 :     myEndOffset(tpl->getEndOffset()),
     331         2716 :     myEdgeStopOffset(tpl->getEdgeStopOffset()),
     332         2716 :     myLaneWidth(tpl->getLaneWidth()),
     333         2716 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     334         2716 :     myAmInTLS(false),
     335         2716 :     myAmMacroscopicConnector(false),
     336         2716 :     myStreetName(tpl->getStreetName()),
     337         2716 :     mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
     338         2716 :     mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
     339         2716 :     myIsOffRamp(false),
     340         2716 :     myIsBidi(tpl->myIsBidi),
     341         5432 :     myIndex(-1) {
     342         3854 :     init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
     343         6643 :     for (int i = 0; i < getNumLanes(); i++) {
     344         3927 :         const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
     345         3927 :         setSpeed(i, tpl->getLaneSpeed(tplIndex));
     346         3927 :         setFriction(i, tpl->getLaneFriction(tplIndex));
     347         3927 :         setPermissions(tpl->getPermissions(tplIndex), i);
     348         3927 :         setLaneWidth(i, tpl->myLanes[tplIndex].width);
     349         3927 :         setLaneType(i, tpl->myLanes[tplIndex].type);
     350         3927 :         myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
     351         3927 :         if (to == tpl->myTo) {
     352         1474 :             setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
     353         1474 :             setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
     354              :         }
     355              :     }
     356         2716 :     if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
     357            3 :         myLoadedLength = tpl->myLoadedLength;
     358              :     }
     359         2716 :     updateParameters(tpl->getParametersMap());
     360         2716 : }
     361              : 
     362              : 
     363         2583 : NBEdge::NBEdge() :
     364              :     Named("DUMMY"),
     365         2583 :     myStep(EdgeBuildingStep::INIT),
     366         2583 :     myFrom(nullptr), myTo(nullptr),
     367         2583 :     myStartAngle(0), myEndAngle(0), myTotalAngle(0),
     368         2583 :     myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
     369         2583 :     myDistance(0),
     370         2583 :     myTurnDestination(nullptr),
     371         2583 :     myPossibleTurnDestination(nullptr),
     372         2583 :     myFromJunctionPriority(-1), myToJunctionPriority(-1),
     373         2583 :     myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
     374         2583 :     myEndOffset(0),
     375         2583 :     myEdgeStopOffset(StopOffset()),
     376         2583 :     myLaneWidth(0),
     377         2583 :     myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
     378         2583 :     myAmInTLS(false),
     379         2583 :     myAmMacroscopicConnector(false),
     380         2583 :     mySignalPosition(Position::INVALID),
     381         7749 :     mySignalNode(nullptr) {
     382         2583 : }
     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         6358 : NBEdge::reinitNodes(NBNode* from, NBNode* to) {
     438              :     // connections may still be valid
     439         6358 :     if (from == nullptr || to == nullptr) {
     440            0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     441              :     }
     442         6358 :     if (myFrom != from) {
     443         3245 :         myFrom->removeEdge(this, false);
     444              :     }
     445         6358 :     if (myTo != to) {
     446         3071 :         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         6358 :     if (myFrom != from) {
     451         3245 :         myFrom = from;
     452         3245 :         myFrom->addOutgoingEdge(this);
     453              :     }
     454         6358 :     if (myTo != to) {
     455         3071 :         myTo = to;
     456         3071 :         myTo->addIncomingEdge(this);
     457              :     }
     458         6358 :     computeAngle();
     459         6358 : }
     460              : 
     461              : 
     462              : void
     463       154073 : NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
     464       154073 :     if (noLanes == 0) {
     465            0 :         throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
     466              :     }
     467       154073 :     if (myFrom == nullptr || myTo == nullptr) {
     468            0 :         throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
     469              :     }
     470       154073 :     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       154072 :     if (myFrom->getID() < myTo->getID()) {
     479        79407 :         PositionVector reverse = myGeom.reverse();
     480        79407 :         reverse.removeDoublePoints(POSITION_EPS, true);
     481       158814 :         myGeom = reverse.reverse();
     482        79407 :     } else {
     483        74665 :         myGeom.removeDoublePoints(POSITION_EPS, true);
     484              :     }
     485              : 
     486       154072 :     if (!tryIgnoreNodePositions || myGeom.size() < 2) {
     487        48385 :         if (myGeom.size() == 0) {
     488        48078 :             myGeom.push_back(myFrom->getPosition());
     489        48078 :             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       154072 :     if (myGeom.size() < 2) {
     496              :         myGeom.clear();
     497            0 :         myGeom.push_back(myFrom->getPosition());
     498            0 :         myGeom.push_back(myTo->getPosition());
     499              :     }
     500       154072 :     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       154072 :     myGeom.ensureMinLength(gPrecision);
     507              :     //
     508       154072 :     myFrom->addOutgoingEdge(this);
     509       154072 :     myTo->addIncomingEdge(this);
     510              :     // prepare container
     511              :     assert(myGeom.size() >= 2);
     512       154072 :     myLength = myGeom.length();
     513       154072 :     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       346212 :     for (int i = 0; i < noLanes; i++) {
     528       384280 :         myLanes.push_back(Lane(this, origID));
     529              :     }
     530       154072 :     computeLaneShapes();
     531       154072 :     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       154072 : }
     545              : 
     546              : 
     547       306225 : NBEdge::~NBEdge() {}
     548              : 
     549              : 
     550              : // -----------  Applying offset
     551              : void
     552        69203 : NBEdge::reshiftPosition(double xoff, double yoff) {
     553        69203 :     myGeom.add(xoff, yoff, 0);
     554       158708 :     for (Lane& lane : myLanes) {
     555        89505 :         lane.customShape.add(xoff, yoff, 0);
     556              :     }
     557        69203 :     computeLaneShapes(); // old shapes are dubious if computed with large coordinates
     558        75666 :     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        69203 :     myFromBorder.add(xoff, yoff, 0);
     565        69203 :     myToBorder.add(xoff, yoff, 0);
     566        69203 :     computeEdgeShape();
     567        69203 :     computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
     568        69203 : }
     569              : 
     570              : 
     571              : void
     572       127841 : NBEdge::roundGeometry() {
     573       127841 :     myGeom.round(gPrecision);
     574       292446 :     for (Lane& lane : myLanes) {
     575       164605 :         lane.customShape.round(gPrecision);
     576              :     }
     577       247755 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
     578       119914 :         (*i).customShape.round(gPrecision);
     579              :     }
     580       127841 : }
     581              : 
     582              : 
     583              : void
     584       127841 : NBEdge::roundSpeed() {
     585       127841 :     mySpeed = roundDecimalToEven(mySpeed, gPrecision);
     586              :     // lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
     587       292446 :     for (Lane& l : myLanes) {
     588       164605 :         l.speed = roundDecimalToEven(l.speed, gPrecision);
     589              :     }
     590       127841 : }
     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         6426 : NBEdge::getInnerGeometry() const {
     614         6426 :     return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
     615              : }
     616              : 
     617              : 
     618              : bool
     619       129656 : NBEdge::hasDefaultGeometry() const {
     620       129656 :     return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
     621              : }
     622              : 
     623              : 
     624              : bool
     625        71421 : NBEdge::hasDefaultGeometryEndpoints() const {
     626       134182 :     return myGeom.front().almostSame(myFrom->getPosition(), 0.01)  &&
     627        62761 :            myGeom.back().almostSame(myTo->getPosition(), 0.01);
     628              : }
     629              : 
     630              : 
     631              : bool
     632       214507 : NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
     633              :     // do not extend past the node position
     634       214507 :     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         5199 : NBEdge::getEndpointAtNode(const NBNode* node) const {
     644         5199 :     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         4481 : NBEdge::setGeometry(const PositionVector& s, bool inner) {
     661         4481 :     Position begin = myGeom.front(); // may differ from node position
     662         4481 :     Position end = myGeom.back(); // may differ from node position
     663              :     myGeom = s;
     664         4481 :     if (inner) {
     665            0 :         myGeom.insert(myGeom.begin(), begin);
     666            0 :         myGeom.push_back(end);
     667              :     }
     668              :     // ensure non-zero length (see ::init)
     669         4481 :     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         4481 :     computeLaneShapes();
     675         4481 :     computeAngle();
     676         4481 :     myLength = myGeom.length();
     677         4481 : }
     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       288710 : NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
     719       288710 :     PositionVector border;
     720       288710 :     if (rectangularCut) {
     721              :         const double extend = 100;
     722         3368 :         border = myGeom.getOrthogonal(p, extend, node == myTo);
     723              :     } else {
     724       287026 :         border.push_back(p);
     725       287026 :         border.push_back(p2);
     726              :     }
     727       288710 :     if (border.size() == 2) {
     728       288710 :         border.extrapolate2D(getTotalWidth());
     729       288710 :         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       288710 : }
     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        38085 : NBEdge::resetNodeBorder(const NBNode* node) {
     762        38085 :     if (node == myFrom) {
     763              :         myFromBorder.clear();
     764              :     } else {
     765              :         assert(node == myTo);
     766              :         myToBorder.clear();
     767              :     }
     768        38085 : }
     769              : 
     770              : 
     771              : bool
     772      6137811 : NBEdge::isBidiRail(bool ignoreSpread) const {
     773      6137811 :     return (isRailway(getPermissions())
     774       205071 :             && (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
     775       203670 :             && myPossibleTurnDestination != nullptr
     776       146440 :             && myPossibleTurnDestination->myPossibleTurnDestination == this
     777       139059 :             && (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
     778       138988 :             && isRailway(myPossibleTurnDestination->getPermissions())
     779     12275622 :             && myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
     780              : }
     781              : 
     782              : 
     783              : bool
     784      5726962 : NBEdge::isBidiEdge(bool checkPotential) const {
     785      5726962 :     return myPossibleTurnDestination != nullptr
     786      5247370 :            && myPossibleTurnDestination->myPossibleTurnDestination == this
     787      4992653 :            && (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
     788         8938 :            && myPossibleTurnDestination->getToNode() == getFromNode()
     789         8938 :            && myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
     790              :            // geometry check a) full overlap geometry
     791      5735900 :            && ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
     792        14582 :                 && (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
     793            0 :                     || (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
     794              :                // b) TWLT (Two-Way-Left-Turn-lane)
     795      5726962 :                || (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
     796      5726962 :               );
     797              : 
     798              : }
     799              : 
     800              : 
     801              : bool
     802         2848 : NBEdge::isRailDeadEnd() const {
     803         2848 :     if (!isRailway(getPermissions())) {
     804              :         return false;
     805              :     }
     806         4443 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
     807         8454 :         if (isRailway(out->getPermissions()) &&
     808         4151 :                 out != getTurnDestination(true)) {
     809              :             return true;
     810              :         }
     811              :     }
     812              :     return true;
     813              : }
     814              : 
     815              : 
     816              : PositionVector
     817       383221 : NBEdge::cutAtIntersection(const PositionVector& old) const {
     818              :     PositionVector shape = old;
     819       766442 :     shape = startShapeAt(shape, myFrom, myFromBorder);
     820              : #ifdef DEBUG_CUT_LANES
     821              :     if (DEBUGCOND) {
     822              :         std::cout << getID() << " cutFrom=" << shape << "\n";
     823              :     }
     824              : #endif
     825       383221 :     if (shape.size() < 2) {
     826              :         // only keep the last snippet
     827          417 :         const double oldLength = old.length();
     828          834 :         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       766442 :     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       383221 :     if (shape.length() < POSITION_EPS) {
     843          426 :         if (old.length() < 2 * POSITION_EPS) {
     844              :             shape = old;
     845              :         } else {
     846          416 :             const double midpoint = old.length() / 2;
     847              :             // EPS*2 because otherwhise shape has only a single point
     848          832 :             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       382795 :         if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
     861              :             // eliminate intermediate points
     862        33183 :             PositionVector tmp;
     863        33183 :             tmp.push_back(shape[0]);
     864        33183 :             tmp.push_back(shape[-1]);
     865              :             shape = tmp;
     866        33183 :             if (tmp.length() < POSITION_EPS) {
     867              :                 // fall back to original shape
     868           98 :                 if (old.length() < 2 * POSITION_EPS) {
     869              :                     shape = old;
     870              :                 } else {
     871           98 :                     const double midpoint = old.length() / 2;
     872              :                     // EPS*2 because otherwhise shape has only a single point
     873          196 :                     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        33085 :                 const double midpoint = shape.length() / 2;
     884              :                 // cut to size and reverse
     885        66170 :                 shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
     886        33085 :                 if (shape.length() < POSITION_EPS) {
     887              :                     assert(false);
     888              :                     // the shape has a sharp turn near the midpoint
     889              :                 }
     890        66170 :                 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        33183 :             const double z = (shape[0].z() + shape[1].z()) / 2;
     899        33183 :             shape[0].setz(z);
     900        33183 :             shape[1].setz(z);
     901        33183 :         }
     902              :     }
     903       383221 :     return shape;
     904            0 : }
     905              : 
     906              : 
     907              : void
     908       235111 : NBEdge::computeEdgeShape(double smoothElevationThreshold) {
     909       235111 :     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       540133 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     936       610044 :         myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
     937              :     }
     938              :     // recompute edge's length as the average of lane lengths
     939              :     double avgLength = 0;
     940       540133 :     for (int i = 0; i < (int)myLanes.size(); i++) {
     941       305022 :         avgLength += myLanes[i].shape.length();
     942              :     }
     943       235111 :     myLength = avgLength / (double) myLanes.size();
     944       235111 :     computeAngle(); // update angles using the finalized node and lane shapes
     945       235111 : }
     946              : 
     947              : 
     948              : PositionVector
     949       766770 : NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
     950       766770 :     if (nodeShape.size() == 0) {
     951       197369 :         nodeShape = startNode->getShape();
     952       197369 :         nodeShape.closePolygon();
     953              :     }
     954              :     PositionVector lb = laneShape;
     955       766770 :     lb.extrapolate2D(100.0);
     956       766770 :     if (nodeShape.intersects(laneShape)) {
     957              :         // shape intersects directly
     958       567383 :         std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
     959              :         assert(pbv.size() > 0);
     960              :         // ensure that the subpart has at least two points
     961       567383 :         double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
     962       567383 :         if (pb < 0) {
     963              :             return laneShape;
     964              :         }
     965       567361 :         PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
     966              :         //PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
     967       567361 :         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       567361 :         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       766770 :     } else if (nodeShape.intersects(lb)) {
     977              :         // extension of first segment intersects
     978        76334 :         std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
     979              :         assert(pbv.size() > 0);
     980              :         double pb = VectorHelper<double>::maxValue(pbv);
     981              :         assert(pb >= 0);
     982        76334 :         PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
     983        76334 :         Position np = lb.positionAtOffset2D(pb);
     984        76334 :         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        76334 :         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        76334 :         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        76334 :     } 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       766770 : }
    1006              : 
    1007              : 
    1008              : const PositionVector&
    1009      2529810 : NBEdge::getLaneShape(int i) const {
    1010      2529810 :     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       355234 : NBEdge::getLaneSpreadFunction() const {
    1022       355234 :     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        92076 : NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
    1060        92076 :     if (myGeom.size() < 3) {
    1061        41766 :         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       329997 :     for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
    1067       279642 :         angles.push_back(myGeom.angleAt2D(i));
    1068              :         //std::cout << " " << angles.back();
    1069              :     }
    1070              :     //std::cout << "\n  relative angles: ";
    1071        50355 :     NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
    1072       279464 :     for (int i = 0; i < (int)angles.size() - 1; ++i) {
    1073       229154 :         const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
    1074              :         //std::cout << relAngle << " ";
    1075       229154 :         if (maxAngle > 0 && relAngle > maxAngle) {
    1076          163 :             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          162 :             } else if (!silent) {
    1086          486 :                 WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
    1087              :             }
    1088              :         }
    1089       229153 :         if (relAngle < DEG2RAD(1)) {
    1090        87020 :             continue;
    1091              :         }
    1092       142133 :         if (i == 0 || i == (int)angles.size() - 2) {
    1093              :             const bool start = i == 0;
    1094        55323 :             const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
    1095        55323 :             const double r = tan(0.5 * (M_PI - relAngle)) * dist;
    1096              :             //std::cout << (start ? "  start" : "  end") << " length=" << dist << " radius=" << r << "  ";
    1097        55323 :             if (minRadius > 0 && r < minRadius) {
    1098          813 :                 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          769 :                 } else if (!silent) {
    1108         2292 :                     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        50355 : }
    1116              : 
    1117              : 
    1118              : // ----------- Setting and getting connections
    1119              : bool
    1120       140860 : NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
    1121       140860 :     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       140860 :     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       140755 :     } else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
    1135       247168 :         myConnections.push_back(Connection(-1, dest, -1));
    1136       123584 :         myConnections.back().permissions = permissions;
    1137              :     }
    1138       140860 :     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       140860 :     if (myStep < EdgeBuildingStep::EDGE2EDGES) {
    1149        64424 :         myStep = EdgeBuildingStep::EDGE2EDGES;
    1150              :     }
    1151              :     return true;
    1152              : }
    1153              : 
    1154              : 
    1155              : bool
    1156       140410 : 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       140410 :     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       140410 :     if (myTo != dest->myFrom) {
    1181              :         return false;
    1182              :     }
    1183       139666 :     if (!addEdge2EdgeConnection(dest)) {
    1184              :         return false;
    1185              :     }
    1186       139666 :     return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
    1187       139666 :                          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       286388 : 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       286388 :     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       286388 :     if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
    1240              :         return false;
    1241              :     }
    1242       275026 :     if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
    1243              :         return true;
    1244              :     }
    1245       274988 :     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       861104 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1251       586127 :         if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
    1252       220246 :             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       220164 :                 permissions = (*i).permissions;
    1256              :             }
    1257       220246 :             i = myConnections.erase(i);
    1258              :         } else {
    1259              :             ++i;
    1260              :         }
    1261              :     }
    1262       549954 :     myConnections.push_back(Connection(lane, destEdge, destLane));
    1263       274977 :     if (mayDefinitelyPass) {
    1264           27 :         myConnections.back().mayDefinitelyPass = true;
    1265              :     }
    1266       274977 :     myConnections.back().keepClear = keepClear;
    1267       274977 :     myConnections.back().contPos = contPos;
    1268       274977 :     myConnections.back().visibility = visibility;
    1269       274977 :     myConnections.back().permissions = permissions;
    1270       274977 :     myConnections.back().indirectLeft = indirectLeft;
    1271       274977 :     myConnections.back().edgeType = edgeType;
    1272       274977 :     myConnections.back().changeLeft = changeLeft;
    1273       274977 :     myConnections.back().changeRight = changeRight;
    1274       274977 :     myConnections.back().speed = speed;
    1275       274977 :     myConnections.back().friction = friction;
    1276       274977 :     myConnections.back().customLength = length;
    1277              :     myConnections.back().customShape = customShape;
    1278       274977 :     myConnections.back().uncontrolled = uncontrolled;
    1279       274977 :     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       271903 :         if (type == Lane2LaneInfoType::COMPUTED) {
    1284              :             // yes, the connection was set using an algorithm which requires a recheck
    1285       104276 :             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       167627 :             if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
    1289       138124 :                 myStep = EdgeBuildingStep::LANES2LANES_DONE;
    1290              :             }
    1291              :         }
    1292              :     }
    1293       274977 :     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      3904562 : NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
    1311              :     std::vector<NBEdge::Connection> ret;
    1312     17673945 :     for (const Connection& c : myConnections) {
    1313     13769383 :         if ((lane < 0 || c.fromLane == lane)
    1314      8925513 :                 && (to == nullptr || to == c.toEdge)
    1315      8909238 :                 && (toLane < 0 || toLane == c.toLane)) {
    1316      8908023 :             ret.push_back(c);
    1317              :         }
    1318              :     }
    1319      3904562 :     return ret;
    1320            0 : }
    1321              : 
    1322              : 
    1323              : const NBEdge::Connection&
    1324        20060 : NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
    1325        65847 :     for (const Connection& c : myConnections) {
    1326        65847 :         if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
    1327        20060 :             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       288563 : NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
    1349       288563 :     return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
    1350              : }
    1351              : 
    1352              : 
    1353              : bool
    1354      1115298 : NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
    1355      1115298 :     if (!ignoreTurnaround && (e == myTurnDestination)) {
    1356              :         return true;
    1357              :     }
    1358              :     return
    1359      1107276 :         find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
    1360              :         !=
    1361              :         myConnections.end();
    1362              : 
    1363              : }
    1364              : 
    1365              : 
    1366              : const EdgeVector*
    1367        67845 : NBEdge::getConnectedSorted() {
    1368              :     // check whether connections exist and if not, use edges from the node
    1369              :     EdgeVector outgoing;
    1370        67845 :     if (myConnections.size() == 0) {
    1371        22181 :         outgoing = myTo->getOutgoingEdges();
    1372              :     } else {
    1373       136242 :         for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1374        90578 :             if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
    1375        90267 :                 outgoing.push_back((*i).toEdge);
    1376              :             }
    1377              :         }
    1378              :     }
    1379        74538 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    1380         6693 :         if (it->fromLane < 0 && it->toLane < 0) {
    1381              :             // found an edge that shall not be connected
    1382         6693 :             EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
    1383         6693 :             if (forbidden != outgoing.end()) {
    1384              :                 outgoing.erase(forbidden);
    1385              :             }
    1386              :         }
    1387              :     }
    1388              :     // allocate the sorted container
    1389        67845 :     int size = (int) outgoing.size();
    1390        67845 :     EdgeVector* edges = new EdgeVector();
    1391        67845 :     edges->reserve(size);
    1392       187169 :     for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
    1393       119324 :         NBEdge* outedge = *i;
    1394       119324 :         if (outedge != nullptr && outedge != myTurnDestination) {
    1395       109504 :             edges->push_back(outedge);
    1396              :         }
    1397              :     }
    1398        67845 :     std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
    1399        67845 :     return edges;
    1400        67845 : }
    1401              : 
    1402              : 
    1403              : EdgeVector
    1404       202911 : NBEdge::getConnectedEdges() const {
    1405              :     EdgeVector ret;
    1406       666873 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1407       463962 :         if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
    1408       282343 :             ret.push_back((*i).toEdge);
    1409              :         }
    1410              :     }
    1411       202911 :     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       625674 : NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes, bool withBusLanes) const {
    1430              :     std::vector<int> ret;
    1431       625674 :     if (currentOutgoing != myTurnDestination) {
    1432      1903604 :         for (const Connection& c : myConnections) {
    1433      1328616 :             if (c.toEdge == currentOutgoing
    1434       501609 :                     && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)
    1435      1829712 :                     && (withBusLanes || getPermissions(c.fromLane) != SVC_BUS)) {
    1436       501077 :                 ret.push_back(c.fromLane);
    1437              :             }
    1438              :         }
    1439              :     }
    1440       625674 :     return ret;
    1441            0 : }
    1442              : 
    1443              : 
    1444              : void
    1445       128707 : NBEdge::sortOutgoingConnectionsByAngle() {
    1446       128707 :     sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
    1447       128707 : }
    1448              : 
    1449              : 
    1450              : void
    1451       184838 : NBEdge::sortOutgoingConnectionsByIndex() {
    1452       184838 :     sort(myConnections.begin(), myConnections.end(), connections_sorter);
    1453       184838 : }
    1454              : 
    1455              : 
    1456              : void
    1457            0 : NBEdge::remapConnections(const EdgeVector& incoming) {
    1458            0 :     EdgeVector connected = getConnectedEdges();
    1459            0 :     for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
    1460            0 :         NBEdge* inc = *i;
    1461              :         // We have to do this
    1462            0 :         inc->myStep = EdgeBuildingStep::EDGE2EDGES;
    1463              :         // add all connections
    1464            0 :         for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
    1465            0 :             inc->addEdge2EdgeConnection(*j);
    1466              :         }
    1467            0 :         inc->removeFromConnections(this);
    1468              :     }
    1469            0 : }
    1470              : 
    1471              : 
    1472              : void
    1473        80487 : NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
    1474              :                               const bool keepPossibleTurns) {
    1475              :     // remove from "myConnections"
    1476        80487 :     const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
    1477        80487 :     const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
    1478       161283 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    1479              :         Connection& c = *i;
    1480        80796 :         if ((toEdge == nullptr || c.toEdge == toEdge)
    1481         8909 :                 && (fromLane < 0 || c.fromLane == fromLane)
    1482         8746 :                 && (toLane < 0 || c.toLane == toLane)) {
    1483         8609 :             if (myTo->isTLControlled()) {
    1484              :                 std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
    1485          470 :                 for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
    1486          235 :                     (*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
    1487              :                 }
    1488              :             }
    1489         8609 :             i = myConnections.erase(i);
    1490              :             tryLater = false;
    1491         8609 :         } else {
    1492        72187 :             if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
    1493          128 :                 if (myTo->isTLControlled()) {
    1494              :                     std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
    1495           10 :                     for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
    1496           83 :                         for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
    1497              :                             NBConnection& tc = *tlcon;
    1498           78 :                             if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
    1499           12 :                                 tc.shiftLaneIndex(this, -1);
    1500              :                             }
    1501              :                         }
    1502              :                     }
    1503              :                 }
    1504              :                 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
    1505          128 :                 c.fromLane--;
    1506              :             }
    1507        72187 :             if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
    1508              :                 //std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
    1509          101 :                 c.toLane--;
    1510              :             }
    1511              :             ++i;
    1512              :         }
    1513              :     }
    1514              :     // check whether it was the turn destination
    1515        80487 :     if (myTurnDestination == toEdge && fromLane < 0) {
    1516         2596 :         myTurnDestination = nullptr;
    1517              :     }
    1518        80487 :     if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
    1519         2194 :         myPossibleTurnDestination = nullptr;
    1520              :     }
    1521        80487 :     if (tryLater) {
    1522        13598 :         myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
    1523              : #ifdef DEBUG_CONNECTION_GUESSING
    1524              :         if (DEBUGCOND) {
    1525              :             std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
    1526              :             for (Connection& c : myConnections) {
    1527              :                 std::cout << "  conn " << c.getDescription(this) << "\n";
    1528              :             }
    1529              :             for (Connection& c : myConnectionsToDelete) {
    1530              :                 std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    1531              :             }
    1532              :         }
    1533              : #endif
    1534              :     }
    1535        80487 : }
    1536              : 
    1537              : 
    1538              : bool
    1539           39 : NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
    1540              :     // iterate over connections
    1541           44 :     for (auto i = myConnections.begin(); i !=  myConnections.end(); i++) {
    1542           44 :         if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
    1543              :             // remove connection
    1544           39 :             myConnections.erase(i);
    1545              :             return true;
    1546              :         }
    1547              :     }
    1548              :     // assert(false);
    1549              :     return false;
    1550              : }
    1551              : 
    1552              : 
    1553              : void
    1554         3769 : NBEdge::invalidateConnections(bool reallowSetting) {
    1555         3769 :     myTurnDestination = nullptr;
    1556              :     myConnections.clear();
    1557         3769 :     if (reallowSetting) {
    1558         3649 :         myStep = EdgeBuildingStep::INIT;
    1559              :     } else {
    1560          120 :         myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
    1561              :     }
    1562         3769 : }
    1563              : 
    1564              : 
    1565              : void
    1566         1207 : NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
    1567              :     // replace in "_connectedEdges"
    1568         2512 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1569         1305 :         if ((*i).toEdge == which) {
    1570          761 :             (*i).toEdge = by;
    1571          761 :             (*i).toLane += laneOff;
    1572              :         }
    1573              :     }
    1574              :     // check whether it was the turn destination
    1575         1207 :     if (myTurnDestination == which) {
    1576           40 :         myTurnDestination = by;
    1577              :     }
    1578         1207 : }
    1579              : 
    1580              : void
    1581        46375 : NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
    1582              :     std::map<int, int> laneMap;
    1583              :     int minLane = -1;
    1584              :     int maxLane = -1;
    1585              :     // get lanes used to approach the edge to remap
    1586              :     bool wasConnected = false;
    1587        53297 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1588         6922 :         if ((*i).toEdge != which) {
    1589         6187 :             continue;
    1590              :         }
    1591              :         wasConnected = true;
    1592          735 :         if ((*i).fromLane != -1) {
    1593              :             int fromLane = (*i).fromLane;
    1594          705 :             laneMap[(*i).toLane] = fromLane;
    1595          705 :             if (minLane == -1 || minLane > fromLane) {
    1596              :                 minLane = fromLane;
    1597              :             }
    1598          705 :             if (maxLane == -1 || maxLane < fromLane) {
    1599              :                 maxLane = fromLane;
    1600              :             }
    1601              :         }
    1602              :     }
    1603        46375 :     if (!wasConnected) {
    1604              :         return;
    1605              :     }
    1606              :     // add new connections
    1607          517 :     std::vector<NBEdge::Connection> conns = origConns;
    1608          517 :     EdgeVector origTargets = getSuccessors();
    1609         2633 :     for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
    1610         2116 :         if ((*i).toEdge == which || (*i).toEdge == this
    1611              :                 // if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
    1612         4174 :                 || std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
    1613              : #ifdef DEBUG_REPLACECONNECTION
    1614              :             if (DEBUGCOND) {
    1615              :                 std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
    1616              :                           << " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
    1617              :             }
    1618              : #endif
    1619          881 :             continue;
    1620              :         }
    1621         1238 :         if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
    1622              :             // do not set lane-level connections
    1623            3 :             replaceInConnections(which, (*i).toEdge, 0);
    1624            3 :             continue;
    1625              :         }
    1626         1235 :         int fromLane = (*i).fromLane;
    1627              :         int toUse = -1;
    1628         1235 :         if (laneMap.find(fromLane) == laneMap.end()) {
    1629          288 :             if (fromLane >= 0 && fromLane <= minLane) {
    1630              :                 toUse = minLane;
    1631              :                 // patch laneMap to avoid crossed-over connections
    1632          455 :                 for (auto& item : laneMap) {
    1633          249 :                     if (item.first < fromLane) {
    1634           35 :                         item.second = MIN2(item.second, minLane);
    1635              :                     }
    1636              :                 }
    1637              :             }
    1638          288 :             if (fromLane >= 0 && fromLane >= maxLane) {
    1639              :                 toUse = maxLane;
    1640              :                 // patch laneMap to avoid crossed-over connections
    1641          336 :                 for (auto& item : laneMap) {
    1642          185 :                     if (item.first > fromLane) {
    1643           68 :                         item.second = MAX2(item.second, maxLane);
    1644              :                     }
    1645              :                 }
    1646              :             }
    1647              :         } else {
    1648          947 :             toUse = laneMap[fromLane];
    1649              :         }
    1650         1235 :         if (toUse == -1) {
    1651              :             toUse = 0;
    1652              :         }
    1653              : #ifdef DEBUG_REPLACECONNECTION
    1654              :         if (DEBUGCOND) {
    1655              :             std::cout  << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
    1656              :                        << " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
    1657              :                        << " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
    1658              :         }
    1659              : #endif
    1660         2470 :         setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
    1661         1235 :                       i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
    1662              :     }
    1663              :     // remove the remapped edge from connections
    1664          517 :     removeFromConnections(which);
    1665          517 : }
    1666              : 
    1667              : 
    1668              : void
    1669          789 : NBEdge::copyConnectionsFrom(NBEdge* src) {
    1670          789 :     myStep = src->myStep;
    1671          789 :     myConnections = src->myConnections;
    1672          789 : }
    1673              : 
    1674              : 
    1675              : bool
    1676         1114 : NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
    1677              :     // only allow using newFromLane if at least 1 vClass is permitted to use
    1678              :     // this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
    1679         1114 :     const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
    1680         1114 :     return (common > 0 && common != SVC_PEDESTRIAN);
    1681              : }
    1682              : 
    1683              : 
    1684              : void
    1685          492 : NBEdge::moveConnectionToLeft(int lane) {
    1686              : #ifdef DEBUG_CONNECTION_CHECKING
    1687              :     std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
    1688              : #endif
    1689              :     int index = 0;
    1690         2987 :     for (int i = 0; i < (int)myConnections.size(); ++i) {
    1691         2495 :         if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
    1692              :             index = i;
    1693              :         }
    1694              :     }
    1695              :     std::vector<Connection>::iterator i = myConnections.begin() + index;
    1696          492 :     Connection c = *i;
    1697          492 :     myConnections.erase(i);
    1698          492 :     setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
    1699          492 : }
    1700              : 
    1701              : 
    1702              : void
    1703          101 : NBEdge::moveConnectionToRight(int lane) {
    1704              : #ifdef DEBUG_CONNECTION_CHECKING
    1705              :     std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
    1706              : #endif
    1707          355 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1708          355 :         if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
    1709          101 :             Connection c = *i;
    1710          101 :             i = myConnections.erase(i);
    1711          101 :             setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
    1712              :             return;
    1713          101 :         }
    1714              :     }
    1715              : }
    1716              : 
    1717              : 
    1718              : double
    1719        53999 : NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
    1720        53999 :     const OptionsCont& oc = OptionsCont::getOptions();
    1721        53999 :     const int numPoints = oc.getInt("junctions.internal-link-detail");
    1722        53999 :     const bool joinTurns = oc.getBool("junctions.join-turns");
    1723        53999 :     const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
    1724        53999 :     const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
    1725        53999 :     const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
    1726        53999 :     const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
    1727        53999 :     const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
    1728        53999 :     const bool higherSpeed = oc.getBool("junctions.higher-speed");
    1729        53999 :     const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
    1730        53999 :     const double defaultContPos = oc.getFloat("default.connection.cont-pos");
    1731        53999 :     const bool fromRail = isRailway(getPermissions());
    1732        53999 :     std::string innerID = ":" + n.getID();
    1733              :     NBEdge* toEdge = nullptr;
    1734        53999 :     int edgeIndex = linkIndex;
    1735              :     int internalLaneIndex = 0;
    1736              :     int numLanes = 0; // number of lanes that share the same edge
    1737              :     double lengthSum = 0; // total shape length of all lanes that share the same edge
    1738              :     int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
    1739              :     bool averageLength = true;
    1740              :     double maxCross = 0.;
    1741       156534 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    1742              :         Connection& con = *i;
    1743       102535 :         con.haveVia = false; // reset first since this may be called multiple times
    1744       102535 :         if (con.toEdge == nullptr) {
    1745            0 :             continue;
    1746              :         }
    1747       102535 :         LinkDirection dir = n.getDirection(this, con.toEdge);
    1748       102535 :         const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
    1749       102535 :         const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
    1750              :         // put turning internal lanes on separate edges
    1751       102535 :         if (con.toEdge != toEdge) {
    1752              :             // skip indices to keep some correspondence between edge ids and link indices:
    1753              :             // internalEdgeIndex + internalLaneIndex = linkIndex
    1754        92652 :             edgeIndex = linkIndex;
    1755              :             toEdge = con.toEdge;
    1756              :             internalLaneIndex = 0;
    1757        92652 :             maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
    1758              :             numLanes = 0;
    1759              :             lengthSum = 0;
    1760              :         }
    1761       102535 :         averageLength = !isTurn || joinTurns; // legacy behavior
    1762       102535 :         SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
    1763       102535 :         const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
    1764       102535 :         PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
    1765              :         std::vector<int> foeInternalLinks;
    1766              : 
    1767       102535 :         if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
    1768          180 :             WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
    1769              :         }
    1770              : 
    1771              :         // crossingPosition, list of foe link indices
    1772       102535 :         std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
    1773              :         std::set<std::string> tmpFoeIncomingLanes;
    1774       102535 :         if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
    1775        64806 :             int index = 0;
    1776              :             std::vector<PositionVector> otherShapes;
    1777        64806 :             const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
    1778              :             const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
    1779       285091 :             for (const NBEdge* i2 : n.getIncomingEdges()) {
    1780      1017526 :                 for (const Connection& k2 : i2->getConnections()) {
    1781       797241 :                     if (k2.toEdge == nullptr) {
    1782            0 :                         continue;
    1783              :                     }
    1784              :                     // vehicles are typically less wide than the lane
    1785              :                     // they drive on but but bicycle lanes should be kept clear for their whole width
    1786       797241 :                     double width2 = k2.toEdge->getLaneWidth(k2.toLane);
    1787       797241 :                     if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
    1788       782871 :                         width2 *= 0.5;
    1789              :                     }
    1790       797241 :                     const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
    1791       797241 :                     LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
    1792       797241 :                     bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
    1793       797241 :                     const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
    1794        14698 :                     bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
    1795              :                     int shapeFlag = 0;
    1796       797241 :                     SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
    1797              :                     // do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
    1798              :                     if (con.customShape.size() == 0
    1799       796522 :                             && k2.customShape.size() == 0
    1800       796120 :                             && (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane  && avoidIntersectCandidate))
    1801       801031 :                             && ((i2->getPermissions(k2.fromLane) & warn) != 0
    1802         3265 :                                 && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
    1803              :                         // recompute with different curve parameters (unless
    1804              :                         // the other connection is "unimportant"
    1805         3186 :                         shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
    1806              :                         PositionVector origShape = shape;
    1807         6372 :                         shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
    1808         3186 :                         oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
    1809         3186 :                         if (oppositeLeftIntersect
    1810         2106 :                                 && (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
    1811              :                             shape = origShape;
    1812              :                         } else {
    1813              :                             // recompute previously computed crossing positions
    1814         3138 :                             if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
    1815         1757 :                                     || avoidedIntersectingLeftOriginLane < con.fromLane) {
    1816         3201 :                                 for (const PositionVector& otherShape : otherShapes) {
    1817         1773 :                                     const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1818         1773 :                                     const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
    1819         1773 :                                                                            "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1820         1773 :                                     if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1821              :                                         assert(minDV >= 0);
    1822         1647 :                                         if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1823          264 :                                             crossingPositions.first = minDV;
    1824              :                                         }
    1825              :                                     }
    1826              :                                 }
    1827              :                             }
    1828              :                             // make sure connections further to the left do not get a wider angle
    1829         3138 :                             avoidedIntersectingLeftOriginLane = con.fromLane;
    1830              :                         }
    1831         3186 :                     }
    1832       797241 :                     const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
    1833              :                     //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";
    1834              :                     // the following special case might get obsolete once we have solved #9745
    1835       797241 :                     const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
    1836              :                     // compute the crossing point
    1837       797241 :                     if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
    1838        33957 :                         crossingPositions.second.push_back(index);
    1839        33957 :                         const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
    1840        33957 :                         otherShapes.push_back(otherShape);
    1841        33957 :                         const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
    1842        33957 :                         const double minDV = firstIntersection(shape, otherShape, width1, width2,
    1843        33957 :                                                                "Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
    1844        33957 :                         if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
    1845              :                             assert(minDV >= 0);
    1846        29042 :                             if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1847        28311 :                                 crossingPositions.first = minDV;
    1848              :                             }
    1849              :                         }
    1850        33957 :                     }
    1851       797241 :                     const bool rightTurnConflict = NBNode::rightTurnConflict(
    1852       797241 :                                                        this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
    1853       797241 :                     const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
    1854           52 :                                                      (con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
    1855       797241 :                     const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
    1856       797241 :                     const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
    1857       797241 :                     const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
    1858              :                     // compute foe internal lanes
    1859       797241 :                     if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
    1860       227325 :                         foeInternalLinks.push_back(index);
    1861              :                     }
    1862              :                     // only warn once per pair of intersecting turns
    1863         2737 :                     if (oppositeLeftIntersect && getID() > i2->getID()
    1864         1371 :                             && (getPermissions(con.fromLane) & warn) != 0
    1865         1147 :                             && (con.toEdge->getPermissions(con.toLane) & warn) != 0
    1866         1111 :                             && (i2->getPermissions(k2.fromLane) & warn) != 0
    1867         1039 :                             && (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
    1868              :                             // do not warn for unregulated nodes
    1869       798267 :                             && n.getType() != SumoXMLNodeType::NOJUNCTION
    1870              :                        ) {
    1871          513 :                         WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
    1872              :                                        n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
    1873              :                     }
    1874              :                     // compute foe incoming lanes
    1875       797241 :                     const bool signalised = hasSignalisedConnectionTo(con.toEdge);
    1876      1458057 :                     if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
    1877       797970 :                             && (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
    1878       243020 :                         tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
    1879              :                     }
    1880       797241 :                     if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
    1881              :                         //std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
    1882              :                         // break symmetry using edge id
    1883              :                         // only store link index and resolve actual lane id later (might be multi-lane internal edge)
    1884          328 :                         tmpFoeIncomingLanes.insert(":" + toString(index));
    1885              :                     }
    1886       797241 :                     index++;
    1887              :                 }
    1888              :             }
    1889        64806 :             if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
    1890              :                 // let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
    1891              :                 // (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
    1892          226 :                 crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
    1893              :             }
    1894              :             // foe pedestrian crossings
    1895        64806 :             std::vector<NBNode::Crossing*> crossings = n.getCrossings();
    1896        82736 :             for (auto c : crossings) {
    1897              :                 const NBNode::Crossing& crossing = *c;
    1898        50143 :                 for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
    1899        32213 :                     const NBEdge* edge = *it_e;
    1900              :                     // compute foe internal lanes
    1901        32213 :                     if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
    1902         8410 :                         foeInternalLinks.push_back(index);
    1903         8410 :                         if (con.toEdge == edge &&
    1904         4197 :                                 ((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
    1905              :                             // build internal junctions (not for left turns at uncontrolled intersections)
    1906              :                             PositionVector crossingShape = crossing.shape;
    1907         1723 :                             crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
    1908         1723 :                             const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
    1909         1723 :                             if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
    1910              :                                 assert(minDV >= 0);
    1911         1594 :                                 if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
    1912          928 :                                     crossingPositions.first = minDV;
    1913              :                                 }
    1914              :                             }
    1915         8410 :                         } else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
    1916           20 :                             crossingPositions.first = 0;
    1917              :                         }
    1918              :                     }
    1919              :                 }
    1920        17930 :                 index++;
    1921              :             }
    1922              : 
    1923        64806 :         }
    1924       102535 :         if (con.contPos == UNSPECIFIED_CONTPOS) {
    1925       102505 :             con.contPos = defaultContPos;
    1926              :         }
    1927       102535 :         if (con.contPos != UNSPECIFIED_CONTPOS) {
    1928              :             // apply custom internal junction position
    1929           50 :             if (con.contPos <= 0 || con.contPos >= shape.length()) {
    1930              :                 // disable internal junction
    1931           21 :                 crossingPositions.first = -1;
    1932              :             } else {
    1933              :                 // set custom position
    1934           29 :                 crossingPositions.first = con.contPos;
    1935              :             }
    1936              :         }
    1937              : 
    1938              :         // @todo compute the maximum speed allowed based on angular velocity
    1939              :         //  see !!! for an explanation (with a_lat_mean ~0.3)
    1940              :         /*
    1941              :         double vmax = (double) 0.3 * (double) 9.80778 *
    1942              :                         getLaneShape(con.fromLane).back().distanceTo(
    1943              :                             con.toEdge->getLaneShape(con.toLane).front())
    1944              :                         / (double) 2.0 / (double) M_PI;
    1945              :         vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
    1946              :         */
    1947       102535 :         if (con.speed == UNSPECIFIED_SPEED) {
    1948       102367 :             if (higherSpeed) {
    1949           44 :                 con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
    1950              :             } else {
    1951       102343 :                 con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
    1952              :             }
    1953       102367 :             if (limitTurnSpeed > 0) {
    1954              :                 // see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
    1955       193926 :                 const double angleRaw = fabs(GeomHelper::angleDiff(
    1956        96963 :                                                  getLaneShape(con.fromLane).angleAt2D(-2),
    1957        96963 :                                                  con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
    1958       190374 :                 const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
    1959        96963 :                 const double length = shape.length2D();
    1960              :                 // do not trust the radius of tiny junctions
    1961              :                 // formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
    1962        96963 :                 if (angle > 0 && length > 1) {
    1963              :                     // permit higher turning speed on wide lanes
    1964        64831 :                     const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
    1965        64831 :                     const double limit = sqrt(limitTurnSpeed * radius);
    1966        64831 :                     const double reduction = con.vmax - limit;
    1967              :                     // always treat connctions at roundabout as turns when warning
    1968        64831 :                     const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
    1969              :                     const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
    1970        64521 :                     if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
    1971        64648 :                             || (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
    1972          503 :                         std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
    1973          343 :                         if (atRoundabout) {
    1974              :                             dirType = "roundabout";
    1975              :                         }
    1976         1029 :                         WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
    1977              :                                        dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
    1978              :                     }
    1979       121168 :                     con.vmax = MIN2(con.vmax, limit);
    1980              :                     // value is saved in <net> attribute. Must be set again when importing from .con.xml
    1981              :                     // con.speed = con.vmax;
    1982              :                 }
    1983              :                 assert(con.vmax > 0);
    1984              :                 //if (getID() == "-1017000.0.00") {
    1985              :                 //    std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
    1986              :                 //        << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
    1987              :                 //}
    1988         5404 :             } else if (fromRail && dir == LinkDirection::TURN) {
    1989            0 :                 con.vmax = 0.01;
    1990              :             }
    1991              :         } else {
    1992          168 :             con.vmax = con.speed;
    1993              :         }
    1994       102535 :         if (con.friction == UNSPECIFIED_FRICTION) {
    1995       102535 :             con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
    1996              :         }
    1997              :         //
    1998              :         assert(shape.size() >= 2);
    1999              :         // get internal splits if any
    2000       205070 :         con.id = innerID + "_" + toString(edgeIndex);
    2001       102535 :         const double shapeLength = shape.length();
    2002              :         double firstLength = shapeLength;
    2003       102535 :         con.internalViaLaneIndex = 0; // reset to default for netedit
    2004       102535 :         if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
    2005        19337 :             std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
    2006              :             con.shape = split.first;
    2007        19337 :             con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
    2008        19337 :             con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
    2009        19337 :             if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia)  {
    2010          235 :                 --splitIndex;
    2011          235 :                 con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
    2012              :             }
    2013        38674 :             con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
    2014        19337 :             ++splitIndex;
    2015              :             con.viaShape = split.second;
    2016        19337 :             con.haveVia = true;
    2017        19337 :             firstLength = con.shape.length();
    2018        19337 :         } else {
    2019              :             con.shape = shape;
    2020              :         }
    2021       102535 :         con.internalLaneIndex = internalLaneIndex;
    2022       102535 :         ++internalLaneIndex;
    2023       102535 :         ++linkIndex;
    2024              :         ++numLanes;
    2025       102535 :         if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
    2026              :             // split length proportionally
    2027           30 :             lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
    2028              :         } else {
    2029       102505 :             lengthSum += firstLength;
    2030              :         }
    2031       102535 :     }
    2032       107998 :     return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
    2033              : }
    2034              : 
    2035              : 
    2036              : double
    2037       146651 : NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
    2038              :     // assign average length to all lanes of the same internal edge if averageLength is set
    2039              :     // the lengthSum only covers the part up to the first internal junction
    2040              :     // TODO This code assumes that either all connections in question have a via or none
    2041              :     double maxCross = 0.;
    2042              :     assert(i - myConnections.begin() >= numLanes);
    2043       249186 :     for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
    2044              :         //std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
    2045              :         Connection& c = (*(i - prevIndex));
    2046       102535 :         const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
    2047       102535 :         c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
    2048       102535 :         if (c.haveVia) {
    2049        38674 :             c.viaLength = MAX2(minLength, c.viaShape.length());
    2050              :         }
    2051       102535 :         if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
    2052           30 :             if (c.haveVia) {
    2053              :                 // split length proportionally
    2054            4 :                 const double a = c.viaLength / (c.shape.length() + c.viaLength);
    2055            6 :                 c.viaLength = MAX2(minLength, a * c.customLength);
    2056              :             }
    2057           30 :             if (!averageLength) {
    2058           19 :                 c.length = MAX2(minLength, c.customLength - c.viaLength);
    2059              :             }
    2060              :         }
    2061       102535 :         if (c.haveVia) {
    2062              :             // we need to be able to leave from the internal junction by accelerating from 0
    2063        19337 :             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)
    2064              :         }
    2065              :         // we need to be able to cross the junction in one go but not if we have an indirect left turn
    2066       102535 :         if (c.indirectLeft) {
    2067           15 :             maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    2068              :         } else {
    2069       146677 :             maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
    2070              :         }
    2071              :     }
    2072       146651 :     return maxCross;
    2073              : }
    2074              : 
    2075              : 
    2076              : double
    2077        78065 : NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
    2078              :     double intersect = std::numeric_limits<double>::max();
    2079        78065 :     if (v2.length() < POSITION_EPS) {
    2080              :         return intersect;
    2081              :     }
    2082              :     try {
    2083              :         PositionVector v1Right = v1;
    2084        78037 :         v1Right.move2side(width1);
    2085              : 
    2086              :         PositionVector v1Left = v1;
    2087        78037 :         v1Left.move2side(-width1);
    2088              : 
    2089              :         PositionVector v2Right = v2;
    2090        78037 :         v2Right.move2side(width2);
    2091              : 
    2092              :         PositionVector v2Left = v2;
    2093        78037 :         v2Left.move2side(-width2);
    2094              : 
    2095              :         // intersect all border combinations
    2096              :         bool skip = secondIntersection;
    2097       102814 :         for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
    2098        24777 :             if (skip) {
    2099              :                 skip = false;
    2100            8 :                 continue;
    2101              :             }
    2102              :             intersect = MIN2(intersect, cand);
    2103        78037 :         }
    2104              :         skip = secondIntersection;
    2105       116256 :         for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
    2106        38219 :             if (skip) {
    2107              :                 skip = false;
    2108           11 :                 continue;
    2109              :             }
    2110              :             intersect = MIN2(intersect, cand);
    2111        78037 :         }
    2112              :         skip = secondIntersection;
    2113       122800 :         for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
    2114        44763 :             if (skip) {
    2115              :                 skip = false;
    2116            9 :                 continue;
    2117              :             }
    2118              :             intersect = MIN2(intersect, cand);
    2119        78037 :         }
    2120              :         skip = secondIntersection;
    2121       117126 :         for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
    2122        39089 :             if (skip) {
    2123              :                 skip = false;
    2124           11 :                 continue;
    2125              :             }
    2126              :             intersect = MIN2(intersect, cand);
    2127        78037 :         }
    2128        78037 :     } catch (InvalidArgument&) {
    2129            0 :         if (error != "") {
    2130            0 :             WRITE_WARNING(error);
    2131              :         }
    2132            0 :     }
    2133              :     //std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
    2134              :     //std::cout << "  intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
    2135              :     //std::cout << "  intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
    2136              :     return intersect;
    2137              : }
    2138              : 
    2139              : 
    2140              : bool
    2141       573515 : NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
    2142       573515 :     if (otherFrom == this) {
    2143              :         // not an opposite pair
    2144              :         return false;
    2145              :     }
    2146       348725 :     return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
    2147              : }
    2148              : 
    2149              : bool
    2150        17884 : NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
    2151              :                          double width1, double width2, int shapeFlag) const {
    2152        17884 :     const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
    2153        17884 :     const double minDV = firstIntersection(shape, otherShape, width1, width2);
    2154        35768 :     return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
    2155        17884 : }
    2156              : 
    2157              : 
    2158              : // -----------
    2159              : int
    2160     12578056 : NBEdge::getJunctionPriority(const NBNode* const node) const {
    2161     12578056 :     if (node == myFrom) {
    2162       288699 :         return myFromJunctionPriority;
    2163              :     } else {
    2164     12289357 :         return myToJunctionPriority;
    2165              :     }
    2166              : }
    2167              : 
    2168              : 
    2169              : void
    2170       369459 : NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
    2171       369459 :     if (node == myFrom) {
    2172       182317 :         myFromJunctionPriority = prio;
    2173              : #ifdef DEBUG_JUNCTIONPRIO
    2174              :         setParameter("fromPrio", toString(prio));
    2175              : #endif
    2176              :     } else {
    2177       187142 :         myToJunctionPriority = prio;
    2178              : #ifdef DEBUG_JUNCTIONPRIO
    2179              :         setParameter("toPrio", toString(prio));
    2180              : #endif
    2181              :     }
    2182       369459 : }
    2183              : 
    2184              : 
    2185              : double
    2186     21332583 : NBEdge::getAngleAtNode(const NBNode* const atNode) const {
    2187     21332583 :     if (atNode == myFrom) {
    2188     10478702 :         return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
    2189              :     }
    2190              :     assert(atNode == myTo);
    2191     10853881 :     return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2192              : }
    2193              : 
    2194              : 
    2195              : double
    2196      3994034 : NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
    2197              :     double res;
    2198      3994034 :     if (atNode == myFrom) {
    2199      1972127 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
    2200              :     } else {
    2201              :         assert(atNode == myTo);
    2202      2021907 :         res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
    2203              :     }
    2204      3994034 :     if (res < 0) {
    2205      2884694 :         res += 360;
    2206              :     }
    2207      3994034 :     return res;
    2208              : }
    2209              : 
    2210              : 
    2211              : double
    2212      6325686 : NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
    2213      6325686 :     if (atNode == myFrom) {
    2214      2939510 :         double res = myStartAngle - 180;
    2215      2939510 :         if (res < 0) {
    2216      1275080 :             res += 360;
    2217              :         }
    2218      2939510 :         return res;
    2219              :     } else {
    2220              :         assert(atNode == myTo);
    2221      3386176 :         return myEndAngle;
    2222              :     }
    2223              : }
    2224              : 
    2225              : 
    2226              : void
    2227       731714 : NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
    2228       731714 :     if (!onlyPossible) {
    2229       659909 :         myTurnDestination = e;
    2230              :     }
    2231       731714 :     myPossibleTurnDestination = e;
    2232       731714 : }
    2233              : 
    2234              : 
    2235              : double
    2236        10706 : NBEdge::getLaneSpeed(int lane) const {
    2237        10706 :     return myLanes[lane].speed;
    2238              : }
    2239              : 
    2240              : 
    2241              : double
    2242         3927 : NBEdge::getLaneFriction(int lane) const {
    2243         3927 :     return myLanes[lane].friction;
    2244              : }
    2245              : 
    2246              : 
    2247              : void
    2248           80 : NBEdge::resetLaneShapes() {
    2249           80 :     computeLaneShapes();
    2250           80 : }
    2251              : 
    2252              : 
    2253              : void
    2254            6 : NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
    2255           24 :     for (Lane& lane : myLanes) {
    2256           18 :         if (lane.changeLeft != SVCAll) {
    2257            6 :             lane.changeLeft = ignoring;
    2258              :         }
    2259           18 :         if (lane.changeRight != SVCAll) {
    2260            6 :             lane.changeRight = ignoring;
    2261              :         }
    2262              :     }
    2263           15 :     for (Connection& con : myConnections) {
    2264            9 :         if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
    2265            0 :             con.changeLeft = ignoring;
    2266              :         }
    2267            9 :         if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
    2268            0 :             con.changeRight = ignoring;
    2269              :         }
    2270              :     }
    2271            6 : }
    2272              : 
    2273              : 
    2274              : void
    2275       393563 : NBEdge::computeLaneShapes() {
    2276              :     // vissim needs this
    2277       393563 :     if (myFrom == myTo) {
    2278            0 :         return;
    2279              :     }
    2280              :     // compute lane offset, first
    2281       393563 :     std::vector<double> offsets(myLanes.size(), 0.);
    2282              :     double offset = 0;
    2283       507139 :     for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
    2284       113576 :         offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
    2285       113576 :         offsets[i] = offset;
    2286              :     }
    2287       393563 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    2288              :         double width = 0;
    2289       387449 :         for (int i = 0; i < (int)myLanes.size(); ++i) {
    2290       221023 :             width += getLaneWidth(i);
    2291              :         }
    2292       166426 :         offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
    2293              :     } else {
    2294       227137 :         double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
    2295       227137 :         offset = laneWidth / 2.;
    2296              :     }
    2297       393563 :     if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
    2298         4916 :         for (NBEdge* e : myTo->getOutgoingEdges()) {
    2299         4279 :             if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
    2300         1765 :                 offset += (e->getTotalWidth() - getTotalWidth()) / 2;
    2301         1765 :                 break;
    2302              :             }
    2303              :         }
    2304              :     }
    2305              : 
    2306       900702 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2307       507139 :         offsets[i] += offset;
    2308              :     }
    2309              : 
    2310              :     // build the shape of each lane
    2311       900702 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    2312       507139 :         if (myLanes[i].customShape.size() != 0) {
    2313              :             myLanes[i].shape = myLanes[i].customShape;
    2314           26 :             continue;
    2315              :         }
    2316              :         try {
    2317      1014226 :             myLanes[i].shape = computeLaneShape(i, offsets[i]);
    2318            0 :         } catch (InvalidArgument& e) {
    2319            0 :             WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
    2320              :             myLanes[i].shape = myGeom;
    2321            0 :         }
    2322              :     }
    2323       393563 : }
    2324              : 
    2325              : 
    2326              : PositionVector
    2327       507113 : NBEdge::computeLaneShape(int lane, double offset) const {
    2328              :     PositionVector shape = myGeom;
    2329              :     try {
    2330       507113 :         shape.move2side(offset);
    2331            0 :     } catch (InvalidArgument& e) {
    2332            0 :         WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
    2333            0 :     }
    2334       507113 :     return shape;
    2335            0 : }
    2336              : 
    2337              : 
    2338              : void
    2339       509891 : NBEdge::computeAngle() {
    2340              :     // taking the angle at the first might be unstable, thus we take the angle
    2341              :     // at a certain distance. (To compare two edges, additional geometry
    2342              :     // segments are considered to resolve ambiguities)
    2343       509891 :     const bool hasFromShape = myFrom->getShape().size() > 0;
    2344       509891 :     const bool hasToShape = myTo->getShape().size() > 0;
    2345       509891 :     Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
    2346       509891 :     Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
    2347              :     PositionVector shape = myGeom;
    2348       509891 :     if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
    2349       224275 :         if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
    2350       117362 :             shape = myLanes[getNumLanes() - 1].shape ;
    2351              :         } else {
    2352       106913 :             shape = myLanes[getNumLanes() / 2].shape;
    2353       106913 :             if (getNumLanes() % 2 == 0) {
    2354              :                 // there is no center lane. shift to get the center
    2355        16814 :                 shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
    2356              :             }
    2357              :         }
    2358              :     }
    2359              : 
    2360              :     // if the junction shape is suspicious we cannot trust the angle to the centroid
    2361       509891 :     const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
    2362       191847 :                                      || myFrom->getShape().around(shape[-1])
    2363       191091 :                                      || !(myFrom->getShape().around(fromCenter)));
    2364       509891 :     const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
    2365       187355 :                                    || myTo->getShape().around(shape[0])
    2366       186709 :                                    || !(myTo->getShape().around(toCenter)));
    2367              : 
    2368       509891 :     const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
    2369       509891 :     const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
    2370       509891 :     const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
    2371              : 
    2372       509891 :     myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
    2373       509891 :     const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
    2374       509891 :     const double myStartAngle3 = getAngleAtNode(myFrom);
    2375       509891 :     myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
    2376       509891 :     const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
    2377       509891 :     const double myEndAngle3 = getAngleAtNode(myTo);
    2378              : 
    2379              : #ifdef DEBUG_ANGLES
    2380              :     if (DEBUGCOND) {
    2381              :         if (suspiciousFromShape) {
    2382              :             std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
    2383              :                       << " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
    2384              :                       << " fromCenter=" << fromCenter
    2385              :                       << " fromPos=" << myFrom->getPosition()
    2386              :                       << " refStart=" << referencePosStart
    2387              :                       << "\n";
    2388              :         }
    2389              :         if (suspiciousToShape) {
    2390              :             std::cout << "suspiciousToShape len=" << shape.length() << "  endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
    2391              :                       << " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
    2392              :                       << " toCenter=" << toCenter
    2393              :                       << " toPos=" << myTo->getPosition()
    2394              :                       << " refEnd=" << referencePosEnd
    2395              :                       << "\n";
    2396              :         }
    2397              :     }
    2398              : #endif
    2399              : 
    2400       509891 :     if (suspiciousFromShape && shape.length() > 1) {
    2401        18489 :         myStartAngle = myStartAngle2;
    2402        45194 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
    2403              :                // don't trust footpath angles
    2404       495580 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2405         2714 :         myStartAngle = myStartAngle3;
    2406         2714 :         if (myStartAngle < 0) {
    2407         1313 :             myStartAngle += 360;
    2408              :         }
    2409              :     }
    2410              : 
    2411       509891 :     if (suspiciousToShape && shape.length() > 1) {
    2412        23945 :         myEndAngle = myEndAngle2;
    2413        24093 :     } else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
    2414              :                // don't trust footpath angles
    2415       491216 :                && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    2416         3273 :         myEndAngle = myEndAngle3;
    2417         3273 :         if (myEndAngle < 0) {
    2418         1604 :             myEndAngle += 360;
    2419              :         }
    2420              :     }
    2421              : 
    2422       509891 :     myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
    2423              : #ifdef DEBUG_ANGLES
    2424              :     if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
    2425              :                                  << " fromCenter=" << fromCenter << " toCenter=" << toCenter
    2426              :                                  << " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
    2427              :                                  << " hasFromShape=" << hasFromShape
    2428              :                                  << " hasToShape=" << hasToShape
    2429              :                                  << " numLanes=" << getNumLanes()
    2430              :                                  << " shapeLane=" << getNumLanes() / 2
    2431              :                                  << " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
    2432              : #endif
    2433       509891 : }
    2434              : 
    2435              : 
    2436              : double
    2437       208472 : NBEdge::getShapeStartAngle() const {
    2438       208472 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2439       208472 :     const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
    2440       208472 :     return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
    2441              : }
    2442              : 
    2443              : 
    2444              : double
    2445       104140 : NBEdge::getShapeEndAngle() const {
    2446       104140 :     const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
    2447       104140 :     const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
    2448       104140 :     return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
    2449              : }
    2450              : 
    2451              : 
    2452              : bool
    2453            0 : NBEdge::hasPermissions() const {
    2454            0 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2455            0 :         if ((*i).permissions != SVCAll) {
    2456              :             return true;
    2457              :         }
    2458              :     }
    2459              :     return false;
    2460              : }
    2461              : 
    2462              : 
    2463              : bool
    2464        25358 : NBEdge::hasLaneSpecificPermissions() const {
    2465              :     std::vector<Lane>::const_iterator i = myLanes.begin();
    2466        25358 :     SVCPermissions firstLanePermissions = i->permissions;
    2467              :     i++;
    2468        27052 :     for (; i != myLanes.end(); ++i) {
    2469        11691 :         if (i->permissions != firstLanePermissions) {
    2470              :             return true;
    2471              :         }
    2472              :     }
    2473              :     return false;
    2474              : }
    2475              : 
    2476              : 
    2477              : bool
    2478        23075 : NBEdge::hasLaneSpecificSpeed() const {
    2479        59532 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2480        36472 :         if (i->speed != getSpeed()) {
    2481              :             return true;
    2482              :         }
    2483              :     }
    2484              :     return false;
    2485              : }
    2486              : 
    2487              : bool
    2488            3 : NBEdge::hasLaneSpecificFriction() const {
    2489           10 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2490            7 :         if (i->friction != myLanes.begin()->friction) {
    2491              :             return true;
    2492              :         }
    2493              :     }
    2494              :     return false;
    2495              : }
    2496              : 
    2497              : bool
    2498        78092 : NBEdge::hasLaneSpecificWidth() const {
    2499       169024 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2500        96848 :         if (i->width != myLanes.begin()->width) {
    2501              :             return true;
    2502              :         }
    2503              :     }
    2504              :     return false;
    2505              : }
    2506              : 
    2507              : 
    2508              : bool
    2509         5890 : NBEdge::hasLaneSpecificType() const {
    2510        12488 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2511         6600 :         if (i->type != myLanes.begin()->type) {
    2512              :             return true;
    2513              :         }
    2514              :     }
    2515              :     return false;
    2516              : }
    2517              : 
    2518              : 
    2519              : bool
    2520        70614 : NBEdge::hasLaneSpecificEndOffset() const {
    2521       158475 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2522        87878 :         if (i->endOffset != myLanes.begin()->endOffset) {
    2523              :             return true;
    2524              :         }
    2525              :     }
    2526              :     return false;
    2527              : }
    2528              : 
    2529              : 
    2530              : bool
    2531        78770 : NBEdge::hasLaneSpecificStopOffsets() const {
    2532       178635 :     for (const auto& lane : myLanes) {
    2533        99870 :         if (lane.laneStopOffset.isDefined()) {
    2534            5 :             if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
    2535              :                 return true;
    2536              :             }
    2537              :         }
    2538              :     }
    2539              :     return false;
    2540              : }
    2541              : 
    2542              : 
    2543              : bool
    2544         5882 : NBEdge::hasAccelLane() const {
    2545        12457 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2546         6576 :         if (i->accelRamp) {
    2547              :             return true;
    2548              :         }
    2549              :     }
    2550              :     return false;
    2551              : }
    2552              : 
    2553              : 
    2554              : bool
    2555         5881 : NBEdge::hasCustomLaneShape() const {
    2556        12446 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2557         6570 :         if (i->customShape.size() > 0) {
    2558              :             return true;
    2559              :         }
    2560              :     }
    2561              :     return false;
    2562              : }
    2563              : 
    2564              : 
    2565              : bool
    2566         5876 : NBEdge::hasLaneParams() const {
    2567         9053 :     for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    2568         6563 :         if (i->getParametersMap().size() > 0) {
    2569              :             return true;
    2570              :         }
    2571              :     }
    2572              :     return false;
    2573              : }
    2574              : 
    2575              : bool
    2576         2490 : NBEdge::prohibitsChanging() const {
    2577         5649 :     for (const Lane& lane : myLanes) {
    2578         3166 :         if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
    2579              :             return true;
    2580              :         }
    2581              :     }
    2582              :     return false;
    2583              : }
    2584              : 
    2585              : bool
    2586         8180 : NBEdge::needsLaneSpecificOutput() const {
    2587         8180 :     return (hasLaneSpecificPermissions()
    2588         5897 :             || hasLaneSpecificSpeed()
    2589         5894 :             || hasLaneSpecificWidth()
    2590         5890 :             || hasLaneSpecificType()
    2591         5888 :             || hasLaneSpecificEndOffset()
    2592         5882 :             || hasLaneSpecificStopOffsets()
    2593         5882 :             || hasAccelLane()
    2594         5881 :             || hasCustomLaneShape()
    2595         5876 :             || hasLaneParams()
    2596         2490 :             || prohibitsChanging()
    2597        10663 :             || (!myLanes.empty() && myLanes.back().oppositeID != ""));
    2598              : }
    2599              : 
    2600              : 
    2601              : 
    2602              : bool
    2603       128086 : NBEdge::computeEdge2Edges(bool noLeftMovers) {
    2604              : #ifdef DEBUG_CONNECTION_GUESSING
    2605              :     if (DEBUGCOND) {
    2606              :         std::cout << "computeEdge2Edges  edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
    2607              :         for (Connection& c : myConnections) {
    2608              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2609              :         }
    2610              :         for (Connection& c : myConnectionsToDelete) {
    2611              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2612              :         }
    2613              :     }
    2614              : #endif
    2615              :     // return if this relationship has been build in previous steps or
    2616              :     //  during the import
    2617       128086 :     if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
    2618              :         return true;
    2619              :     }
    2620        67379 :     const bool fromRail = isRailway(getPermissions());
    2621       233523 :     for (NBEdge* out : myTo->getOutgoingEdges()) {
    2622       166144 :         if (noLeftMovers && myTo->isLeftMover(this, out)) {
    2623         1140 :             continue;
    2624              :         }
    2625              :         // avoid sharp railway turns
    2626       165004 :         if (fromRail && isRailway(out->getPermissions())) {
    2627         9547 :             const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
    2628         9547 :             if (angle > 150) {
    2629         3770 :                 continue;
    2630         5777 :             } else if (angle > 90) {
    2631              :                 // possibly the junction is large enough to achieve a plausible radius:
    2632          129 :                 const PositionVector& fromShape = myLanes.front().shape;
    2633          129 :                 const PositionVector& toShape = out->getLanes().front().shape;
    2634          129 :                 PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
    2635          129 :                 const double radius = shape.length2D() / DEG2RAD(angle);
    2636          129 :                 const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
    2637              :                 //std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
    2638          129 :                 if (radius < minRadius) {
    2639              :                     continue;
    2640              :                 }
    2641          129 :             }
    2642              :         }
    2643       161106 :         if (out == myTurnDestination) {
    2644              :             // will be added by appendTurnaround
    2645        42920 :             continue;
    2646              :         }
    2647       118186 :         if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
    2648              :             // no common permissions
    2649        29102 :             continue;
    2650              :         }
    2651       178168 :         myConnections.push_back(Connection(-1, out, -1));
    2652              :     }
    2653        67379 :     myStep = EdgeBuildingStep::EDGE2EDGES;
    2654        67379 :     return true;
    2655              : }
    2656              : 
    2657              : 
    2658              : bool
    2659       128086 : NBEdge::computeLanes2Edges() {
    2660              : #ifdef DEBUG_CONNECTION_GUESSING
    2661              :     if (DEBUGCOND) {
    2662              :         std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
    2663              :         for (Connection& c : myConnections) {
    2664              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2665              :         }
    2666              :         for (Connection& c : myConnectionsToDelete) {
    2667              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2668              :         }
    2669              :     }
    2670              : #endif
    2671              :     // return if this relationship has been build in previous steps or
    2672              :     //  during the import
    2673       128086 :     if (myStep >= EdgeBuildingStep::LANES2EDGES) {
    2674        60241 :         if (myStep == EdgeBuildingStep::LANES2LANES_USER && myConnections.size() > 1) {
    2675       130119 :             for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    2676        95539 :                 if ((*i).toEdge == nullptr) {
    2677            3 :                     WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
    2678            1 :                     i = myConnections.erase(i);
    2679              :                 } else {
    2680              :                     i++;
    2681              :                 }
    2682              :             }
    2683              :         }
    2684        60241 :         return true;
    2685              :     }
    2686              :     assert(myStep == EdgeBuildingStep::EDGE2EDGES);
    2687              :     // get list of possible outgoing edges sorted by direction clockwise
    2688              :     //  the edge in the backward direction (turnaround) is not in the list
    2689        67845 :     const EdgeVector* edges = getConnectedSorted();
    2690        67845 :     if (myConnections.size() != 0 && edges->size() == 0) {
    2691              :         // dead end per definition!?
    2692              :         myConnections.clear();
    2693              :     } else {
    2694              :         // divide the lanes on reachable edges
    2695        67823 :         divideOnEdges(edges);
    2696              :     }
    2697        67845 :     delete edges;
    2698        67845 :     myStep = EdgeBuildingStep::LANES2EDGES;
    2699        67845 :     return true;
    2700              : }
    2701              : 
    2702              : 
    2703              : std::vector<LinkDirection>
    2704          394 : NBEdge::decodeTurnSigns(int turnSigns, int shift) {
    2705              :     std::vector<LinkDirection> result;
    2706         3546 :     for (int i = 0; i < 8; i++) {
    2707              :         // see LinkDirection in SUMOXMLDefinitions.h
    2708         3152 :         if ((turnSigns & (1 << (i + shift))) != 0) {
    2709          323 :             result.push_back((LinkDirection)(1 << i));
    2710              :         }
    2711              :     }
    2712          394 :     return result;
    2713            0 : }
    2714              : 
    2715              : void
    2716          276 : NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
    2717          276 :     if (dirs.size() > 0) {
    2718           77 :         if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
    2719            2 :             perm &= ~spec;
    2720              :         } else {
    2721           75 :             perm |= spec;
    2722              :         }
    2723              :     }
    2724          276 : }
    2725              : 
    2726              : bool
    2727           34 : NBEdge::applyTurnSigns() {
    2728              : #ifdef DEBUG_TURNSIGNS
    2729              :     std::cout << "applyTurnSigns edge=" << getID() << "\n";
    2730              : #endif
    2731              :     // build a map of target edges and lanes
    2732              :     std::vector<const NBEdge*> targets;
    2733              :     std::map<const NBEdge*, std::vector<int> > toLaneMap;
    2734          144 :     for (const Connection& c : myConnections) {
    2735          110 :         if (myLanes[c.fromLane].turnSigns != 0) {
    2736          110 :             if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
    2737           90 :                 targets.push_back(c.toEdge);
    2738              :             }
    2739          110 :             toLaneMap[c.toEdge].push_back(c.toLane);
    2740              :         }
    2741              :     }
    2742              :     // might be unsorted due to bike lane connections
    2743          124 :     for (auto& item : toLaneMap) {
    2744           90 :         std::sort(item.second.begin(), item.second.end());
    2745              :     }
    2746              : 
    2747              :     // check number of distinct signed directions and count the number of signs for each direction
    2748              :     std::map<LinkDirection, int> signCons;
    2749              :     int allDirs = 0;
    2750          114 :     for (const Lane& lane : myLanes) {
    2751           80 :         allDirs |= lane.turnSigns;
    2752          179 :         for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
    2753           99 :             signCons[dir]++;
    2754           80 :         }
    2755              :     }
    2756           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
    2757           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
    2758           34 :     allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
    2759              : 
    2760           34 :     if ((allDirs & (int)LinkDirection::NODIR) != 0) {
    2761            3 :         targets.push_back(nullptr); // dead end
    2762              :     }
    2763              : 
    2764              :     SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
    2765              :     // build a mapping from sign directions to targets
    2766           34 :     std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
    2767              :     std::map<LinkDirection, const NBEdge*> dirMap;
    2768              : #ifdef DEBUG_TURNSIGNS
    2769              :     std::cout << "  numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
    2770              : #endif
    2771           34 :     if (signedDirs.size() > targets.size()) {
    2772           18 :         WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
    2773            6 :         return false;
    2774           28 :     } else if (signedDirs.size() < targets.size()) {
    2775              :         // we need to drop some targets (i.e. turn-around)
    2776              :         // use sumo-directions as a guide
    2777              :         std::vector<LinkDirection> sumoDirs;
    2778           82 :         for (const NBEdge* to : targets) {
    2779           65 :             sumoDirs.push_back(myTo->getDirection(this, to));
    2780              :         }
    2781              :         // remove targets to the left
    2782              :         bool checkMore = true;
    2783           37 :         while (signedDirs.size() < targets.size() && checkMore) {
    2784              :             checkMore = false;
    2785              :             //std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
    2786           20 :             if (sumoDirs.back() != signedDirs.back()) {
    2787              :                 targets.pop_back();
    2788              :                 sumoDirs.pop_back();
    2789              :                 checkMore = true;
    2790              :             }
    2791              :         }
    2792              :         // remove targets to the right
    2793              :         checkMore = true;
    2794           20 :         while (signedDirs.size() < targets.size() && checkMore) {
    2795              :             checkMore = false;
    2796            3 :             if (sumoDirs.front() != signedDirs.front()) {
    2797              :                 targets.erase(targets.begin());
    2798              :                 sumoDirs.erase(sumoDirs.begin());
    2799              :                 checkMore = true;
    2800              :             }
    2801              :         }
    2802              :         // remove targets by permissions
    2803              :         int i = 0;
    2804           25 :         while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
    2805            8 :             if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
    2806              :                 targets.erase(targets.begin() + i);
    2807              :                 sumoDirs.erase(sumoDirs.begin() + i);
    2808              :             } else {
    2809            8 :                 i++;
    2810              :             }
    2811              :         }
    2812           17 :         if (signedDirs.size() != targets.size()) {
    2813            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());
    2814              :             return false;
    2815              :         }
    2816           17 :     }
    2817              :     // directions and connections are both sorted from right to left
    2818           87 :     for (int i = 0; i < (int)signedDirs.size(); i++) {
    2819           62 :         dirMap[signedDirs[i]] = targets[i];
    2820              :     }
    2821              :     // check whether we have enough target lanes for a each signed direction
    2822           83 :     for (auto item : signCons) {
    2823           60 :         const LinkDirection dir = item.first;
    2824           60 :         if (dir == LinkDirection::NODIR) {
    2825            2 :             continue;
    2826              :         }
    2827           58 :         const NBEdge* to = dirMap[dir];
    2828           58 :         int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
    2829           58 :         if (candidates == 0) {
    2830            4 :             WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
    2831            2 :             return false;
    2832              :         }
    2833           57 :         std::vector<int>& knownTargets = toLaneMap[to];
    2834           57 :         if ((int)knownTargets.size() < item.second) {
    2835            4 :             if (candidates < item.second) {
    2836            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"),
    2837              :                                getID(), item.second, toString(dir), to->getID(), candidates);
    2838            1 :                 return false;
    2839              :             }
    2840              :             int i;
    2841              :             int iInc;
    2842              :             int iEnd;
    2843            3 :             if (dir > LinkDirection::STRAIGHT) {
    2844              :                 // set more targets on the left
    2845            1 :                 i = to->getNumLanes() - 1;
    2846              :                 iInc = -1;
    2847              :                 iEnd = -1;
    2848              :             } else {
    2849              :                 // set more targets on the right
    2850            2 :                 i = 0;
    2851              :                 iInc = 1;
    2852            2 :                 iEnd = to->getNumLanes();
    2853              :             }
    2854            8 :             while ((int)knownTargets.size() < item.second && i != iEnd) {
    2855            5 :                 if ((to->getPermissions(i) & defaultPermissions) != 0) {
    2856            5 :                     if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
    2857            3 :                         knownTargets.push_back(i);
    2858              :                     }
    2859              :                 }
    2860            5 :                 i += iInc;
    2861              :             }
    2862            3 :             if ((int)knownTargets.size() != item.second) {
    2863            0 :                 WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
    2864            0 :                 return false;
    2865              :             }
    2866            3 :             std::sort(knownTargets.begin(), knownTargets.end());
    2867              :         }
    2868              :     }
    2869              :     std::map<const NBEdge*, int> toLaneIndex;
    2870           79 :     for (int i = 0; i < getNumLanes(); i++) {
    2871           56 :         const int turnSigns = myLanes[i].turnSigns;
    2872              :         // no turnSigns are given for bicycle lanes and sidewalks
    2873           56 :         if (turnSigns != 0) {
    2874              :             // clear existing connections
    2875          253 :             for (auto it = myConnections.begin(); it != myConnections.end();) {
    2876          197 :                 if (it->fromLane == i) {
    2877           80 :                     it = myConnections.erase(it);
    2878              :                 } else {
    2879              :                     it++;
    2880              :                 }
    2881              :             }
    2882              :             // add new connections
    2883           56 :             int allSigns = (turnSigns
    2884           56 :                             | turnSigns >> TURN_SIGN_SHIFT_BUS
    2885           56 :                             | turnSigns >> TURN_SIGN_SHIFT_TAXI
    2886           56 :                             | turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
    2887           56 :             std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
    2888           56 :             std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
    2889           56 :             std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
    2890           56 :             std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
    2891              :             //std::cout << "  allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
    2892           56 :             SVCPermissions fromP = getPermissions(i);
    2893           56 :             if ((fromP & SVC_PASSENGER) != 0) {
    2894              :                 // if the source permits passenger traffic, the target should too
    2895              :                 fromP = SVC_PASSENGER;
    2896              :             }
    2897          125 :             for (LinkDirection dir : decodeTurnSigns(allSigns)) {
    2898           69 :                 SVCPermissions perm = 0;
    2899           69 :                 updateTurnPermissions(perm, dir, SVCAll, all);
    2900           69 :                 updateTurnPermissions(perm, dir, SVC_BUS, bus);
    2901           69 :                 updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
    2902           69 :                 updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
    2903           69 :                 if (perm == SVCAll) {
    2904           67 :                     perm = SVC_UNSPECIFIED;
    2905              :                 }
    2906              :                 //std::cout << "   lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
    2907           69 :                 NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
    2908           69 :                 if (to != nullptr) {
    2909              :                     if (toLaneIndex.count(to) == 0) {
    2910              :                         // initialize to rightmost feasible lane
    2911           55 :                         int toLane = toLaneMap[to][0];
    2912           57 :                         while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
    2913              :                             toLane++;
    2914              :                             /*
    2915              :                             if (toLane == to->getNumLanes()) {
    2916              :                                 SOFT_ASSERT(false);
    2917              :                             #ifdef DEBUG_TURNSIGNS
    2918              :                                 std::cout << "  could not find passenger lane for target=" << to->getID() << "\n";
    2919              :                             #endif
    2920              :                                 return false;
    2921              :                             }
    2922              :                             */
    2923              :                         }
    2924              : #ifdef DEBUG_TURNSIGNS
    2925              :                         std::cout << "  target=" << to->getID() << " initial toLane=" << toLane << "\n";
    2926              : #endif
    2927           55 :                         toLaneIndex[to] = toLane;
    2928              :                     }
    2929              : #ifdef DEBUG_TURNSIGNS
    2930              :                     //std::cout << "  set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
    2931              : #endif
    2932           67 :                     setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
    2933              :                                   false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
    2934              :                                   UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
    2935              :                                   myDefaultConnectionLength, PositionVector::EMPTY,
    2936              :                                   UNSPECIFIED_CONNECTION_UNCONTROLLED,
    2937              :                                   perm);
    2938           67 :                     if (toLaneIndex[to] < to->getNumLanes() - 1
    2939           67 :                             && (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
    2940           13 :                         toLaneIndex[to]++;
    2941           54 :                     } else if (toLaneIndex[to] < to->getNumLanes() - 2
    2942           54 :                                && (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
    2943              :                         // skip forbidden lane
    2944            0 :                         toLaneIndex[to] += 2;
    2945              :                     }
    2946              :                 }
    2947           56 :             }
    2948           56 :         }
    2949              :     }
    2950           23 :     sortOutgoingConnectionsByAngle();
    2951           23 :     sortOutgoingConnectionsByIndex();
    2952              :     return true;
    2953           68 : }
    2954              : 
    2955              : 
    2956              : bool
    2957       128086 : NBEdge::recheckLanes() {
    2958              : #ifdef DEBUG_CONNECTION_GUESSING
    2959              :     if (DEBUGCOND) {
    2960              :         std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
    2961              :         for (Connection& c : myConnections) {
    2962              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    2963              :         }
    2964              :         for (Connection& c : myConnectionsToDelete) {
    2965              :             std::cout << "  connToDelete " << c.getDescription(this) << "\n";
    2966              :         }
    2967              :     }
    2968              : #endif
    2969              :     // check delayed removals
    2970       134794 :     for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
    2971         6708 :         removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
    2972              :     }
    2973       128086 :     std::vector<int> connNumbersPerLane(myLanes.size(), 0);
    2974       384056 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    2975       255970 :         if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
    2976          105 :             i = myConnections.erase(i);
    2977              :         } else {
    2978              :             if ((*i).fromLane >= 0) {
    2979       255865 :                 ++connNumbersPerLane[(*i).fromLane];
    2980              :             }
    2981              :             ++i;
    2982              :         }
    2983              :     }
    2984       128086 :     if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
    2985              : #ifdef DEBUG_TURNSIGNS
    2986              :         if (myLanes.back().turnSigns != 0) {
    2987              :             std::cout << getID() << " hasTurnSigns\n";
    2988              :             if (myTurnSignTarget != myTo->getID()) {
    2989              :                 std::cout << "   tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
    2990              :             }
    2991              :         }
    2992              : #endif
    2993        57901 :         if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
    2994              :             // check #1:
    2995              :             // If there is a lane with no connections and any neighbour lane has
    2996              :             //  more than one connections, try to move one of them.
    2997              :             // This check is only done for edges which connections were assigned
    2998              :             //  using the standard algorithm.
    2999       133094 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    3000        75216 :                 if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
    3001              :                     // dead-end lane found
    3002              :                     bool hasDeadEnd = true;
    3003              :                     // find lane with two connections or more to the right of the current lane
    3004         3941 :                     for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
    3005         2397 :                         if (getPermissions(i) != getPermissions(i2)) {
    3006              :                             break;
    3007              :                         }
    3008         1612 :                         if (connNumbersPerLane[i2] > 1) {
    3009          356 :                             connNumbersPerLane[i2]--;
    3010          848 :                             for (int i3 = i2; i3 != i; i3++) {
    3011          492 :                                 moveConnectionToLeft(i3);
    3012          492 :                                 sortOutgoingConnectionsByAngle();
    3013          492 :                                 sortOutgoingConnectionsByIndex();
    3014              :                             }
    3015              :                             hasDeadEnd = false;
    3016              :                         }
    3017              :                     }
    3018         2329 :                     if (hasDeadEnd) {
    3019              :                         // find lane with two connections or more to the left of the current lane
    3020         3062 :                         for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
    3021         1184 :                             if (getPermissions(i) != getPermissions(i2)) {
    3022              :                                 break;
    3023              :                             }
    3024         1089 :                             if (connNumbersPerLane[i2] > 1) {
    3025           75 :                                 connNumbersPerLane[i2]--;
    3026          176 :                                 for (int i3 = i2; i3 != i; i3--) {
    3027          101 :                                     moveConnectionToRight(i3);
    3028          101 :                                     sortOutgoingConnectionsByAngle();
    3029          101 :                                     sortOutgoingConnectionsByIndex();
    3030              :                                 }
    3031              :                                 hasDeadEnd = false;
    3032              :                             }
    3033              :                         }
    3034              :                     }
    3035         1973 :                     if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
    3036              :                         int passengerLanes = 0;
    3037              :                         int passengerTargetLanes = 0;
    3038          906 :                         for (const Lane& lane : myLanes) {
    3039          695 :                             if ((lane.permissions & SVC_PASSENGER) != 0) {
    3040          569 :                                 passengerLanes++;
    3041              :                             }
    3042              :                         }
    3043          787 :                         for (const NBEdge* out : myTo->getOutgoingEdges()) {
    3044          576 :                             if (!isTurningDirectionAt(out)) {
    3045         1028 :                                 for (const Lane& lane : out->getLanes()) {
    3046          636 :                                     if ((lane.permissions & SVC_PASSENGER) != 0) {
    3047          515 :                                         passengerTargetLanes++;
    3048              :                                     }
    3049              :                                 }
    3050              :                             }
    3051              :                         }
    3052          211 :                         if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
    3053              :                             // no need for dead-ends
    3054           39 :                             if (i > 0) {
    3055              :                                 // check if a connection to the right has a usable target to the left of its target
    3056           28 :                                 std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
    3057           28 :                                 if (rightCons.size() > 0) {
    3058              :                                     const Connection& rc = rightCons.back();
    3059           28 :                                     NBEdge* to = rc.toEdge;
    3060           28 :                                     int toLane = rc.toLane + 1;
    3061              :                                     if (toLane < to->getNumLanes()
    3062            0 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3063           28 :                                             && !hasConnectionTo(to, toLane)) {
    3064              : #ifdef DEBUG_CONNECTION_CHECKING
    3065              :                                         std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
    3066              : #endif
    3067            0 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    3068              :                                         hasDeadEnd = false;
    3069            0 :                                         sortOutgoingConnectionsByAngle();
    3070            0 :                                         sortOutgoingConnectionsByIndex();
    3071              :                                     }
    3072              :                                     if (hasDeadEnd) {
    3073              :                                         // check if a connection to the right has a usable target to the right of its target
    3074           28 :                                         toLane = rc.toLane - 1;
    3075              :                                         if (toLane >= 0
    3076            7 :                                                 && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
    3077            7 :                                                 && (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3078           34 :                                                 && !hasConnectionTo(to, toLane)) {
    3079              :                                             // shift the right lane connection target right and connect the dead lane to the old target
    3080            1 :                                             getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
    3081              : #ifdef DEBUG_CONNECTION_CHECKING
    3082              :                                             std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
    3083              : #endif
    3084            1 :                                             setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
    3085              :                                             hasDeadEnd = false;
    3086            1 :                                             sortOutgoingConnectionsByAngle();
    3087            1 :                                             sortOutgoingConnectionsByIndex();
    3088              :                                         }
    3089              :                                     }
    3090              :                                 }
    3091           28 :                             }
    3092           39 :                             if (hasDeadEnd && i < getNumLanes() - 1) {
    3093              :                                 // check if a connection to the left has a usable target to the right of its target
    3094           30 :                                 std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
    3095           30 :                                 if (leftCons.size() > 0) {
    3096           30 :                                     NBEdge* to = leftCons.front().toEdge;
    3097           30 :                                     int toLane = leftCons.front().toLane - 1;
    3098              :                                     if (toLane >= 0
    3099            4 :                                             && (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
    3100           34 :                                             && !hasConnectionTo(to, toLane)) {
    3101              : #ifdef DEBUG_CONNECTION_CHECKING
    3102              :                                         std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
    3103              : #endif
    3104            4 :                                         setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
    3105              :                                         hasDeadEnd = false;
    3106            4 :                                         sortOutgoingConnectionsByAngle();
    3107            4 :                                         sortOutgoingConnectionsByIndex();
    3108              :                                     }
    3109              :                                 }
    3110           30 :                             }
    3111              : #ifdef ADDITIONAL_WARNINGS
    3112              :                             if (hasDeadEnd) {
    3113              :                                 WRITE_WARNING("Found dead-end lane " + getLaneID(i));
    3114              :                             }
    3115              : #endif
    3116              :                         }
    3117              :                     }
    3118              :                 }
    3119              :             }
    3120        57878 :             removeInvalidConnections();
    3121              :         }
    3122              :     }
    3123              :     // check involuntary dead end at "real" junctions
    3124       128086 :     if (getPermissions() != SVC_PEDESTRIAN) {
    3125       111799 :         if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
    3126          336 :             WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
    3127              :         }
    3128       111799 :         const EdgeVector& incoming = myFrom->getIncomingEdges();
    3129       111799 :         if (incoming.size() > 1) {
    3130       188039 :             for (int i = 0; i < (int)myLanes.size(); i++) {
    3131       105309 :                 if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
    3132              :                     bool connected = false;
    3133       133781 :                     for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
    3134       133603 :                         if ((*in)->hasConnectionTo(this, i)) {
    3135              :                             connected = true;
    3136              :                             break;
    3137              :                         }
    3138              :                     }
    3139        99262 :                     if (!connected) {
    3140          534 :                         WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
    3141              :                     }
    3142              :                 }
    3143              :             }
    3144              :         }
    3145              :     }
    3146              :     // avoid deadend due to change prohibitions
    3147       128086 :     if (getNumLanes() > 1 && myConnections.size() > 0) {
    3148        82821 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    3149        58433 :             Lane& lane = myLanes[i];
    3150        50471 :             if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
    3151           53 :                                                 && getSuccessors(SVC_PASSENGER).size() > 1))
    3152        58448 :                     && getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
    3153         2442 :                 const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
    3154         2442 :                 const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
    3155         2442 :                 if (forbiddenLeft && (i == 0 || forbiddenRight)) {
    3156            2 :                     lane.changeLeft = SVC_UNSPECIFIED;
    3157            4 :                     WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
    3158         2440 :                 } else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
    3159            1 :                     lane.changeRight = SVC_UNSPECIFIED;
    3160            2 :                     WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
    3161              :                 }
    3162              :             }
    3163              :         }
    3164              :     }
    3165              : #ifdef ADDITIONAL_WARNINGS
    3166              :     // check for connections with bad access permissions
    3167              :     for (const Connection& c : myConnections) {
    3168              :         SVCPermissions fromP = getPermissions(c.fromLane);
    3169              :         SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
    3170              :         if ((fromP & SVC_PASSENGER) != 0
    3171              :                 && toP == SVC_BICYCLE) {
    3172              :             bool hasAlternative = false;
    3173              :             for (const Connection& c2 : myConnections) {
    3174              :                 if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
    3175              :                         && (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
    3176              :                     hasAlternative = true;
    3177              :                 }
    3178              :             }
    3179              :             if (!hasAlternative) {
    3180              :                 WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
    3181              :             }
    3182              :         }
    3183              :     }
    3184              : 
    3185              : #endif
    3186              : #ifdef DEBUG_CONNECTION_GUESSING
    3187              :     if (DEBUGCOND) {
    3188              :         std::cout << "recheckLanes (final) edge=" << getID() << "\n";
    3189              :         for (Connection& c : myConnections) {
    3190              :             std::cout << "  conn " << c.getDescription(this) << "\n";
    3191              :         }
    3192              :     }
    3193              : #endif
    3194       128086 :     if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
    3195        71203 :         myStep = EdgeBuildingStep::LANES2LANES_DONE;
    3196              :     }
    3197       128086 :     return true;
    3198       128086 : }
    3199              : 
    3200              : 
    3201       128133 : void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
    3202       128133 :     if (getNumLanes() == 0) {
    3203            0 :         return;
    3204              :     }
    3205       128133 :     const int leftmostLane = getNumLanes() - 1;
    3206              :     // check oppositeID stored in other lanes
    3207       165602 :     for (int i = 0; i < leftmostLane; i++) {
    3208        37469 :         const std::string& oppositeID = getLanes()[i].oppositeID;
    3209        74938 :         NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
    3210        37469 :         if (oppositeID != "" && oppositeID != "-") {
    3211            6 :             if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
    3212            1 :                 getLaneStruct(leftmostLane).oppositeID = oppositeID;
    3213            3 :                 WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
    3214              :             } else {
    3215           15 :                 WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
    3216              :             }
    3217            6 :             getLaneStruct(i).oppositeID = "";
    3218              :         }
    3219              :     }
    3220       128133 :     const std::string& oppositeID = getLanes().back().oppositeID;
    3221       128133 :     if (oppositeID != "" && oppositeID != "-") {
    3222          112 :         NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
    3223          112 :         if (oppEdge == nullptr) {
    3224            3 :             WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
    3225            1 :             getLaneStruct(leftmostLane).oppositeID = "";
    3226              :         } else {
    3227          111 :             if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
    3228            3 :                 WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
    3229            1 :                 getLaneStruct(getNumLanes() - 1).oppositeID = "";
    3230              :             } else {
    3231          220 :                 if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
    3232            1 :                     const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
    3233            4 :                     WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
    3234            1 :                     getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
    3235              :                 }
    3236          110 :                 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
    3237          110 :                 const std::string leftmostID = getLaneID(leftmostLane);
    3238          110 :                 if (oppLane.oppositeID == "") {
    3239           15 :                     WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
    3240              :                     oppLane.oppositeID = leftmostID;
    3241          105 :                 } else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
    3242            1 :                     const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
    3243            1 :                     NBEdge* oppOppEdge = ec.retrieve(oppOpp);
    3244            1 :                     if (oppOppEdge == nullptr) {
    3245            0 :                         WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
    3246              :                         oppLane.oppositeID = leftmostID;
    3247              :                     } else {
    3248            1 :                         if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
    3249            0 :                             WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
    3250              :                         } else {
    3251            5 :                             WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
    3252              :                         }
    3253              :                         oppLane.oppositeID = leftmostID;
    3254            1 :                         NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
    3255            1 :                         if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
    3256              :                             oppOppLane.oppositeID = "";
    3257              :                         }
    3258              :                     }
    3259              :                 }
    3260          110 :                 if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
    3261            3 :                     if (fixOppositeLengths) {
    3262            1 :                         const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
    3263            4 :                         WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
    3264              :                                        oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
    3265            1 :                         setLoadedLength(avgLength);
    3266            1 :                         oppEdge->setLoadedLength(avgLength);
    3267              :                     } else {
    3268           10 :                         WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
    3269              :                                     ") differs in length from edge '" + getID() + "' (length " +
    3270              :                                     toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
    3271            2 :                         getLaneStruct(getNumLanes() - 1).oppositeID = "";
    3272              :                     }
    3273              :                 }
    3274              :             }
    3275              :         }
    3276              :     }
    3277              :     // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
    3278       128133 :     const NBEdge* bidi = getBidiEdge();
    3279       128133 :     if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
    3280         6980 :         getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
    3281              :     }
    3282              :     // check for valid offset and speed
    3283       135103 :     const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
    3284              :     int i = 0;
    3285       293735 :     for (const NBEdge::Lane& l : getLanes()) {
    3286       165602 :         if (startOffset + l.endOffset > getLength()) {
    3287            4 :             WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
    3288              :                            toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
    3289       165600 :         } else if (l.speed < 0.) {
    3290            0 :             WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
    3291       165600 :         } else if (l.speed == 0.) {
    3292            0 :             WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
    3293              :         }
    3294       165602 :         i++;
    3295              :     }
    3296              : }
    3297              : 
    3298        57878 : void NBEdge::removeInvalidConnections() {
    3299              :     // check restrictions
    3300       190428 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3301              :         Connection& c = *i;
    3302       132550 :         const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
    3303       132550 :         if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
    3304              :             // these are computed in NBNode::buildWalkingAreas
    3305              : #ifdef DEBUG_CONNECTION_CHECKING
    3306              :             std::cout << " remove pedCon " << c.getDescription(this) << "\n";
    3307              : #endif
    3308          475 :             i = myConnections.erase(i);
    3309       132075 :         } else if (common == 0) {
    3310              :             // no common permissions.
    3311              :             // try to find a suitable target lane to the right
    3312           83 :             const int origToLane = c.toLane;
    3313           83 :             c.toLane = -1; // ignore this connection when calling hasConnectionTo
    3314              :             int toLane = origToLane;
    3315           83 :             while (toLane > 0
    3316           45 :                     && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3317          143 :                     && !hasConnectionTo(c.toEdge, toLane)
    3318              :                   ) {
    3319           30 :                 toLane--;
    3320              :             }
    3321           83 :             if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3322           83 :                     && !hasConnectionTo(c.toEdge, toLane)) {
    3323            4 :                 c.toLane = toLane;
    3324              :                 ++i;
    3325              :             } else {
    3326              :                 // try to find a suitable target lane to the left
    3327              :                 toLane = origToLane;
    3328          148 :                 while (toLane < (int)c.toEdge->getNumLanes() - 1
    3329           95 :                         && (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
    3330          217 :                         && !hasConnectionTo(c.toEdge, toLane)
    3331              :                       ) {
    3332           69 :                     toLane++;
    3333              :                 }
    3334           79 :                 if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
    3335           79 :                         && !hasConnectionTo(c.toEdge, toLane)) {
    3336            0 :                     c.toLane = toLane;
    3337              :                     ++i;
    3338              :                 } else {
    3339              :                     // no alternative target found
    3340              : #ifdef DEBUG_CONNECTION_CHECKING
    3341              :                     std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
    3342              : #endif
    3343           79 :                     i = myConnections.erase(i);
    3344              :                 }
    3345              :             }
    3346       140372 :         } else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
    3347       140290 :                    && isTurningDirectionAt(c.toEdge))  {
    3348              :             // do not allow sharp rail turns
    3349              : #ifdef DEBUG_CONNECTION_CHECKING
    3350              :             std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
    3351              : #endif
    3352         2731 :             i = myConnections.erase(i);
    3353              :         } else {
    3354              :             ++i;
    3355              :         }
    3356              :     }
    3357        57878 : }
    3358              : 
    3359              : void
    3360        67823 : NBEdge::divideOnEdges(const EdgeVector* outgoing) {
    3361        67823 :     if (outgoing->size() == 0) {
    3362              :         // we have to do this, because the turnaround may have been added before
    3363              :         myConnections.clear();
    3364        11686 :         return;
    3365              :     }
    3366              : 
    3367              : #ifdef DEBUG_CONNECTION_GUESSING
    3368              :     if (DEBUGCOND) {
    3369              :         std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
    3370              :     }
    3371              : #endif
    3372              : 
    3373              :     // build connections for miv lanes
    3374              :     std::vector<int> availableLanes;
    3375       127995 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3376        71858 :         if ((getPermissions(i) & SVC_PASSENGER) != 0) {
    3377        46150 :             availableLanes.push_back(i);
    3378              :         }
    3379              :     }
    3380        56137 :     if (availableLanes.size() > 0) {
    3381        34339 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3382              :     }
    3383              :     // build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
    3384              :     availableLanes.clear();
    3385       127995 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3386        71858 :         const SVCPermissions perms = getPermissions(i);
    3387        71858 :         if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
    3388        61589 :             continue;
    3389              :         }
    3390        10269 :         availableLanes.push_back(i);
    3391              :     }
    3392        56137 :     if (availableLanes.size() > 0) {
    3393        10197 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3394              :     }
    3395              :     // build connections for busses from lanes that were excluded in the previous step
    3396              :     availableLanes.clear();
    3397       127995 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3398        71858 :         const SVCPermissions perms = getPermissions(i);
    3399        71858 :         if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
    3400        71594 :             continue;
    3401              :         }
    3402          264 :         availableLanes.push_back(i);
    3403              :     }
    3404        56137 :     if (availableLanes.size() > 0) {
    3405          261 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3406              :     }
    3407              :     // build connections for bicycles (possibly combined with pedestrians)
    3408              :     availableLanes.clear();
    3409       127995 :     for (int i = 0; i < (int)myLanes.size(); ++i) {
    3410        71858 :         const SVCPermissions perms = getPermissions(i);
    3411        71858 :         if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
    3412        70181 :             continue;
    3413              :         }
    3414         1677 :         availableLanes.push_back(i);
    3415              :     }
    3416        56137 :     if (availableLanes.size() > 0) {
    3417         1652 :         divideSelectedLanesOnEdges(outgoing, availableLanes);
    3418              :     }
    3419              :     // clean up unassigned fromLanes
    3420              :     bool explicitTurnaround = false;
    3421        56137 :     SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
    3422       245904 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
    3423       189767 :         if ((*i).fromLane == -1) {
    3424        89932 :             if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
    3425              :                 explicitTurnaround = true;
    3426          109 :                 turnaroundPermissions = (*i).permissions;
    3427              :             }
    3428        89932 :             if ((*i).permissions != SVC_UNSPECIFIED) {
    3429         1472 :                 for (Connection& c : myConnections) {
    3430         1280 :                     if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
    3431              :                         // carry over loaded edge2edge permissions
    3432          345 :                         c.permissions = (*i).permissions;
    3433              :                     }
    3434              :                 }
    3435              :             }
    3436        89932 :             i = myConnections.erase(i);
    3437              :         } else {
    3438              :             ++i;
    3439              :         }
    3440              :     }
    3441        56137 :     if (explicitTurnaround) {
    3442          218 :         myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
    3443          109 :         myConnections.back().permissions = turnaroundPermissions;
    3444              :     }
    3445        56137 :     sortOutgoingConnectionsByIndex();
    3446        56137 : }
    3447              : 
    3448              : 
    3449              : void
    3450        46449 : NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3451        46449 :     const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
    3452        46449 :     if (priorities.empty()) {
    3453              :         return;
    3454              :     }
    3455              : #ifdef DEBUG_CONNECTION_GUESSING
    3456              :     if (DEBUGCOND) {
    3457              :         std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
    3458              :     }
    3459              : #endif
    3460              :     // compute the resulting number of lanes that should be used to reach the following edge
    3461        46400 :     const int numOutgoing = (int)outgoing->size();
    3462              :     std::vector<int> resultingLanesFactor;
    3463        46400 :     resultingLanesFactor.reserve(numOutgoing);
    3464              :     int minResulting = std::numeric_limits<int>::max();
    3465       137256 :     for (int i = 0; i < numOutgoing; i++) {
    3466              :         // res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
    3467        90856 :         const int res = priorities[i] * (int)availableLanes.size();
    3468        90856 :         resultingLanesFactor.push_back(res);
    3469        90856 :         if (minResulting > res && res > 0) {
    3470              :             // prevent minResulting from becoming 0
    3471              :             minResulting = res;
    3472              :         }
    3473              :     }
    3474              :     // compute the number of virtual edges
    3475              :     //  a virtual edge is used as a replacement for a real edge from now on
    3476              :     //  it shall allow to divide the existing lanes on this structure without
    3477              :     //  regarding the structure of outgoing edges
    3478              :     int numVirtual = 0;
    3479              :     // compute the transition from virtual to real edges
    3480              :     EdgeVector transition;
    3481        46400 :     transition.reserve(numOutgoing);
    3482       137256 :     for (int i = 0; i < numOutgoing; i++) {
    3483              :         // tmpNum will be the number of connections from this edge to the next edge
    3484              :         assert(i < (int)resultingLanesFactor.size());
    3485        90856 :         const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
    3486        90856 :         numVirtual += tmpNum;
    3487       516158 :         for (int j = 0; j < tmpNum; j++) {
    3488       425302 :             transition.push_back((*outgoing)[i]);
    3489              :         }
    3490              :     }
    3491              : #ifdef DEBUG_CONNECTION_GUESSING
    3492              :     if (DEBUGCOND) {
    3493              :         std::cout << "   minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
    3494              :     }
    3495              : #endif
    3496              : 
    3497              :     // assign lanes to edges
    3498              :     //  (conversion from virtual to real edges is done)
    3499              :     ToEdgeConnectionsAdder adder(transition);
    3500        46400 :     Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
    3501              :     const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
    3502       137256 :     for (NBEdge* const target : *outgoing) {
    3503              :         assert(l2eConns.find(target) != l2eConns.end());
    3504       192466 :         for (const int j : l2eConns.find(target)->second) {
    3505       101610 :             const int fromIndex = availableLanes[j];
    3506       101610 :             if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
    3507              :                 // exclude connection if fromLane and toEdge have no common permissions
    3508           61 :                 continue;
    3509              :             }
    3510       101549 :             if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
    3511              :                 // exclude connection if the only commonly permitted class are pedestrians
    3512              :                 // these connections are later built in NBNode::buildWalkingAreas
    3513          249 :                 continue;
    3514              :             }
    3515              :             // avoid building more connections than the edge has viable lanes (earlier
    3516              :             // ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
    3517              :             //    @todo To decide which target lanes are still available we need to do a
    3518              :             // preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
    3519       101300 :             const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3520              :             int targetLanes = target->getNumLanes();
    3521       101300 :             if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3522         7216 :                 --targetLanes;
    3523              :             }
    3524       101300 :             if (numConsToTarget >= targetLanes) {
    3525         2623 :                 continue;
    3526              :             }
    3527        98677 :             if (myLanes[fromIndex].connectionsDone) {
    3528              :                 // we already have complete information about connections from
    3529              :                 // this lane. do not add anything else
    3530              : #ifdef DEBUG_CONNECTION_GUESSING
    3531              :                 if (DEBUGCOND) {
    3532              :                     std::cout << "     connectionsDone from " << getID() << "_" << fromIndex << ": ";
    3533              :                     for (const Connection& c : getConnectionsFromLane(fromIndex)) {
    3534              :                         std::cout << c.getDescription(this) << ", ";
    3535              :                     }
    3536              :                     std::cout << "\n";
    3537              :                 }
    3538              : #endif
    3539           38 :                 continue;
    3540              :             }
    3541       197278 :             myConnections.push_back(Connection(fromIndex, target, -1));
    3542              : #ifdef DEBUG_CONNECTION_GUESSING
    3543              :             if (DEBUGCOND) {
    3544              :                 std::cout << "     request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3545              :             }
    3546              : #endif
    3547              :         }
    3548              :     }
    3549              : 
    3550        46400 :     addStraightConnections(outgoing, availableLanes, priorities);
    3551        46449 : }
    3552              : 
    3553              : 
    3554              : void
    3555        46400 : NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
    3556              :     // ensure sufficient straight connections for the (highest-priority) straight target
    3557        46400 :     const int numOutgoing = (int) outgoing->size();
    3558              :     NBEdge* target = nullptr;
    3559              :     NBEdge* rightOfTarget = nullptr;
    3560              :     NBEdge* leftOfTarget = nullptr;
    3561              :     int maxPrio = 0;
    3562       137256 :     for (int i = 0; i < numOutgoing; i++) {
    3563        90856 :         if (maxPrio < priorities[i]) {
    3564        75941 :             const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
    3565        75941 :             if (dir == LinkDirection::STRAIGHT) {
    3566        38196 :                 maxPrio = priorities[i];
    3567        38196 :                 target = (*outgoing)[i];
    3568        38196 :                 rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
    3569        38196 :                 leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
    3570              :             }
    3571              :         }
    3572              :     }
    3573        46400 :     if (target == nullptr) {
    3574              :         return;
    3575              :     }
    3576        38030 :     int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
    3577              :     int targetLanes = (int)target->getNumLanes();
    3578        38030 :     if (target->getPermissions(0) == SVC_PEDESTRIAN) {
    3579         3216 :         --targetLanes;
    3580              :     }
    3581        38030 :     const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
    3582              : #ifdef DEBUG_CONNECTION_GUESSING
    3583              :     if (DEBUGCOND) {
    3584              :         std::cout << "  checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
    3585              :     }
    3586              : #endif
    3587              :     std::vector<int>::const_iterator it_avail = availableLanes.begin();
    3588        39125 :     while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
    3589         1095 :         const int fromIndex = *it_avail;
    3590              :         if (
    3591              :             // not yet connected
    3592         1095 :             (count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
    3593              :             // matching permissions
    3594          650 :             && ((getPermissions(fromIndex) & target->getPermissions()) != 0)
    3595              :             // more than pedestrians
    3596          648 :             && ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
    3597              :             // lane not yet fully defined
    3598         1741 :             && !myLanes[fromIndex].connectionsDone
    3599              :         ) {
    3600              : #ifdef DEBUG_CONNECTION_GUESSING
    3601              :             if (DEBUGCOND) {
    3602              :                 std::cout << "    candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3603              :             }
    3604              : #endif
    3605              :             // prevent same-edge conflicts
    3606              :             if (
    3607              :                 // no outgoing connections to the right from further left
    3608          317 :                 ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3609              :                 // no outgoing connections to the left from further right
    3610         1260 :                 && (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
    3611              : #ifdef DEBUG_CONNECTION_GUESSING
    3612              :                 if (DEBUGCOND) {
    3613              :                     std::cout << "     request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
    3614              :                 }
    3615              : #endif
    3616         1144 :                 myConnections.push_back(Connection(fromIndex, target, -1));
    3617          572 :                 numConsToTarget++;
    3618              :             } else {
    3619              : #ifdef DEBUG_CONNECTION_GUESSING
    3620              :                 if (DEBUGCOND) std::cout
    3621              :                             << "     fail check1="
    3622              :                             << ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
    3623              :                             << " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
    3624              :                             << " rightOfTarget=" << rightOfTarget->getID()
    3625              :                             << " leftOfTarget=" << leftOfTarget->getID()
    3626              :                             << "\n";
    3627              : #endif
    3628              : 
    3629              :             }
    3630              :         }
    3631              :         ++it_avail;
    3632              :     }
    3633              : }
    3634              : 
    3635              : 
    3636              : const std::vector<int>
    3637        46449 : NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
    3638              :     std::vector<int> priorities;
    3639        46449 :     MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
    3640              :     const int dist = mainDirections.getStraightest();
    3641        46449 :     if (dist == -1) {
    3642              :         return priorities;
    3643              :     }
    3644              :     // copy the priorities first
    3645        46400 :     priorities.reserve(outgoing->size());
    3646       137256 :     for (const NBEdge* const out : *outgoing) {
    3647        90856 :         int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
    3648              :         assert((prio + 1) * 2 > 0);
    3649        90856 :         prio = (prio + 1) * 2;
    3650        90856 :         priorities.push_back(prio);
    3651              :     }
    3652              :     // when the right turning direction has not a higher priority, divide
    3653              :     //  the importance by 2 due to the possibility to leave the junction
    3654              :     //  faster from this lane
    3655              : #ifdef DEBUG_CONNECTION_GUESSING
    3656              :     if (DEBUGCOND) std::cout << "  prepareEdgePriorities " << getID()
    3657              :                                  << " outgoing=" << toString(*outgoing)
    3658              :                                  << " priorities1=" << toString(priorities)
    3659              :                                  << " dist=" << dist
    3660              :                                  << "\n";
    3661              : #endif
    3662        46400 :     if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
    3663              :         assert(priorities.size() > 0);
    3664        14653 :         priorities[0] /= 2;
    3665              : #ifdef DEBUG_CONNECTION_GUESSING
    3666              :         if (DEBUGCOND) {
    3667              :             std::cout << "   priorities2=" << toString(priorities) << "\n";
    3668              :         }
    3669              : #endif
    3670              :     }
    3671              :     // HEURISTIC:
    3672              :     // when no higher priority exists, let the forward direction be
    3673              :     //  the main direction
    3674        46400 :     if (mainDirections.empty()) {
    3675              :         assert(dist < (int)priorities.size());
    3676        10374 :         priorities[dist] *= 2;
    3677              : #ifdef DEBUG_CONNECTION_GUESSING
    3678              :         if (DEBUGCOND) {
    3679              :             std::cout << "   priorities3=" << toString(priorities) << "\n";
    3680              :         }
    3681              : #endif
    3682              :     }
    3683        46400 :     if (NBNode::isTrafficLight(myTo->getType())) {
    3684         4350 :         priorities[dist] += 1;
    3685              :     } else {
    3686              :         // try to ensure separation of left turns
    3687        42050 :         if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
    3688          818 :             priorities[0] /= 4;
    3689          818 :             priorities[(int)priorities.size() - 1] /= 2;
    3690              : #ifdef DEBUG_CONNECTION_GUESSING
    3691              :             if (DEBUGCOND) {
    3692              :                 std::cout << "   priorities6=" << toString(priorities) << "\n";
    3693              :             }
    3694              : #endif
    3695        41232 :         } else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
    3696        21705 :                    && outgoing->size() > 2
    3697         4457 :                    && availableLanes.size() == 2
    3698        41412 :                    && (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
    3699          171 :             priorities[0] /= 4;
    3700          171 :             priorities.back() /= 2;
    3701              : #ifdef DEBUG_CONNECTION_GUESSING
    3702              :             if (DEBUGCOND) {
    3703              :                 std::cout << "   priorities7=" << toString(priorities) << "\n";
    3704              :             }
    3705              : #endif
    3706              :         }
    3707              :     }
    3708        46400 :     if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
    3709        24612 :         if (myLanes.size() > 2) {
    3710         2950 :             priorities[dist] *= 2;
    3711              : #ifdef DEBUG_CONNECTION_GUESSING
    3712              :             if (DEBUGCOND) {
    3713              :                 std::cout << "   priorities4=" << toString(priorities) << "\n";
    3714              :             }
    3715              : #endif
    3716              :         } else {
    3717        21662 :             priorities[dist] *= 3;
    3718              : #ifdef DEBUG_CONNECTION_GUESSING
    3719              :             if (DEBUGCOND) {
    3720              :                 std::cout << "   priorities5=" << toString(priorities) << "\n";
    3721              :             }
    3722              : #endif
    3723              :         }
    3724              :     }
    3725              :     return priorities;
    3726        46449 : }
    3727              : 
    3728              : 
    3729              : void
    3730        66839 : NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
    3731              :     // do nothing if no turnaround is known
    3732        66839 :     if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
    3733              :         return;
    3734              :     }
    3735              :     // do nothing if the destination node is controlled by a tls and no turnarounds
    3736              :     //  shall be appended for such junctions
    3737        44659 :     if (noTLSControlled && myTo->isTLControlled()) {
    3738              :         return;
    3739              :     }
    3740        44587 :     if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
    3741              :         return;
    3742              :     }
    3743              :     bool isDeadEnd = true;
    3744        46231 :     for (const Connection& c : myConnections) {
    3745        37103 :         if ((c.toEdge->getPermissions(c.toLane)
    3746        37103 :                 & getPermissions(c.fromLane)
    3747        37103 :                 & SVC_PASSENGER) != 0
    3748        37103 :                 || (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
    3749              :             isDeadEnd = false;
    3750              :             break;
    3751              :         }
    3752              :     }
    3753        44573 :     if (onlyDeadends && !isDeadEnd) {
    3754              :         return;
    3755              :     }
    3756        44374 :     const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3757        44374 :     if (onlyTurnlane) {
    3758           90 :         for (const Connection& c : getConnectionsFromLane(fromLane)) {
    3759           68 :             LinkDirection dir = myTo->getDirection(this, c.toEdge);
    3760           68 :             if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
    3761              :                 return;
    3762              :             }
    3763           82 :         }
    3764              :     }
    3765        44314 :     const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3766        44314 :     if (checkPermissions) {
    3767        44314 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
    3768              :             // exclude connection if fromLane and toEdge have no common permissions
    3769              :             return;
    3770              :         }
    3771        44041 :         if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
    3772              :             // exclude connection if the only commonly permitted class are pedestrians
    3773              :             // these connections are later built in NBNode::buildWalkingAreas
    3774              :             return;
    3775              :         }
    3776              :     }
    3777              :     // avoid railway turn-arounds
    3778        41433 :     if (isRailway(getPermissions() & myTurnDestination->getPermissions())
    3779        41433 :             && fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
    3780              :         // except at dead-ends on bidi-edges where they model a reversal in train direction
    3781              :         // @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
    3782         3151 :         if (isBidiRail() && isRailDeadEnd()) {
    3783              :             // add a slow connection because direction-reversal implies stopping
    3784         2848 :             setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
    3785         2848 :             return;
    3786              :         } else {
    3787          303 :             return;
    3788              :         }
    3789              :     };
    3790        38282 :     if (noGeometryLike && !isDeadEnd) {
    3791              :         // ignore paths and service entrances if this edge is for passenger traffic
    3792        32088 :         if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
    3793        24248 :                                      && !onlyTurnlane
    3794        24240 :                                      && myTo->geometryLike(
    3795        56328 :                                          NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
    3796        56328 :                                          NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
    3797              :             // make sure the turnDestination has other incoming edges
    3798         6095 :             EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
    3799         6095 :             if (turnIncoming.size() > 1) {
    3800              :                 // this edge is always part of incoming
    3801              :                 return;
    3802              :             }
    3803         6095 :         }
    3804              :     }
    3805        64584 :     setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
    3806              : }
    3807              : 
    3808              : 
    3809              : bool
    3810     16850355 : NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
    3811              :     // maybe it was already set as the turning direction
    3812     16850355 :     if (edge == myTurnDestination) {
    3813              :         return true;
    3814     13503017 :     } else if (myTurnDestination != nullptr) {
    3815              :         // otherwise - it's not if a turning direction exists
    3816              :         return false;
    3817              :     }
    3818      5544436 :     return edge == myPossibleTurnDestination;
    3819              : }
    3820              : 
    3821              : 
    3822              : NBNode*
    3823            0 : NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
    3824              :     // return the from-node when the position is at the begin of the edge
    3825            0 :     if (pos < tolerance) {
    3826            0 :         return myFrom;
    3827              :     }
    3828              :     // return the to-node when the position is at the end of the edge
    3829            0 :     if (pos > myLength - tolerance) {
    3830            0 :         return myTo;
    3831              :     }
    3832              :     return nullptr;
    3833              : }
    3834              : 
    3835              : 
    3836              : void
    3837           22 : NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
    3838              :     int lanes = e->getNumLanes();
    3839           49 :     for (int i = 0; i < lanes; i++) {
    3840           68 :         for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
    3841              :             assert(el.tlID == "");
    3842           82 :             addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
    3843           27 :         }
    3844              :     }
    3845           22 : }
    3846              : 
    3847              : 
    3848              : bool
    3849           93 : NBEdge::lanesWereAssigned() const {
    3850           93 :     return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
    3851              : }
    3852              : 
    3853              : 
    3854              : double
    3855            0 : NBEdge::getMaxLaneOffset() {
    3856            0 :     return SUMO_const_laneWidth * (double)myLanes.size();
    3857              : }
    3858              : 
    3859              : 
    3860              : bool
    3861       267181 : NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
    3862      1293972 :     for (const Connection& c : myConnections) {
    3863      1027388 :         if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
    3864              :             return false;
    3865              :         }
    3866              :     }
    3867              :     return true;
    3868              : }
    3869              : 
    3870              : 
    3871              : bool
    3872        43038 : NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
    3873        43038 :     const int fromLane = c.getFromLane();
    3874        43038 :     NBEdge* toEdge = c.getTo();
    3875        43038 :     const int toLane = c.getToLane();
    3876              :     const int tlIndex = c.getTLIndex();
    3877              :     const int tlIndex2 = c.getTLIndex2();
    3878              :     // check whether the connection was not set as not to be controled previously
    3879        43038 :     if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
    3880              :         return false;
    3881              :     }
    3882              : 
    3883              :     assert(fromLane < 0 || fromLane < (int) myLanes.size());
    3884              :     // try to use information about the connections if given
    3885        43038 :     if (fromLane >= 0 && toLane >= 0) {
    3886              :         // find the specified connection
    3887              :         std::vector<Connection>::iterator i =
    3888        43038 :             find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
    3889              :         // ok, we have to test this as on the removal of self-loop edges some connections
    3890              :         //  will be reassigned
    3891        43038 :         if (i != myConnections.end()) {
    3892              :             // get the connection
    3893              :             Connection& connection = *i;
    3894              :             // set the information about the tl
    3895        43038 :             connection.tlID = tlID;
    3896        43038 :             connection.tlLinkIndex = tlIndex;
    3897        43038 :             connection.tlLinkIndex2 = tlIndex2;
    3898              :             return true;
    3899              :         }
    3900              :     }
    3901              :     // if the original connection was not found, set the information for all
    3902              :     //  connections
    3903              :     int no = 0;
    3904              :     bool hadError = false;
    3905            0 :     for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    3906            0 :         if ((*i).toEdge != toEdge) {
    3907            0 :             continue;
    3908              :         }
    3909            0 :         if (fromLane >= 0 && fromLane != (*i).fromLane) {
    3910            0 :             continue;
    3911              :         }
    3912            0 :         if (toLane >= 0 && toLane != (*i).toLane) {
    3913            0 :             continue;
    3914              :         }
    3915            0 :         if ((*i).tlID == "") {
    3916              :             (*i).tlID = tlID;
    3917            0 :             (*i).tlLinkIndex = tlIndex;
    3918            0 :             (*i).tlLinkIndex2 = tlIndex2;
    3919            0 :             no++;
    3920              :         } else {
    3921            0 :             if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
    3922            0 :                 WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
    3923              :                 hadError = true;
    3924              :             }
    3925              :         }
    3926              :     }
    3927            0 :     if (hadError && no == 0) {
    3928            0 :         WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
    3929              :     }
    3930              :     return true;
    3931              : }
    3932              : 
    3933              : 
    3934              : void
    3935       128162 : NBEdge::clearControllingTLInformation() {
    3936       380969 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
    3937       252807 :         it->tlID = "";
    3938              :     }
    3939       128162 : }
    3940              : 
    3941              : 
    3942              : PositionVector
    3943       617146 : NBEdge::getCWBoundaryLine(const NBNode& n) const {
    3944       617146 :     PositionVector ret;
    3945              :     int lane;
    3946       617146 :     if (myFrom == (&n)) {
    3947              :         // outgoing
    3948       330375 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3949       330375 :         ret = myLanes[lane].shape;
    3950              :     } else {
    3951              :         // incoming
    3952       286771 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3953       573542 :         ret = myLanes[lane].shape.reverse();
    3954              :     }
    3955       617146 :     ret.move2side(getLaneWidth(lane) / 2.);
    3956       617146 :     return ret;
    3957            0 : }
    3958              : 
    3959              : 
    3960              : PositionVector
    3961       614908 : NBEdge::getCCWBoundaryLine(const NBNode& n) const {
    3962       614908 :     PositionVector ret;
    3963              :     int lane;
    3964       614908 :     if (myFrom == (&n)) {
    3965              :         // outgoing
    3966       286767 :         lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
    3967       286767 :         ret = myLanes[lane].shape;
    3968              :     } else {
    3969              :         // incoming
    3970       328141 :         lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
    3971       656282 :         ret = myLanes[lane].shape.reverse();
    3972              :     }
    3973       614908 :     ret.move2side(-getLaneWidth(lane) / 2.);
    3974       614908 :     return ret;
    3975            0 : }
    3976              : 
    3977              : 
    3978              : bool
    3979         8579 : NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
    3980              :     // ok, the number of lanes must match
    3981         8579 :     if (myLanes.size() != possContinuation->myLanes.size()) {
    3982              :         reason = "laneNumber";
    3983          327 :         return false;
    3984              :     }
    3985              :     // do not create self loops
    3986         8252 :     if (myFrom == possContinuation->myTo) {
    3987              :         reason = "loop";
    3988         1136 :         return false;
    3989              :     }
    3990              :     // conserve bidi-rails
    3991         7116 :     if (isBidiRail() != possContinuation->isBidiRail()) {
    3992              :         reason = "bidi-rail";
    3993            4 :         return false;
    3994              :     }
    3995              :     // also, check whether the connections - if any exit do allow to join
    3996              :     //  both edges
    3997              :     // This edge must have a one-to-one connection to the following lanes
    3998         7112 :     switch (myStep) {
    3999              :         case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
    4000              :             break;
    4001              :         case EdgeBuildingStep::INIT:
    4002              :             break;
    4003           14 :         case EdgeBuildingStep::EDGE2EDGES: {
    4004              :             // the following edge must be connected
    4005           14 :             const EdgeVector& conn = getConnectedEdges();
    4006           14 :             if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
    4007              :                 reason = "disconnected";
    4008              :                 return false;
    4009              :             }
    4010           14 :         }
    4011              :         break;
    4012           20 :         case EdgeBuildingStep::LANES2EDGES:
    4013              :         case EdgeBuildingStep::LANES2LANES_RECHECK:
    4014              :         case EdgeBuildingStep::LANES2LANES_DONE:
    4015              :         case EdgeBuildingStep::LANES2LANES_USER: {
    4016              :             // the possible continuation must be connected
    4017           20 :             if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
    4018              :                 reason = "disconnected";
    4019            0 :                 return false;
    4020              :             }
    4021              :             // all lanes must go to the possible continuation
    4022           20 :             std::vector<int> conns = getConnectionLanes(possContinuation);
    4023           20 :             const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
    4024           20 :             if (conns.size() < myLanes.size() - offset) {
    4025              :                 reason = "some lanes disconnected";
    4026              :                 return false;
    4027              :             }
    4028           20 :         }
    4029              :         break;
    4030              :         default:
    4031              :             break;
    4032              :     }
    4033         7112 :     const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
    4034         7113 :     if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
    4035              :         return true;
    4036              :     }
    4037         7111 :     const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
    4038         7111 :     if (maxJunctionSize >= 0) {
    4039            4 :         const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
    4040            4 :         if (junctionSize > maxJunctionSize + POSITION_EPS) {
    4041            8 :             reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
    4042            2 :             return false;
    4043              :         }
    4044              :     }
    4045              :     // the priority, too (?)
    4046         7109 :     if (getPriority() != possContinuation->getPriority()) {
    4047              :         reason = "priority";
    4048           70 :         return false;
    4049              :     }
    4050              :     // the speed allowed
    4051         7039 :     if (mySpeed != possContinuation->mySpeed) {
    4052              :         reason = "speed";
    4053         1065 :         return false;
    4054              :     }
    4055              :     // the routingType
    4056         5974 :     if (myRoutingType != possContinuation->myRoutingType) {
    4057              :         reason = "routingType";
    4058            2 :         return false;
    4059              :     }
    4060              :     // spreadtype should match or it will look ugly
    4061         5972 :     if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
    4062              :         reason = "spreadType";
    4063           78 :         return false;
    4064              :     }
    4065              :     // matching lanes must have identical properties
    4066        13775 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4067         7966 :         if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
    4068            0 :             reason = "lane " + toString(i) + " speed";
    4069           85 :             return false;
    4070         7966 :         } else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
    4071          116 :             reason = "lane " + toString(i) + " permissions";
    4072           58 :             return false;
    4073         7908 :         } else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
    4074           14 :             reason = "lane " + toString(i) + " change restrictions";
    4075            7 :             return false;
    4076         7980 :         } else if (myLanes[i].width != possContinuation->myLanes[i].width &&
    4077         8059 :                    fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
    4078           40 :             reason = "lane " + toString(i) + " width";
    4079           20 :             return false;
    4080              :         }
    4081              :     }
    4082              :     // if given identically osm names
    4083        15941 :     if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
    4084         5918 :             && ((myStreetName != "" && possContinuation->getStreetName() != "")
    4085              :                 // only permit merging a short unnamed road with a longer named road
    4086           21 :                 || (myStreetName != "" && myLength <= possContinuation->getLength())
    4087           16 :                 || (myStreetName == "" && myLength >= possContinuation->getLength()))) {
    4088              :         return false;
    4089              :     }
    4090              : 
    4091              :     return true;
    4092              : }
    4093              : 
    4094              : 
    4095              : void
    4096         5512 : NBEdge::append(NBEdge* e) {
    4097              :     // append geometry
    4098         5512 :     myGeom.append(e->myGeom);
    4099        12957 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4100         7445 :         myLanes[i].customShape.append(e->myLanes[i].customShape);
    4101         9540 :         if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
    4102        16985 :                 || OptionsCont::getOptions().getBool("output.original-names")) {
    4103        16062 :             const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
    4104        16062 :             const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
    4105         5354 :             if (origID != origID2) {
    4106         9756 :                 myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
    4107              :             }
    4108              :         }
    4109         7445 :         myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
    4110         7445 :         myLanes[i].turnSigns = e->myLanes[i].turnSigns;
    4111              :     }
    4112         5512 :     if (e->getLength() > myLength) {
    4113              :         // possibly some lane attributes differ (when using option geometry.remove.min-length)
    4114              :         // make sure to use the attributes from the longer edge
    4115         6069 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4116         3401 :             myLanes[i].width = e->myLanes[i].width;
    4117              :         }
    4118              :         // defined name prevails over undefined name of shorter road
    4119         2668 :         if (myStreetName == "") {
    4120         1261 :             myStreetName = e->myStreetName;
    4121              :         }
    4122              :     }
    4123              :     // recompute length
    4124         5512 :     myLength += e->myLength;
    4125         5512 :     if (myLoadedLength > 0 || e->myLoadedLength > 0) {
    4126            1 :         myLoadedLength = getFinalLength() + e->getFinalLength();
    4127              :     }
    4128              :     // copy the connections and the building step if given
    4129         5512 :     myStep = e->myStep;
    4130         5512 :     myConnections = e->myConnections;
    4131         5512 :     myTurnDestination = e->myTurnDestination;
    4132         5512 :     myPossibleTurnDestination = e->myPossibleTurnDestination;
    4133         5512 :     myConnectionsToDelete = e->myConnectionsToDelete;
    4134         5512 :     updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
    4135              :     // set the node
    4136         5512 :     myTo = e->myTo;
    4137         5512 :     myTurnSignTarget = e->myTurnSignTarget;
    4138              :     myToBorder = e->myToBorder;
    4139        11024 :     mergeParameters(e->getParametersMap());
    4140              :     if (e->mySignalPosition != Position::INVALID) {
    4141         1169 :         mySignalPosition = e->mySignalPosition;
    4142              :     }
    4143         5512 :     computeAngle(); // myEndAngle may be different now
    4144         5512 : }
    4145              : 
    4146              : 
    4147              : void
    4148         5519 : NBEdge::updateRemovedNodes(const std::string& removed) {
    4149        11038 :     std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
    4150         5519 :     if (!result.empty() && !removed.empty()) {
    4151              :         result += " ";
    4152              :     }
    4153              :     result += removed;
    4154         5519 :     if (!result.empty()) {
    4155            9 :         setParameter(SUMO_PARAM_REMOVED_NODES, result);
    4156              :     }
    4157         5519 : }
    4158              : 
    4159              : 
    4160              : bool
    4161       966606 : NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
    4162      4019315 :     for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
    4163      3380879 :         if ((*i).toEdge == e && (*i).tlID != "") {
    4164              :             return true;
    4165              :         }
    4166              :     }
    4167              :     return false;
    4168              : }
    4169              : 
    4170              : 
    4171              : NBEdge*
    4172       924829 : NBEdge::getTurnDestination(bool possibleDestination) const {
    4173       924829 :     if (myTurnDestination == nullptr && possibleDestination) {
    4174        74501 :         return myPossibleTurnDestination;
    4175              :     }
    4176              :     return myTurnDestination;
    4177              : }
    4178              : 
    4179              : 
    4180              : std::string
    4181       333463 : NBEdge::getLaneID(int lane) const {
    4182       666926 :     return myID + "_" + toString(lane);
    4183              : }
    4184              : 
    4185              : 
    4186              : bool
    4187           65 : NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
    4188           65 :     std::vector<double> distances = myGeom.distances(e->getGeometry());
    4189              :     assert(distances.size() > 0);
    4190          130 :     return VectorHelper<double>::maxValue(distances) < threshold;
    4191           65 : }
    4192              : 
    4193              : 
    4194              : void
    4195           67 : NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
    4196              :     assert(index <= (int)myLanes.size());
    4197          201 :     myLanes.insert(myLanes.begin() + index, Lane(this, ""));
    4198              :     // copy attributes
    4199           67 :     if (myLanes.size() > 1) {
    4200           67 :         int templateIndex = index > 0 ? index - 1 : index + 1;
    4201           67 :         myLanes[index].speed = myLanes[templateIndex].speed;
    4202           67 :         myLanes[index].friction = myLanes[templateIndex].friction;
    4203           67 :         myLanes[index].permissions = myLanes[templateIndex].permissions;
    4204           67 :         myLanes[index].preferred = myLanes[templateIndex].preferred;
    4205           67 :         myLanes[index].endOffset = myLanes[templateIndex].endOffset;
    4206           67 :         myLanes[index].width = myLanes[templateIndex].width;
    4207           67 :         myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
    4208              :     }
    4209           67 :     const EdgeVector& incs = myFrom->getIncomingEdges();
    4210           67 :     if (recomputeShape) {
    4211           51 :         computeLaneShapes();
    4212              :     }
    4213           67 :     if (recomputeConnections) {
    4214          136 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    4215           85 :             (*i)->invalidateConnections(true);
    4216              :         }
    4217           51 :         invalidateConnections(true);
    4218           16 :     } else if (shiftIndices) {
    4219              :         // shift outgoing connections above the added lane to the left
    4220            0 :         for (Connection& c : myConnections) {
    4221            0 :             if (c.fromLane >= index) {
    4222            0 :                 c.fromLane += 1;
    4223              :             }
    4224              :         }
    4225              :         // shift incoming connections above the added lane to the left
    4226            0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4227            0 :             for (Connection& c : inc->myConnections) {
    4228            0 :                 if (c.toEdge == this && c.toLane >= index) {
    4229            0 :                     c.toLane += 1;
    4230              :                 }
    4231              :             }
    4232              :         }
    4233            0 :         myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
    4234            0 :         myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
    4235              :     }
    4236           67 : }
    4237              : 
    4238              : void
    4239           54 : NBEdge::incLaneNo(int by) {
    4240           54 :     int newLaneNo = (int)myLanes.size() + by;
    4241          121 :     while ((int)myLanes.size() < newLaneNo) {
    4242              :         // recompute shapes on last addition
    4243           67 :         const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4244           67 :         addLane((int)myLanes.size(), recompute, recompute, false);
    4245              :     }
    4246           54 : }
    4247              : 
    4248              : 
    4249              : void
    4250           68 : NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
    4251              :     assert(index < (int)myLanes.size());
    4252           68 :     myLanes.erase(myLanes.begin() + index);
    4253           68 :     if (recompute) {
    4254           16 :         computeLaneShapes();
    4255           16 :         const EdgeVector& incs = myFrom->getIncomingEdges();
    4256           17 :         for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
    4257            1 :             (*i)->invalidateConnections(true);
    4258              :         }
    4259           16 :         invalidateConnections(true);
    4260           52 :     } else if (shiftIndices) {
    4261           44 :         removeFromConnections(nullptr, index, -1, false, true);
    4262          120 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4263           76 :             inc->removeFromConnections(this, -1, index, false, true);
    4264              :         }
    4265              :     }
    4266           68 : }
    4267              : 
    4268              : 
    4269              : void
    4270           42 : NBEdge::decLaneNo(int by) {
    4271           42 :     int newLaneNo = (int) myLanes.size() - by;
    4272              :     assert(newLaneNo > 0);
    4273           66 :     while ((int)myLanes.size() > newLaneNo) {
    4274              :         // recompute shapes on last removal
    4275           24 :         const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
    4276           24 :         deleteLane((int)myLanes.size() - 1, recompute, false);
    4277              :     }
    4278           42 : }
    4279              : 
    4280              : 
    4281              : void
    4282         6279 : NBEdge::markAsInLane2LaneState() {
    4283              :     assert(myTo->getOutgoingEdges().size() == 0);
    4284         6279 :     myStep = EdgeBuildingStep::LANES2LANES_DONE;
    4285         6279 : }
    4286              : 
    4287              : 
    4288              : void
    4289         7879 : NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4290         7879 :     if (lane < 0) { // all lanes are meant...
    4291         7818 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4292         5510 :             allowVehicleClass(i, vclass);
    4293              :         }
    4294              :     } else {
    4295              :         assert(lane < (int)myLanes.size());
    4296         5571 :         myLanes[lane].permissions |= vclass;
    4297              :     }
    4298         7879 : }
    4299              : 
    4300              : 
    4301              : void
    4302        26242 : NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
    4303        26242 :     if (lane < 0) { // all lanes are meant...
    4304        26242 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4305        16180 :             disallowVehicleClass((int) i, vclass);
    4306              :         }
    4307              :     } else {
    4308              :         assert(lane < (int)myLanes.size());
    4309        16180 :         myLanes[lane].permissions &= ~vclass;
    4310              :     }
    4311        26242 : }
    4312              : 
    4313              : 
    4314              : void
    4315           68 : NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
    4316           68 :     if (lane < 0) { // all lanes are meant...
    4317            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4318            0 :             preferVehicleClass(i, vclasses);
    4319              :         }
    4320              :     } else {
    4321              :         assert(lane < (int)myLanes.size());
    4322           68 :         myLanes[lane].permissions |= vclasses;
    4323           68 :         myLanes[lane].preferred |= vclasses;
    4324              :     }
    4325           68 : }
    4326              : 
    4327              : 
    4328              : void
    4329        98164 : NBEdge::setLaneWidth(int lane, double width) {
    4330        98164 :     if (lane < 0) {
    4331              :         // all lanes are meant...
    4332         4917 :         myLaneWidth = width;
    4333         9855 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4334              :             // ... do it for each lane
    4335         4938 :             setLaneWidth(i, width);
    4336              :         }
    4337              :         return;
    4338              :     }
    4339              :     assert(lane < (int)myLanes.size());
    4340        93247 :     myLanes[lane].width = width;
    4341              : }
    4342              : 
    4343              : void
    4344         3972 : NBEdge::setLaneType(int lane, const std::string& type) {
    4345         3972 :     if (lane < 0) {
    4346            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4347              :             // ... do it for each lane
    4348            0 :             setLaneType(i, type);
    4349              :         }
    4350              :         return;
    4351              :     }
    4352              :     assert(lane < (int)myLanes.size());
    4353         3972 :     myLanes[lane].type = type;
    4354              : }
    4355              : 
    4356              : 
    4357              : double
    4358      4383297 : NBEdge::getLaneWidth(int lane) const {
    4359      4383297 :     return myLanes[lane].width != UNSPECIFIED_WIDTH
    4360      4383297 :            ? myLanes[lane].width
    4361      3630646 :            : getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
    4362              : }
    4363              : 
    4364              : double
    4365       109579 : NBEdge::getInternalLaneWidth(
    4366              :     const NBNode& node,
    4367              :     const NBEdge::Connection& connection,
    4368              :     const NBEdge::Lane& successor,
    4369              :     bool isVia) const {
    4370              : 
    4371       109579 :     if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
    4372            3 :         return getLaneWidth(connection.fromLane);
    4373              :     }
    4374              : 
    4375       109576 :     return (isBikepath(getPermissions(connection.fromLane)) && (
    4376       109576 :                 getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
    4377         1017 :            myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
    4378              : }
    4379              : 
    4380              : double
    4381      1055818 : NBEdge::getTotalWidth() const {
    4382              :     double result = 0;
    4383      2434413 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4384      1378595 :         result += getLaneWidth(i);
    4385              :     }
    4386      1055818 :     return result;
    4387              : }
    4388              : 
    4389              : double
    4390        64931 : NBEdge::getEndOffset(int lane) const {
    4391        64931 :     return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
    4392              : }
    4393              : 
    4394              : 
    4395              : const StopOffset&
    4396       726043 : NBEdge::getEdgeStopOffset() const {
    4397       726043 :     return myEdgeStopOffset;
    4398              : }
    4399              : 
    4400              : 
    4401              : const StopOffset&
    4402           24 : NBEdge::getLaneStopOffset(int lane) const {
    4403           24 :     if (lane == -1) {
    4404           12 :         return myEdgeStopOffset;
    4405              :     } else {
    4406           12 :         return myLanes[lane].laneStopOffset;
    4407              :     }
    4408              : }
    4409              : 
    4410              : 
    4411              : void
    4412        82774 : NBEdge::setEndOffset(int lane, double offset) {
    4413        82774 :     if (lane < 0) {
    4414              :         // all lanes are meant...
    4415            3 :         myEndOffset = offset;
    4416            8 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4417              :             // ... do it for each lane
    4418            5 :             setEndOffset(i, offset);
    4419              :         }
    4420              :         return;
    4421              :     }
    4422              :     assert(lane < (int)myLanes.size());
    4423        82771 :     myLanes[lane].endOffset = offset;
    4424              : }
    4425              : 
    4426              : 
    4427              : bool
    4428       147474 : NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
    4429       147474 :     if (lane < 0) {
    4430        64720 :         if (!overwrite && myEdgeStopOffset.isDefined()) {
    4431              :             return false;
    4432              :         }
    4433              :         // all lanes are meant...
    4434        64716 :         if (offset.getOffset() < 0) {
    4435              :             // Edge length unknown at parsing time, thus check here.
    4436            3 :             WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
    4437            1 :             return false;
    4438              :         } else {
    4439        64715 :             myEdgeStopOffset = offset;
    4440              :         }
    4441        82754 :     } else if (lane < (int)myLanes.size()) {
    4442        82754 :         if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
    4443        82748 :             if (offset.getOffset() < 0) {
    4444              :                 // Edge length unknown at parsing time, thus check here.
    4445            0 :                 WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
    4446              :             } else {
    4447        82748 :                 myLanes[lane].laneStopOffset = offset;
    4448              :             }
    4449              :         }
    4450              :     } else {
    4451            0 :         WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
    4452              :     }
    4453              :     return true;
    4454              : }
    4455              : 
    4456              : 
    4457              : void
    4458        87348 : NBEdge::setSpeed(int lane, double speed) {
    4459        87348 :     if (lane < 0) {
    4460              :         // all lanes are meant...
    4461          347 :         mySpeed = speed;
    4462         1094 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4463              :             // ... do it for each lane
    4464          747 :             setSpeed(i, speed);
    4465              :         }
    4466              :         return;
    4467              :     }
    4468              :     assert(lane < (int)myLanes.size());
    4469        87001 :     myLanes[lane].speed = speed;
    4470              : }
    4471              : 
    4472              : 
    4473              : void
    4474        87441 : NBEdge::setFriction(int lane, double friction) {
    4475        87441 :     if (lane < 0) {
    4476              :         // all lanes are meant...
    4477          789 :         myFriction = friction;
    4478         2263 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4479              :             // ... do it for each lane
    4480         1474 :             setFriction(i, friction);
    4481              :         }
    4482              :         return;
    4483              :     }
    4484              :     assert(lane < (int)myLanes.size());
    4485        86652 :     myLanes[lane].friction = friction;
    4486              : }
    4487              : 
    4488              : 
    4489              : void
    4490        81329 : NBEdge::setAcceleration(int lane, bool accelRamp) {
    4491              :     assert(lane >= 0);
    4492              :     assert(lane < (int)myLanes.size());
    4493        81329 :     myLanes[lane].accelRamp = accelRamp;
    4494        81329 : }
    4495              : 
    4496              : 
    4497              : void
    4498           77 : NBEdge::setLaneShape(int lane, const PositionVector& shape) {
    4499              :     assert(lane >= 0);
    4500              :     assert(lane < (int)myLanes.size());
    4501           77 :     myLanes[lane].customShape = shape;
    4502           77 : }
    4503              : 
    4504              : 
    4505              : void
    4506       279438 : NBEdge::setPermissions(SVCPermissions permissions, int lane) {
    4507       279438 :     if (lane < 0) {
    4508       186283 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4509              :             // ... do it for each lane
    4510       103100 :             setPermissions(permissions, i);
    4511              :         }
    4512              :     } else {
    4513              :         assert(lane < (int)myLanes.size());
    4514       196255 :         myLanes[lane].permissions = permissions;
    4515              :     }
    4516       279438 : }
    4517              : 
    4518              : 
    4519              : void
    4520            0 : NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
    4521            0 :     if (lane < 0) {
    4522            0 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4523              :             // ... do it for each lane
    4524            0 :             setPreferredVehicleClass(permissions, i);
    4525              :         }
    4526              :     } else {
    4527              :         assert(lane < (int)myLanes.size());
    4528            0 :         myLanes[lane].preferred = permissions;
    4529              :     }
    4530            0 : }
    4531              : 
    4532              : 
    4533              : void
    4534        81575 : NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
    4535              :     assert(lane >= 0);
    4536              :     assert(lane < (int)myLanes.size());
    4537        81575 :     myLanes[lane].changeLeft = changeLeft;
    4538        81575 :     myLanes[lane].changeRight = changeRight;
    4539        81575 : }
    4540              : 
    4541              : 
    4542              : SVCPermissions
    4543     94207316 : NBEdge::getPermissions(int lane) const {
    4544     94207316 :     if (lane < 0) {
    4545              :         SVCPermissions result = 0;
    4546     85311478 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4547     50865273 :             result |= getPermissions(i);
    4548              :         }
    4549     34446205 :         return result;
    4550              :     } else {
    4551              :         assert(lane < (int)myLanes.size());
    4552     59761111 :         return myLanes[lane].permissions;
    4553              :     }
    4554              : }
    4555              : 
    4556              : 
    4557              : void
    4558        84217 : NBEdge::setLoadedLength(double val) {
    4559        84217 :     myLoadedLength = val;
    4560        84217 : }
    4561              : 
    4562              : void
    4563           10 : NBEdge::setAverageLengthWithOpposite(double val) {
    4564           10 :     myLength = val;
    4565           10 : }
    4566              : 
    4567              : 
    4568              : void
    4569          200 : NBEdge::dismissVehicleClassInformation() {
    4570          647 :     for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
    4571          447 :         (*i).permissions = SVCAll;
    4572          447 :         (*i).preferred = 0;
    4573              :     }
    4574          200 : }
    4575              : 
    4576              : 
    4577              : bool
    4578       406909 : NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
    4579       406909 :     if (c1.fromLane != c2.fromLane) {
    4580        99211 :         return c1.fromLane < c2.fromLane;
    4581              :     }
    4582       307698 :     if (c1.toEdge != c2.toEdge) {
    4583              :         return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
    4584              :     }
    4585         7013 :     return c1.toLane < c2.toLane;
    4586              : }
    4587              : 
    4588              : 
    4589              : double
    4590        10210 : NBEdge::getSignalOffset() const {
    4591              :     if (mySignalPosition == Position::INVALID) {
    4592              :         return UNSPECIFIED_SIGNAL_OFFSET;
    4593              :     } else {
    4594          697 :         Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
    4595          697 :                            myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
    4596              :         //std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
    4597              :         return mySignalPosition.distanceTo2D(laneEnd);
    4598              :     }
    4599              : }
    4600              : 
    4601              : 
    4602              : int
    4603        36851 : NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
    4604              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4605        36851 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4606        36851 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4607        48762 :     for (int i = start; i != end; i += direction) {
    4608              :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4609              :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4610        30224 :         if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
    4611        48752 :                 || ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
    4612        30238 :             return i;
    4613              :         }
    4614              :     }
    4615              :     return -1;
    4616              : }
    4617              : 
    4618              : int
    4619        62335 : NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
    4620              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4621        62335 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4622        62335 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4623        81167 :     for (int i = start; i != end; i += direction) {
    4624              :         // SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
    4625              :         // in the exclusive case, lanes that allow pedestrians along with any other class also count as road
    4626        64469 :         SVCPermissions p = myLanes[i].permissions;
    4627        64469 :         if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
    4628        18832 :                 || (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
    4629        45637 :             return i;
    4630              :         }
    4631              :     }
    4632              :     return -1;
    4633              : }
    4634              : 
    4635              : int
    4636       854895 : NBEdge::getSpecialLane(SVCPermissions permissions) const {
    4637      1947921 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4638      1096768 :         if (myLanes[i].permissions == permissions) {
    4639         3742 :             return i;
    4640              :         }
    4641              :     }
    4642              :     return -1;
    4643              : }
    4644              : 
    4645              : int
    4646      1320742 : NBEdge::getFirstAllowedLaneIndex(int direction) const {
    4647              :     assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
    4648      1320742 :     const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
    4649      1320742 :     const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
    4650      1321166 :     for (int i = start; i != end; i += direction) {
    4651      1320977 :         if (myLanes[i].permissions != 0) {
    4652      1320553 :             return i;
    4653              :         }
    4654              :     }
    4655          189 :     return end - direction;
    4656              : }
    4657              : 
    4658              : 
    4659              : std::set<SVCPermissions>
    4660         5071 : NBEdge::getPermissionVariants(int iStart, int iEnd) const {
    4661              :     std::set<SVCPermissions> result;
    4662         5071 :     if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
    4663            0 :         throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
    4664              :     }
    4665        11370 :     for (int i = iStart; i < iEnd; ++i) {
    4666         6299 :         result.insert(getPermissions(i));
    4667              :     }
    4668         5071 :     return result;
    4669              : }
    4670              : 
    4671              : int
    4672      7992906 : NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
    4673              :     int result = 0;
    4674     20215992 :     for (const Lane& lane : myLanes) {
    4675     12223086 :         if ((allPermissions && (lane.permissions & permissions) == permissions)
    4676       535918 :                 || (!allPermissions && (lane.permissions & permissions) != 0)) {
    4677     10717980 :             result++;
    4678              :         }
    4679              :     }
    4680      7992906 :     return result;
    4681              : }
    4682              : 
    4683              : bool
    4684            0 : NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
    4685              :     assert(lane >= 0 && lane < getNumLanes());
    4686            0 :     return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
    4687              : }
    4688              : 
    4689              : bool
    4690            0 : NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
    4691              :     assert(lane >= 0 && lane < getNumLanes());
    4692            0 :     return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
    4693              : }
    4694              : 
    4695              : double
    4696         5799 : NBEdge::getCrossingAngle(NBNode* node) {
    4697         5799 :     double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
    4698         5799 :     if (angle < 0) {
    4699         1490 :         angle += 360.0;
    4700              :     }
    4701         5799 :     if (angle >= 360) {
    4702            0 :         angle -= 360.0;
    4703              :     }
    4704         5799 :     if (gDebugFlag1) {
    4705            0 :         std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
    4706              :     }
    4707         5799 :     return angle;
    4708              : }
    4709              : 
    4710              : 
    4711              : NBEdge::Lane
    4712            0 : NBEdge::getFirstNonPedestrianLane(int direction) const {
    4713            0 :     int index = getFirstNonPedestrianLaneIndex(direction);
    4714            0 :     if (index < 0) {
    4715            0 :         throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
    4716              :     }
    4717            0 :     return myLanes[index];
    4718              : }
    4719              : 
    4720              : std::string
    4721        18679 : NBEdge::getSidewalkID() {
    4722              :     // see IntermodalEdge::getSidewalk()
    4723        22317 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4724        18732 :         if (myLanes[i].permissions == SVC_PEDESTRIAN) {
    4725        15094 :             return getLaneID(i);
    4726              :         }
    4727              :     }
    4728         3585 :     for (int i = 0; i < (int)myLanes.size(); i++) {
    4729         3585 :         if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
    4730         3585 :             return getLaneID(i);
    4731              :         }
    4732              :     }
    4733            0 :     return getLaneID(0);
    4734              : }
    4735              : 
    4736              : void
    4737         3292 : NBEdge::addSidewalk(double width) {
    4738         3292 :     addRestrictedLane(width, SVC_PEDESTRIAN);
    4739         3292 : }
    4740              : 
    4741              : 
    4742              : void
    4743            0 : NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4744            0 :     restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
    4745            0 : }
    4746              : 
    4747              : 
    4748              : void
    4749          591 : NBEdge::addBikeLane(double width) {
    4750          591 :     addRestrictedLane(width, SVC_BICYCLE);
    4751          591 : }
    4752              : 
    4753              : 
    4754              : void
    4755            0 : NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4756            0 :     restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
    4757            0 : }
    4758              : 
    4759              : bool
    4760         5066 : NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
    4761        12619 :     for (const Lane& lane : myLanes) {
    4762         7823 :         if (lane.permissions == vclass) {
    4763              :             return true;
    4764              :         }
    4765              :     }
    4766              :     return false;
    4767              : }
    4768              : 
    4769              : 
    4770              : void
    4771         4297 : NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
    4772         4297 :     if (hasRestrictedLane(vclass)) {
    4773           12 :         WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
    4774            4 :         return;
    4775              :     }
    4776         4293 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
    4777         1201 :         myGeom.move2side(width / 2);
    4778              :     }
    4779              :     // disallow the designated vclass on all "old" lanes
    4780         4293 :     disallowVehicleClass(-1, vclass);
    4781              :     // don't create a restricted vehicle lane to the right of a sidewalk
    4782         4293 :     const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
    4783              :     if (newIndex == 0) {
    4784              :         // disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
    4785         4252 :         disallowVehicleClass(-1, SVC_PEDESTRIAN);
    4786              :     }
    4787              :     // add new lane
    4788        12879 :     myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
    4789         4293 :     myLanes[newIndex].permissions = vclass;
    4790         4293 :     myLanes[newIndex].width = fabs(width);
    4791              :     // shift outgoing connections to the left
    4792         4320 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4793              :         Connection& c = *it;
    4794           27 :         if (c.fromLane >= newIndex) {
    4795           27 :             c.fromLane += 1;
    4796              :         }
    4797              :     }
    4798              :     // shift incoming connections to the left
    4799         4293 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4800         9433 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4801         5140 :         (*it)->shiftToLanesToEdge(this, 1);
    4802              :     }
    4803         4293 :     myFrom->shiftTLConnectionLaneIndex(this, 1);
    4804         4293 :     myTo->shiftTLConnectionLaneIndex(this, 1);
    4805         4293 :     computeLaneShapes();
    4806              : }
    4807              : 
    4808              : 
    4809              : void
    4810            0 : NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
    4811              :     // check that previously lane was transformed
    4812            0 :     if (myLanes[0].permissions != vclass) {
    4813            0 :         WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
    4814            0 :         return;
    4815              :     }
    4816              :     // restore old values
    4817              :     myGeom = oldGeometry;
    4818            0 :     myLanes = oldLanes;
    4819            0 :     myConnections = oldConnections;
    4820              :     // shift incoming connections to the right
    4821            0 :     const EdgeVector& incoming = myFrom->getIncomingEdges();
    4822            0 :     for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4823            0 :         (*it)->shiftToLanesToEdge(this, 0);
    4824              :     }
    4825              :     // Shift TL conections
    4826            0 :     myFrom->shiftTLConnectionLaneIndex(this, 0);
    4827            0 :     myTo->shiftTLConnectionLaneIndex(this, 0);
    4828            0 :     computeLaneShapes();
    4829              : }
    4830              : 
    4831              : 
    4832              : void
    4833         5140 : NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
    4834              :     /// XXX could we repurpose the function replaceInConnections ?
    4835         5245 :     for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
    4836          105 :         if ((*it).toEdge == to && (*it).toLane >= 0) {
    4837           27 :             (*it).toLane += laneOff;
    4838              :         }
    4839              :     }
    4840         5140 : }
    4841              : 
    4842              : 
    4843              : bool
    4844        67720 : NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
    4845        67720 :     if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
    4846        17157 :             && !isRailway(getPermissions())
    4847        10743 :             && !isRailway(other->getPermissions())
    4848        78433 :             && getBidiEdge() == nullptr) {
    4849        10697 :         const int i = (node == myTo ? -1 : 0);
    4850        10697 :         const int i2 = (node == myTo ? 0 : -1);
    4851        10697 :         const double dist = myGeom[i].distanceTo2D(node->getPosition());
    4852        10697 :         const double neededOffset = getTotalWidth() / 2;
    4853        10697 :         const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
    4854        10697 :                                   other->getGeometry().distance2D(myGeom[i]));
    4855        10697 :         const double neededOffset2 = neededOffset + (other->getLaneSpreadFunction() == LaneSpreadFunction::CENTER
    4856        10697 :                 ? (other->getTotalWidth()) / 2 : 0);
    4857        10697 :         const double missing = neededOffset - dist;
    4858        10697 :         const double missing2 = neededOffset2 - dist2;
    4859              :         double shift = 0;
    4860        10697 :         if (missing > 0 && missing2 > 0) {
    4861              :             shift = MIN2(missing, missing2);
    4862         2028 :         } else if (missing2) {
    4863              :             shift = missing2;
    4864              :         }
    4865        10697 :         if (shift > 0) {
    4866              :             PositionVector tmp = myGeom;
    4867              :             try {
    4868         8772 :                 tmp.move2side(shift);
    4869         8772 :                 tmp[i].round(gPrecision);
    4870         8772 :                 myGeom[i] = tmp[i];
    4871         8772 :                 computeAngle();
    4872              :                 return true;
    4873              :                 //std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << "  by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
    4874            0 :             } catch (InvalidArgument&) {
    4875            0 :                 WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
    4876            0 :             }
    4877         8772 :         }
    4878              :     }
    4879              :     return false;
    4880              : }
    4881              : 
    4882              : 
    4883              : Position
    4884           93 : NBEdge::geometryPositionAtOffset(double offset) const {
    4885           93 :     if (myLoadedLength > 0) {
    4886            2 :         return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
    4887              :     } else {
    4888           91 :         return myGeom.positionAtOffset(offset);
    4889              :     }
    4890              : }
    4891              : 
    4892              : 
    4893              : double
    4894       138340 : NBEdge::getFinalLength() const {
    4895              :     double result = getLoadedLength();
    4896       276680 :     if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
    4897              :         // use length to junction center even if a modified geometry was given
    4898        76331 :         PositionVector geom = cutAtIntersection(myGeom);
    4899        76331 :         geom.push_back_noDoublePos(getToNode()->getCenter());
    4900        76331 :         geom.push_front_noDoublePos(getFromNode()->getCenter());
    4901        76331 :         result = geom.length();
    4902        76331 :     }
    4903              :     double avgEndOffset = 0;
    4904       313331 :     for (const Lane& lane : myLanes) {
    4905       174991 :         avgEndOffset += lane.endOffset;
    4906              :     }
    4907       138340 :     if (isBidiRail()) {
    4908        13428 :         avgEndOffset += myPossibleTurnDestination->getEndOffset();
    4909              :     }
    4910       138340 :     avgEndOffset /= (double)myLanes.size();
    4911       138344 :     return MAX2(result - avgEndOffset, POSITION_EPS);
    4912              : }
    4913              : 
    4914              : 
    4915              : void
    4916          965 : NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
    4917          965 :     if (laneIdx == -1) {
    4918          908 :         for (int i = 0; i < (int)myLanes.size(); i++) {
    4919          986 :             setOrigID(origID, append, i);
    4920              :         }
    4921              :     } else {
    4922          550 :         if (origID != "") {
    4923          550 :             if (append) {
    4924          114 :                 std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
    4925           57 :                 if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
    4926           57 :                     oldIDs.push_back(origID);
    4927              :                 }
    4928           57 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
    4929           57 :             } else {
    4930          493 :                 myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
    4931              :             }
    4932              :         } else {
    4933              :             // do not record empty origID parameter
    4934            0 :             myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
    4935              :         }
    4936              :     }
    4937          965 : }
    4938              : 
    4939              : 
    4940              : const EdgeVector&
    4941          570 : NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
    4942              :     // @todo cache successors instead of recomputing them every time
    4943              :     mySuccessors.clear();
    4944              :     //std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
    4945         3022 :     for (const Connection& con : myConnections) {
    4946         2414 :         if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
    4947          159 :                 (vClass == SVC_IGNORING || (getPermissions(con.fromLane)
    4948          159 :                                             & con.toEdge->getPermissions(con.toLane) & vClass) != 0)
    4949         4866 :                 && std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
    4950         1642 :             mySuccessors.push_back(con.toEdge);
    4951              :             //std::cout << "   succ=" << con.toEdge->getID() << "\n";
    4952              :         }
    4953              :     }
    4954          570 :     return mySuccessors;
    4955              : }
    4956              : 
    4957              : 
    4958              : const ConstRouterEdgePairVector&
    4959         4283 : NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
    4960              :     // @todo cache successors instead of recomputing them every time
    4961              :     myViaSuccessors.clear();
    4962         9297 :     for (const Connection& con : myConnections) {
    4963              :         std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
    4964              :         // special case for Persons in Netedit
    4965         5014 :         if (vClass == SVC_PEDESTRIAN) {
    4966            0 :             myViaSuccessors.push_back(pair);    // Pedestrians have complete freedom of movement in all sucessors
    4967         5014 :         } else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
    4968         5014 :                    (con.toEdge != nullptr) &&
    4969         5014 :                    ((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
    4970              :             // ignore duplicates
    4971         4864 :             if (con.getLength() > 0) {
    4972              :                 pair.second = &con;
    4973              :             }
    4974         9728 :             myViaSuccessors.push_back(pair);
    4975              :         }
    4976              :     }
    4977         4283 :     return myViaSuccessors;
    4978              : }
    4979              : 
    4980              : 
    4981              : void
    4982            0 : NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
    4983            0 :     if (outgoing) {
    4984            0 :         for (const Connection& c : myConnections) {
    4985            0 :             std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4986              :         }
    4987              :     }
    4988            0 :     if (incoming) {
    4989            0 :         for (NBEdge* inc : myFrom->getIncomingEdges()) {
    4990            0 :             for (Connection& c : inc->myConnections) {
    4991            0 :                 if (c.toEdge == this) {
    4992            0 :                     std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
    4993              :                 }
    4994              :             }
    4995              :         }
    4996              :     }
    4997            0 : }
    4998              : 
    4999              : 
    5000              : int
    5001           99 : NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
    5002          198 :     return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
    5003              : }
    5004              : 
    5005              : bool
    5006           36 : NBEdge::joinLanes(SVCPermissions perms) {
    5007              :     bool haveJoined = false;
    5008              :     int i = 0;
    5009          181 :     while (i < getNumLanes() - 1) {
    5010          145 :         if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
    5011           37 :             const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
    5012           37 :             const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
    5013           37 :             deleteLane(i, false, true);
    5014           37 :             setLaneWidth(i, newWidth);
    5015           37 :             setLaneType(i, newType);
    5016              :             haveJoined = true;
    5017              :         } else {
    5018          108 :             i++;
    5019              :         }
    5020              :     }
    5021           36 :     return haveJoined;
    5022              : }
    5023              : 
    5024              : 
    5025              : EdgeVector
    5026        62274 : NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
    5027              :     EdgeVector result;
    5028       268000 :     for (NBEdge* edge : edges) {
    5029       205726 :         if ((edge->getPermissions() & permissions) != 0) {
    5030       190545 :             result.push_back(edge);
    5031              :         }
    5032              :     }
    5033        62274 :     return result;
    5034            0 : }
    5035              : 
    5036              : NBEdge*
    5037         2926 : NBEdge::getStraightContinuation(SVCPermissions permissions) const {
    5038         2926 :     EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
    5039         2926 :     if (cands.size() == 0) {
    5040              :         return nullptr;
    5041              :     }
    5042         2922 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
    5043         2922 :     NBEdge* best = cands.front();
    5044         2922 :     if (isTurningDirectionAt(best)) {
    5045              :         return nullptr;
    5046              :     } else {
    5047              :         return best;
    5048              :     }
    5049         2926 : }
    5050              : 
    5051              : NBEdge*
    5052          186 : NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
    5053          186 :     EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
    5054          186 :     if (cands.size() == 0) {
    5055              :         return nullptr;
    5056              :     }
    5057          174 :     sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
    5058          174 :     NBEdge* best = cands.front();
    5059          174 :     if (best->isTurningDirectionAt(this)) {
    5060              :         return nullptr;
    5061              :     } else {
    5062              :         return best;
    5063              :     }
    5064          186 : }
    5065              : 
    5066              : 
    5067              : NBEdge*
    5068           47 : NBEdge::guessOpposite(bool reguess) {
    5069              :     NBEdge* opposite = nullptr;
    5070           47 :     if (getNumLanes() > 0) {
    5071              :         NBEdge::Lane& lastLane = myLanes.back();
    5072           47 :         const double lastWidth = getLaneWidth(getNumLanes() - 1);
    5073           47 :         if (lastLane.oppositeID == "" || reguess) {
    5074           76 :             for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
    5075           44 :                 if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
    5076              :                     const NBEdge::Lane& candLastLane = cand->getLanes().back();
    5077           48 :                     if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
    5078           30 :                         const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
    5079              :                         // in sharp corners, the difference may be higher
    5080              :                         // factor (sqrt(2) for 90 degree corners
    5081           30 :                         const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
    5082           60 :                         const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
    5083              :                         //std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
    5084           30 :                         if (distance < threshold) {
    5085              :                             opposite = cand;
    5086              :                         }
    5087              :                     }
    5088              :                 }
    5089              :             }
    5090           32 :             if (opposite != nullptr) {
    5091           56 :                 lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
    5092              :             }
    5093              :         }
    5094              :     }
    5095           47 :     return opposite;
    5096              : }
    5097              : 
    5098              : double
    5099            0 : NBEdge::getDistancAt(double pos) const {
    5100              :     // negative values of myDistances indicate descending kilometrage
    5101            0 :     return fabs(myDistance + pos);
    5102              : }
    5103              : 
    5104              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1