LCOV - code coverage report
Current view: top level - src/netbuild - NBNode.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.0 % 1939 1783
Test Date: 2026-07-26 16:30:20 Functions: 91.9 % 136 125

            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    NBNode.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @date    Tue, 20 Nov 2001
      20              : ///
      21              : // The representation of a single node
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <map>
      27              : #include <cassert>
      28              : #include <algorithm>
      29              : #include <vector>
      30              : #include <deque>
      31              : #include <set>
      32              : #include <cmath>
      33              : #include <iterator>
      34              : #include <utils/common/UtilExceptions.h>
      35              : #include <utils/common/StringUtils.h>
      36              : #include <utils/options/OptionsCont.h>
      37              : #include <utils/geom/GeomHelper.h>
      38              : #include <utils/common/MsgHandler.h>
      39              : #include <utils/common/StdDefs.h>
      40              : #include <utils/common/ToString.h>
      41              : #include <utils/geom/GeoConvHelper.h>
      42              : #include <utils/iodevices/OutputDevice.h>
      43              : #include <iomanip>
      44              : #include "NBNode.h"
      45              : #include "NBAlgorithms.h"
      46              : #include "NBNodeCont.h"
      47              : #include "NBNodeShapeComputer.h"
      48              : #include "NBEdgeCont.h"
      49              : #include "NBTypeCont.h"
      50              : #include "NBHelpers.h"
      51              : #include "NBDistrict.h"
      52              : #include "NBContHelper.h"
      53              : #include "NBRequest.h"
      54              : #include "NBOwnTLDef.h"
      55              : #include "NBLoadedSUMOTLDef.h"
      56              : #include "NBTrafficLightLogicCont.h"
      57              : #include "NBTrafficLightDefinition.h"
      58              : 
      59              : // allow to extend a crossing across multiple edges
      60              : #define EXTEND_CROSSING_ANGLE_THRESHOLD 35.0 // degrees
      61              : // create intermediate walking areas if either of the following thresholds is exceeded
      62              : #define SPLIT_CROSSING_WIDTH_THRESHOLD 1.5 // meters
      63              : #define SPLIT_CROSSING_ANGLE_THRESHOLD 5 // degrees
      64              : 
      65              : // minimum length for a weaving section at a combined on-off ramp
      66              : #define MIN_WEAVE_LENGTH 20.0
      67              : 
      68              : //#define DEBUG_CONNECTION_GUESSING
      69              : //#define DEBUG_SMOOTH_GEOM
      70              : //#define DEBUG_PED_STRUCTURES
      71              : //#define DEBUG_EDGE_SORTING
      72              : //#define DEBUG_CROSSING_OUTLINE
      73              : //#define DEBUGCOND true
      74              : #define DEBUG_NODE_ID "C"
      75              : #define DEBUGCOND (getID() == DEBUG_NODE_ID)
      76              : #define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUG_NODE_ID))
      77              : #ifdef DEBUG_PED_STRUCTURES
      78              : #define DEBUGCOUT(cond, msg) DEBUGOUT(cond, msg)
      79              : #else
      80              : #define DEBUGCOUT(cond, msg)
      81              : #endif
      82              : 
      83              : // ===========================================================================
      84              : // static members
      85              : // ===========================================================================
      86              : const int NBNode::FORWARD(1);
      87              : const int NBNode::BACKWARD(-1);
      88              : const double NBNode::UNSPECIFIED_RADIUS = -1;
      89              : const int NBNode::AVOID_WIDE_LEFT_TURN(1);
      90              : const int NBNode::AVOID_WIDE_RIGHT_TURN(2);
      91              : const int NBNode::FOUR_CONTROL_POINTS(4);
      92              : const int NBNode::AVOID_INTERSECTING_LEFT_TURNS(8);
      93              : const int NBNode::SCURVE_IGNORE(16);
      94              : const int NBNode::INDIRECT_LEFT(32);
      95              : 
      96              : SVCPermissions NBNode::myHaveRailSignalClasses;
      97              : SVCPermissions NBNode::myPermitUnsignalizedClasses;
      98              : 
      99              : // ===========================================================================
     100              : // method definitions
     101              : // ===========================================================================
     102              : /* -------------------------------------------------------------------------
     103              :  * NBNode::ApproachingDivider-methods
     104              :  * ----------------------------------------------------------------------- */
     105        92569 : NBNode::ApproachingDivider::ApproachingDivider(
     106        92569 :     const EdgeVector& approaching, NBEdge* currentOutgoing) :
     107        92569 :     myApproaching(approaching),
     108        92569 :     myCurrentOutgoing(currentOutgoing),
     109        92569 :     myNumStraight(0),
     110        92569 :     myIsBikeEdge(currentOutgoing->getPermissions() == SVC_BICYCLE),
     111       184871 :     myIsBusEdge((currentOutgoing->getPermissions() & SVC_BUS) != 0 && (currentOutgoing->getPermissions() & ~(SVC_BUS | SVC_VULNERABLE)) == 0)
     112              : {
     113              :     // collect lanes which are expliclity targeted
     114              :     std::set<int> approachedLanes;
     115              :     bool hasIncomingBusLane = false;
     116       267340 :     for (const NBEdge* const approachingEdge : myApproaching) {
     117       644993 :         for (const NBEdge::Connection& con : approachingEdge->getConnections()) {
     118       470222 :             if (con.toEdge == myCurrentOutgoing) {
     119       197205 :                 approachedLanes.insert(con.toLane);
     120              :             }
     121              :         }
     122       174771 :         myDirections.push_back(approachingEdge->getToNode()->getDirection(approachingEdge, currentOutgoing));
     123       174771 :         if (myDirections.back() == LinkDirection::STRAIGHT) {
     124        74927 :             myNumStraight++;
     125              :         }
     126       174771 :         hasIncomingBusLane |= (approachingEdge->getSpecialLane(SVC_BUS) != -1);
     127              :     }
     128              :     // compute the indices of lanes that should be targeted (excluding pedestrian
     129              :     // lanes that will be connected from walkingAreas and forbidden lanes)
     130              :     // if the lane is targeted by an explicitly set connection we need
     131              :     // to make it available anyway
     132       213140 :     for (int i = 0; i < currentOutgoing->getNumLanes(); ++i) {
     133       120571 :         const SVCPermissions lp = currentOutgoing->getPermissions(i);
     134         5159 :         if ((lp == SVC_PEDESTRIAN
     135              :                 // don't consider bicycle lanes as targets unless the target
     136              :                 // edge is exclusively for bicycles
     137       113972 :                 || (lp == SVC_BICYCLE && !myIsBikeEdge)
     138       113328 :                 || (lp == SVC_BUS && hasIncomingBusLane)
     139       113302 :                 || isForbidden(lp))
     140       120571 :                 && approachedLanes.count(i) == 0) {
     141         5159 :             continue;
     142              :         }
     143       115412 :         myAvailableLanes.push_back(i);
     144              :     }
     145        92569 : }
     146              : 
     147              : 
     148        92569 : NBNode::ApproachingDivider::~ApproachingDivider() {}
     149              : 
     150              : 
     151              : void
     152       186051 : NBNode::ApproachingDivider::execute(const int src, const int dest) {
     153              :     assert((int)myApproaching.size() > src);
     154              :     // get the origin edge
     155       186051 :     NBEdge* incomingEdge = myApproaching[src];
     156       186051 :     if (incomingEdge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_DONE || incomingEdge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     157        96218 :         return;
     158              :     }
     159        89967 :     if (myAvailableLanes.size() == 0) {
     160              :         return;
     161              :     }
     162        89877 :     const bool withBikes = myIsBikeEdge || incomingEdge->getPermissions() == SVC_BICYCLE;
     163        89877 :     std::vector<int> approachingLanes = incomingEdge->getConnectionLanes(myCurrentOutgoing, withBikes, true);
     164       179687 :     if (approachingLanes.size() > myAvailableLanes.size() ||
     165        89941 :             (incomingEdge->getSpecialLane(SVC_BUS) >= 0 && myCurrentOutgoing->getSpecialLane(SVC_BUS) >= 0)) {
     166           77 :         const bool withBusLanes = myIsBusEdge || ((incomingEdge->getPermissions() & SVC_BUS) != 0 && (incomingEdge->getPermissions() & ~(SVC_BUS | SVC_VULNERABLE)) == 0);
     167           77 :         approachingLanes = incomingEdge->getConnectionLanes(myCurrentOutgoing, withBikes, withBusLanes);
     168              :     }
     169        89877 :     if (approachingLanes.size() == 0) {
     170              :         return;
     171              :     }
     172              : #ifdef DEBUG_CONNECTION_GUESSING
     173              :     if (DEBUGCOND2(incomingEdge->getToNode())) {
     174              :         std::cout << "Bre:ex src=" << src << " dest=" << dest << " in=" << incomingEdge->getID() << " apLanes=" << toString(approachingLanes) << "\n";
     175              :     }
     176              : 
     177              : #endif
     178        89833 :     int numConnections = (int)approachingLanes.size();
     179              :     double factor = 1;
     180              :     const bool rightOnRed = incomingEdge->getToNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED;
     181        89833 :     if (myNumStraight == 1 && myDirections[src] == LinkDirection::STRAIGHT && (
     182              :                 // we do not want to destroy ramp-like assignments where the
     183              :                 // on-connection-per-lane rule avoids conflicts
     184              :                 // - at a traffic light the phases are seperated so there is no conflict anyway
     185         3738 :                 (incomingEdge->getToNode()->isTLControlled() && !rightOnRed)
     186              :                 // - there are no incoming edges to the right
     187        31848 :                 || src == 0
     188              :                 // - a minor straight road is likely in conflict anyway
     189        16207 :                 || (incomingEdge->getJunctionPriority(incomingEdge->getToNode()) == NBEdge::MINOR_ROAD && !rightOnRed))) {
     190        26511 :         numConnections = (int)myAvailableLanes.size();
     191        26511 :         factor = (double)approachingLanes.size() / (double)numConnections;
     192        26511 :         if (factor > 0.5) {
     193              :             factor = 1;
     194              :         }
     195              :     }
     196        89833 :     std::deque<int>* approachedLanes = spread(numConnections, dest);
     197              :     assert(approachedLanes->size() <= myAvailableLanes.size());
     198              :     // set lanes
     199        89833 :     const int maxFrom = (int)approachingLanes.size() - 1;
     200       192631 :     for (int i = 0; i < (int)approachedLanes->size(); i++) {
     201              :         // distribute i evenly on approaching lanes in case we are building more
     202              :         // connections than there are lanes
     203       102798 :         int fromLane = approachingLanes[MIN2((int)(i * factor), maxFrom)];
     204       102798 :         int approached = myAvailableLanes[(*approachedLanes)[i]];
     205       205596 :         incomingEdge->setConnection(fromLane, myCurrentOutgoing, approached, NBEdge::Lane2LaneInfoType::COMPUTED);
     206              :     }
     207        89833 :     delete approachedLanes;
     208        89877 : }
     209              : 
     210              : 
     211              : std::deque<int>*
     212        89833 : NBNode::ApproachingDivider::spread(int numLanes, int dest) const {
     213        89833 :     std::deque<int>* ret = new std::deque<int>();
     214              :     // when only one lane is approached, we check, whether the double-value
     215              :     //  is assigned more to the left or right lane
     216        89833 :     if (numLanes == 1) {
     217              :         ret->push_back(dest);
     218        80466 :         return ret;
     219              :     }
     220              : 
     221         9367 :     const int numOutgoingLanes = (int)myAvailableLanes.size();
     222              :     //
     223              :     ret->push_back(dest);
     224              :     int noSet = 1;
     225              :     int roffset = 1;
     226              :     int loffset = 1;
     227        13056 :     while (noSet < numLanes) {
     228              :         // It may be possible, that there are not enough lanes the source
     229              :         //  lanes may be divided on
     230              :         //  In this case, they remain unset
     231              :         //  !!! this is only a hack. It is possible, that this yields in
     232              :         //   uncommon divisions
     233         9714 :         if (numOutgoingLanes == noSet) {
     234              :             return ret;
     235              :         }
     236              : 
     237              :         // as due to the conversion of double->uint the numbers will be lower
     238              :         //  than they should be, we try to append to the left side first
     239              :         //
     240              :         // check whether the left boundary of the approached street has
     241              :         //  been overridden; if so, move all lanes to the right
     242         9711 :         if (dest + loffset >= numOutgoingLanes) {
     243         4907 :             loffset -= 1;
     244         4907 :             roffset += 1;
     245        10128 :             for (int i = 0; i < (int)ret->size(); i++) {
     246         5221 :                 (*ret)[i] = (*ret)[i] - 1;
     247              :             }
     248              :         }
     249              :         // append the next lane to the left of all edges
     250              :         //  increase the position (destination edge)
     251         9711 :         ret->push_back(dest + loffset);
     252         9711 :         noSet++;
     253         9711 :         loffset += 1;
     254              : 
     255              :         // as above
     256         9711 :         if (numOutgoingLanes == noSet) {
     257              :             return ret;
     258              :         }
     259              : 
     260              :         // now we try to append the next lane to the right side, when needed
     261         3689 :         if (noSet < numLanes) {
     262              :             // check whether the right boundary of the approached street has
     263              :             //  been overridden; if so, move all lanes to the right
     264         3254 :             if (dest < roffset) {
     265          764 :                 loffset += 1;
     266          764 :                 roffset -= 1;
     267         2344 :                 for (int i = 0; i < (int)ret->size(); i++) {
     268         1580 :                     (*ret)[i] = (*ret)[i] + 1;
     269              :                 }
     270              :             }
     271         3254 :             ret->push_front(dest - roffset);
     272         3254 :             noSet++;
     273         3254 :             roffset += 1;
     274              :         }
     275              :     }
     276              :     return ret;
     277              : }
     278              : 
     279              : 
     280              : /* -------------------------------------------------------------------------
     281              :  * NBNode::Crossing-methods
     282              :  * ----------------------------------------------------------------------- */
     283         3305 : NBNode::Crossing::Crossing(const NBNode* _node, const EdgeVector& _edges, double _width, bool _priority, int _customTLIndex, int _customTLIndex2, const PositionVector& _customShape) :
     284              :     Parameterised(),
     285         3305 :     node(_node),
     286         3305 :     edges(_edges),
     287         3305 :     customWidth(_width),
     288         3305 :     width(_width),
     289         3305 :     priority(_priority),
     290              :     customShape(_customShape),
     291         3305 :     tlLinkIndex(_customTLIndex),
     292         3305 :     tlLinkIndex2(_customTLIndex2),
     293         3305 :     customTLIndex(_customTLIndex),
     294         3305 :     customTLIndex2(_customTLIndex2),
     295         3305 :     valid(true) {
     296         3305 : }
     297              : 
     298              : 
     299              : /* -------------------------------------------------------------------------
     300              :  * NBNode-methods
     301              :  * ----------------------------------------------------------------------- */
     302        51492 : NBNode::NBNode(const std::string& id, const Position& position,
     303        51492 :                SumoXMLNodeType type) :
     304        51492 :     Named(StringUtils::convertUmlaute(id)),
     305        51492 :     myPosition(position),
     306        51492 :     myType(type),
     307        51492 :     myDistrict(nullptr),
     308        51492 :     myHaveCustomPoly(false),
     309        51492 :     myRequest(nullptr),
     310        51492 :     myRadius(UNSPECIFIED_RADIUS),
     311        51492 :     myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
     312        51494 :     myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
     313        51492 :     myFringeType(FringeType::DEFAULT),
     314        51492 :     myRoundaboutType(RoundaboutType::DEFAULT),
     315        51492 :     myDiscardAllCrossings(false),
     316        51492 :     myCrossingsLoadedFromSumoNet(0),
     317        51492 :     myDisplacementError(0),
     318        51492 :     myIsBentPriority(false),
     319        51492 :     myTypeWasGuessed(false) {
     320        51492 :     if (!SUMOXMLDefinitions::isValidNetID(myID)) {
     321            3 :         throw ProcessError(TLF("Invalid node id '%'.", myID));
     322              :     }
     323              :     if (myPosition.isNAN()) {
     324            3 :         throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
     325              :     }
     326        51508 : }
     327              : 
     328              : 
     329        34373 : NBNode::NBNode(const std::string& id, const Position& position, NBDistrict* district) :
     330        34373 :     Named(StringUtils::convertUmlaute(id)),
     331        34373 :     myPosition(position),
     332        34373 :     myType(district == nullptr ? SumoXMLNodeType::UNKNOWN : SumoXMLNodeType::DISTRICT),
     333        34373 :     myDistrict(district),
     334        34373 :     myHaveCustomPoly(false),
     335        34373 :     myRequest(nullptr),
     336        34373 :     myRadius(UNSPECIFIED_RADIUS),
     337        34373 :     myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
     338        34373 :     myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
     339        34373 :     myFringeType(FringeType::DEFAULT),
     340        34373 :     myRoundaboutType(RoundaboutType::DEFAULT),
     341        34373 :     myDiscardAllCrossings(false),
     342        34373 :     myCrossingsLoadedFromSumoNet(0),
     343        34373 :     myDisplacementError(0),
     344        34373 :     myIsBentPriority(false),
     345        34373 :     myTypeWasGuessed(false) {
     346        34373 :     if (!SUMOXMLDefinitions::isValidNetID(myID)) {
     347            0 :         throw ProcessError(TLF("Invalid node id '%'.", myID));
     348              :     }
     349              :     if (myPosition.isNAN()) {
     350            0 :         throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
     351              :     }
     352        34373 : }
     353              : 
     354              : 
     355       171726 : NBNode::~NBNode() {
     356        85863 :     delete myRequest;
     357       343452 : }
     358              : 
     359              : 
     360              : void
     361         4495 : NBNode::reinit(const Position& position, SumoXMLNodeType type,
     362              :                bool updateEdgeGeometries) {
     363         4495 :     myPosition = position;
     364              :     if (myPosition.isNAN()) {
     365            0 :         throw ProcessError(TLF("Invalid position '%' for node '%'", myPosition, myID));
     366              :     }
     367              :     // patch type
     368         4495 :     myType = type;
     369         4495 :     if (!isTrafficLight(myType)) {
     370         4227 :         removeTrafficLights();
     371              :     }
     372         4495 :     if (updateEdgeGeometries) {
     373         3269 :         for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
     374         1926 :             PositionVector geom = (*i)->getGeometry();
     375         1926 :             geom[-1] = myPosition;
     376         1926 :             (*i)->setGeometry(geom);
     377         1926 :         }
     378         3275 :         for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
     379         1932 :             PositionVector geom = (*i)->getGeometry();
     380         1932 :             geom[0] = myPosition;
     381         1932 :             (*i)->setGeometry(geom);
     382         1932 :         }
     383              :     }
     384         4495 : }
     385              : 
     386              : 
     387              : 
     388              : // -----------  Applying offset
     389              : void
     390        38579 : NBNode::reshiftPosition(double xoff, double yoff) {
     391              :     myPosition.add(xoff, yoff, 0);
     392        38579 :     myPoly.add(xoff, yoff, 0);
     393        38586 :     for (auto& wacs : myWalkingAreaCustomShapes) {
     394            7 :         wacs.shape.add(xoff, yoff, 0);
     395              :     }
     396        38884 :     for (auto& c : myCrossings) {
     397          305 :         c->customShape.add(xoff, yoff, 0);
     398              :     }
     399        38579 : }
     400              : 
     401              : 
     402              : void
     403        70690 : NBNode::roundGeometry() {
     404        70690 :     myPosition.round(gPrecision);
     405        70690 :     if (myHaveCustomPoly) {
     406           52 :         myPoly.round(gPrecision);
     407              :     }
     408        70701 :     for (auto& wacs : myWalkingAreaCustomShapes) {
     409           11 :         wacs.shape.round(gPrecision);
     410              :     }
     411        72382 :     for (auto& c : myCrossings) {
     412         1692 :         c->customShape.round(gPrecision);
     413              :     }
     414        70690 : }
     415              : 
     416              : 
     417              : void
     418          829 : NBNode::mirrorX() {
     419              :     myPosition.mul(1, -1);
     420          829 :     myPoly.mirrorX();
     421              :     // mirror pre-computed geometry of crossings and walkingareas
     422          861 :     for (auto& c : myCrossings) {
     423           32 :         c->customShape.mirrorX();
     424           32 :         c->shape.mirrorX();
     425              :     }
     426          861 :     for (auto& wa : myWalkingAreas) {
     427           32 :         wa.shape.mirrorX();
     428              :     }
     429          831 :     for (auto& wacs : myWalkingAreaCustomShapes) {
     430            2 :         wacs.shape.mirrorX();
     431              :     }
     432          829 : }
     433              : 
     434              : 
     435              : // -----------  Methods for dealing with assigned traffic lights
     436              : void
     437        31889 : NBNode::addTrafficLight(NBTrafficLightDefinition* tlDef) {
     438              :     myTrafficLights.insert(tlDef);
     439              :     // rail signals receive a temporary traffic light in order to set connection tl-linkIndex
     440        31889 :     if (!isTrafficLight(myType) && myType != SumoXMLNodeType::RAIL_SIGNAL && myType != SumoXMLNodeType::RAIL_CROSSING) {
     441         2747 :         myType = SumoXMLNodeType::TRAFFIC_LIGHT;
     442              :     }
     443        31889 : }
     444              : 
     445              : 
     446              : void
     447         6599 : NBNode::removeTrafficLight(NBTrafficLightDefinition* tlDef) {
     448         6599 :     tlDef->removeNode(this);
     449              :     myTrafficLights.erase(tlDef);
     450         6599 : }
     451              : 
     452              : 
     453              : void
     454        18977 : NBNode::removeTrafficLights(bool setAsPriority) {
     455              :     std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
     456        19974 :     for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
     457          997 :         removeTrafficLight(*i);
     458              :     }
     459        18977 :     if (setAsPriority) {
     460           24 :         myType = myRequest != nullptr ? SumoXMLNodeType::PRIORITY : (
     461            3 :                      myType == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION ? SumoXMLNodeType::NOJUNCTION : SumoXMLNodeType::DEAD_END);
     462              :     }
     463        18977 : }
     464              : 
     465              : bool
     466         3741 : NBNode::hadSignal() const {
     467        12052 :     for (NBEdge* e : getIncomingEdges()) {
     468              :         if (e->getSignalPosition() != Position::INVALID) {
     469              :             return true;
     470              :         }
     471              :     }
     472              :     return false;
     473              : }
     474              : 
     475              : 
     476              : void
     477         1575 : NBNode::invalidateTLS(NBTrafficLightLogicCont& tlCont, bool addedConnections, bool removedConnections) {
     478         1575 :     if (isTLControlled()) {
     479              :         std::set<NBTrafficLightDefinition*> oldDefs(myTrafficLights);
     480         1378 :         for (std::set<NBTrafficLightDefinition*>::iterator it = oldDefs.begin(); it != oldDefs.end(); ++it) {
     481          689 :             NBTrafficLightDefinition* orig = *it;
     482          689 :             if (dynamic_cast<NBLoadedSUMOTLDef*>(orig) != nullptr) {
     483           12 :                 dynamic_cast<NBLoadedSUMOTLDef*>(orig)->registerModifications(addedConnections, removedConnections);
     484          677 :             } else if (dynamic_cast<NBOwnTLDef*>(orig) == nullptr) {
     485            0 :                 NBTrafficLightDefinition* newDef = new NBOwnTLDef(orig->getID(), orig->getOffset(), orig->getType());
     486              :                 const std::vector<NBNode*>& nodes = orig->getNodes();
     487            0 :                 while (!nodes.empty()) {
     488            0 :                     newDef->addNode(nodes.front());
     489            0 :                     nodes.front()->removeTrafficLight(orig);
     490              :                 }
     491            0 :                 tlCont.removeFully(orig->getID());
     492            0 :                 tlCont.insert(newDef);
     493              :             }
     494              :         }
     495              :     }
     496         1575 : }
     497              : 
     498              : 
     499              : void
     500         8586 : NBNode::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
     501        10313 :     for (std::set<NBTrafficLightDefinition*>::iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
     502         1727 :         (*it)->shiftTLConnectionLaneIndex(edge, offset, threshold);
     503              :     }
     504         8586 : }
     505              : 
     506              : // ----------- Prunning the input
     507              : int
     508        84086 : NBNode::removeSelfLoops(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tc) {
     509              :     int ret = 0;
     510              :     int pos = 0;
     511              :     EdgeVector::const_iterator j = myIncomingEdges.begin();
     512       219493 :     while (j != myIncomingEdges.end()) {
     513              :         // skip edges which are only incoming and not outgoing
     514       135407 :         if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), *j) == myOutgoingEdges.end()) {
     515              :             ++j;
     516       135407 :             ++pos;
     517       135407 :             continue;
     518              :         }
     519              :         // an edge with both its origin and destination being the current
     520              :         //  node should be removed
     521            0 :         NBEdge* dummy = *j;
     522            0 :         WRITE_WARNINGF(TL(" Removing self-looping edge '%'"), dummy->getID());
     523              :         // get the list of incoming edges connected to the self-loop
     524            0 :         EdgeVector incomingConnected = dummy->getIncomingEdges();
     525              :         // get the list of outgoing edges connected to the self-loop
     526            0 :         EdgeVector outgoingConnected = dummy->getConnectedEdges();
     527              :         // let the self-loop remap its connections
     528            0 :         dummy->remapConnections(incomingConnected);
     529            0 :         remapRemoved(tc, dummy, incomingConnected, outgoingConnected);
     530              :         // delete the self-loop
     531            0 :         ec.erase(dc, dummy);
     532              :         j = myIncomingEdges.begin() + pos;
     533            0 :         ++ret;
     534            0 :     }
     535        84086 :     return ret;
     536              : }
     537              : 
     538              : 
     539              : // -----------
     540              : void
     541       157143 : NBNode::addIncomingEdge(NBEdge* edge) {
     542              :     assert(edge != 0);
     543       157143 :     if (find(myIncomingEdges.begin(), myIncomingEdges.end(), edge) == myIncomingEdges.end()) {
     544       154903 :         myIncomingEdges.push_back(edge);
     545       154903 :         myAllEdges.push_back(edge);
     546              :     }
     547       157143 : }
     548              : 
     549              : 
     550              : void
     551       157317 : NBNode::addOutgoingEdge(NBEdge* edge) {
     552              :     assert(edge != 0);
     553       157317 :     if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge) == myOutgoingEdges.end()) {
     554       155076 :         myOutgoingEdges.push_back(edge);
     555       155076 :         myAllEdges.push_back(edge);
     556              :     }
     557       157317 : }
     558              : 
     559              : 
     560              : bool
     561        84090 : NBNode::isSimpleContinuation(bool checkLaneNumbers, bool checkWidth) const {
     562              :     // one in, one out->continuation
     563        84090 :     if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
     564        11987 :         NBEdge* in = myIncomingEdges.front();
     565        11987 :         NBEdge* out = myOutgoingEdges.front();
     566              :         // both must have the same number of lanes
     567        11979 :         return ((!checkLaneNumbers || in->getNumLanes() == out->getNumLanes())
     568        21860 :                 && (!checkWidth || in->getTotalWidth() == out->getTotalWidth()));
     569              :     }
     570              :     // two in and two out and both in reverse direction
     571        72103 :     if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
     572        37234 :         for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
     573        29212 :             NBEdge* in = *i;
     574        29212 :             EdgeVector::const_iterator opposite = find_if(myOutgoingEdges.begin(), myOutgoingEdges.end(), NBContHelper::opposite_finder(in));
     575              :             // must have an opposite edge
     576        29212 :             if (opposite == myOutgoingEdges.end()) {
     577        10920 :                 return false;
     578              :             }
     579              :             // both must have the same number of lanes
     580        20781 :             NBContHelper::nextCW(myOutgoingEdges, opposite);
     581        20781 :             if (checkLaneNumbers && in->getNumLanes() != (*opposite)->getNumLanes()) {
     582              :                 return false;
     583              :             }
     584        18674 :             if (checkWidth && in->getTotalWidth() != (*opposite)->getTotalWidth()) {
     585              :                 return false;
     586              :             }
     587              :         }
     588              :         return true;
     589              :     }
     590              :     // nope
     591              :     return false;
     592              : }
     593              : 
     594              : 
     595              : PositionVector
     596       281251 : NBNode::computeSmoothShape(const PositionVector& begShape,
     597              :                            const PositionVector& endShape,
     598              :                            int numPoints,
     599              :                            bool isTurnaround,
     600              :                            double extrapolateBeg,
     601              :                            double extrapolateEnd,
     602              :                            NBNode* recordError,
     603              :                            int shapeFlag) const {
     604              : 
     605       281251 :     bool ok = true;
     606       281251 :     if ((shapeFlag & INDIRECT_LEFT) != 0) {
     607           32 :         return indirectLeftShape(begShape, endShape, numPoints);
     608              :     }
     609       281219 :     PositionVector init = bezierControlPoints(begShape, endShape, isTurnaround, extrapolateBeg, extrapolateEnd, ok, recordError, DEG2RAD(5), shapeFlag);
     610              : #ifdef DEBUG_SMOOTH_GEOM
     611              :     if (DEBUGCOND) {
     612              :         std::cout << "computeSmoothShape node " << getID() << " begShape=" << begShape << " endShape=" << endShape << " init=" << init << " shapeFlag=" << shapeFlag << "\n";
     613              :     }
     614              : #endif
     615       281219 :     if (init.size() == 0) {
     616        87748 :         PositionVector ret;
     617        87748 :         ret.push_back(begShape.back());
     618        87748 :         ret.push_back(endShape.front());
     619              :         return ret;
     620        87748 :     } else {
     621       193471 :         return init.bezier(numPoints).smoothedZFront();
     622              :     }
     623       281219 : }
     624              : 
     625              : PositionVector
     626       283236 : NBNode::bezierControlPoints(
     627              :     const PositionVector& begShape,
     628              :     const PositionVector& endShape,
     629              :     bool isTurnaround,
     630              :     double extrapolateBeg,
     631              :     double extrapolateEnd,
     632              :     bool& ok,
     633              :     NBNode* recordError,
     634              :     double straightThresh,
     635              :     int shapeFlag) {
     636              : 
     637       283236 :     const Position beg = begShape.back();
     638       283236 :     const Position end = endShape.front();
     639              :     const double dist = beg.distanceTo2D(end);
     640       283236 :     PositionVector init;
     641       283236 :     if (dist < POSITION_EPS || beg.distanceTo2D(begShape[-2]) < POSITION_EPS || end.distanceTo2D(endShape[1]) < POSITION_EPS) {
     642              : #ifdef DEBUG_SMOOTH_GEOM
     643              :         if (DEBUGCOND2(recordError)) std::cout << "   bezierControlPoints failed beg=" << beg << " end=" << end
     644              :                                                    << " dist=" << dist
     645              :                                                    << " distBegLast=" << beg.distanceTo2D(begShape[-2])
     646              :                                                    << " distEndFirst=" << end.distanceTo2D(endShape[1])
     647              :                                                    << "\n";
     648              : #endif
     649              :         // typically, this node a is a simpleContinuation. see also #2539
     650              :         return init;
     651              :     } else {
     652       273359 :         init.push_back(beg);
     653       273359 :         if (isTurnaround) {
     654              :             // turnarounds:
     655              :             //  - end of incoming lane
     656              :             //  - position between incoming/outgoing end/begin shifted by the distance orthogonally
     657              :             //  - begin of outgoing lane
     658        23991 :             Position center = PositionVector::positionAtOffset2D(beg, end, beg.distanceTo2D(end) / (double) 2.);
     659        23991 :             center.sub(beg.y() - end.y(), end.x() - beg.x());
     660        23991 :             init.push_back(center);
     661              :         } else {
     662              :             const double EXT = 100;
     663       249368 :             const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
     664       249368 :             PositionVector endShapeBegLine(endShape[0], endShape[1]);
     665       249368 :             PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
     666       249368 :             endShapeBegLine.extrapolate2D(EXT, true);
     667       249368 :             begShapeEndLineRev.extrapolate2D(EXT, true);
     668              : #ifdef DEBUG_SMOOTH_GEOM
     669              :             if (DEBUGCOND2(recordError)) std::cout
     670              :                         << "   endShapeBegLine=" << endShapeBegLine
     671              :                         << " begShapeEndLineRev=" << begShapeEndLineRev
     672              :                         << " angle=" << RAD2DEG(angle) << "\n";
     673              : #endif
     674       249368 :             if (fabs(angle) < M_PI / 4.) {
     675              :                 // very low angle: could be an s-shape or a straight line
     676       110817 :                 const double displacementAngle = GeomHelper::angleDiff(begShape.angleAt2D(-2), beg.angleTo2D(end));
     677       110817 :                 const double bendDeg = RAD2DEG(fabs(displacementAngle - angle));
     678       110817 :                 const double halfDistance = dist / 2;
     679       110817 :                 if (fabs(displacementAngle) <= straightThresh && fabs(angle) <= straightThresh) {
     680              : #ifdef DEBUG_SMOOTH_GEOM
     681              :                     if (DEBUGCOND2(recordError)) std::cout << "   bezierControlPoints identified straight line beg=" << beg << " end=" << end
     682              :                                                                << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle) << "\n";
     683              : #endif
     684        75023 :                     return PositionVector();
     685        35794 :                 } else if (bendDeg > 22.5 && pow(bendDeg / 45, 2) / dist > 0.13) {
     686              :                     // do not allow s-curves with extreme bends
     687              :                     // (a linear dependency is to restrictive at low displacementAngles and too permisive at high angles)
     688              : #ifdef DEBUG_SMOOTH_GEOM
     689              :                     if (DEBUGCOND2(recordError)) std::cout << "   bezierControlPoints found extreme s-curve, falling back to straight line beg=" << beg << " end=" << end
     690              :                                                                << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
     691              :                                                                << " dist=" << dist << " bendDeg=" << bendDeg << " bd2=" << pow(bendDeg / 45, 2)
     692              :                                                                << " displacementError=" << sin(displacementAngle) * dist
     693              :                                                                << " begShape=" << begShape << " endShape=" << endShape << "\n";
     694              : #endif
     695         1426 :                     ok = false;
     696         1426 :                     if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
     697          948 :                         recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)fabs(sin(displacementAngle) * dist));
     698              :                     }
     699         1426 :                     return PositionVector();
     700              :                 } else {
     701        34368 :                     const double endLength = begShape[-2].distanceTo2D(begShape[-1]);
     702        34368 :                     const double off1 = endLength + MIN2(extrapolateBeg, halfDistance);
     703        68736 :                     init.push_back(PositionVector::positionAtOffset2D(begShapeEndLineRev[1], begShapeEndLineRev[0], off1));
     704        34368 :                     const double off2 = EXT - MIN2(extrapolateEnd, halfDistance);
     705        68736 :                     init.push_back(PositionVector::positionAtOffset2D(endShapeBegLine[0], endShapeBegLine[1], off2));
     706              : #ifdef DEBUG_SMOOTH_GEOM
     707              :                     if (DEBUGCOND2(recordError)) std::cout << "   bezierControlPoints found s-curve beg=" << beg << " end=" << end
     708              :                                                                << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
     709              :                                                                << " halfDistance=" << halfDistance << "\n";
     710              : #endif
     711              :                 }
     712              :             } else {
     713              :                 // turning
     714              :                 //  - end of incoming lane
     715              :                 //  - intersection of the extrapolated lanes
     716              :                 //  - begin of outgoing lane
     717              :                 // attention: if there is no intersection, use a straight line
     718       138551 :                 Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
     719              :                 if (intersect == Position::INVALID) {
     720              : #ifdef DEBUG_SMOOTH_GEOM
     721              :                     if (DEBUGCOND2(recordError)) {
     722              :                         std::cout << "   bezierControlPoints failed beg=" << beg << " end=" << end << " intersect=" << intersect
     723              :                                   << " endShapeBegLine=" << endShapeBegLine
     724              :                                   << " begShapeEndLineRev=" << begShapeEndLineRev
     725              :                                   << "\n";
     726              :                     }
     727              : #endif
     728         1875 :                     ok = false;
     729         1875 :                     if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
     730              :                         // it's unclear if this error can be solved via stretching the intersection.
     731          579 :                         recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
     732              :                     }
     733         1875 :                     return PositionVector();
     734              :                 }
     735       136676 :                 const double begOffset = begShapeEndLineRev.nearest_offset_to_point2D(intersect);
     736       136676 :                 const double endOffset = endShapeBegLine.nearest_offset_to_point2D(intersect);
     737              :                 /*
     738              :                 if ((shapeFlag & FOUR_CONTROL_POINTS) == 0 && (begOffset >= EXT || endOffset >= EXT)) {
     739              :                     // intersection point lies within begShape / endShape so we cannot use it
     740              :                     if (dist < 2) {
     741              :                         return PositionVector();
     742              :                     }
     743              :                     shapeFlag |= FOUR_CONTROL_POINTS;
     744              :                     extrapolateBeg = MIN2(10.0, dist / 2);
     745              :                     extrapolateEnd = extrapolateBeg;
     746              :                 }
     747              :                 */
     748       136676 :                 const double minControlLength = MIN2((double)1.0, dist / 2);
     749              :                 const double distBeg = intersect.distanceTo2D(beg);
     750              :                 const double distEnd = intersect.distanceTo2D(end);
     751       136676 :                 const bool lengthenBeg = distBeg <= minControlLength;
     752       136676 :                 const bool lengthenEnd = distEnd <= minControlLength;
     753              : #ifdef DEBUG_SMOOTH_GEOM
     754              :                 if (DEBUGCOND2(recordError)) std::cout
     755              :                             << "   beg=" << beg << " end=" << end << " intersect=" << intersect
     756              :                             << " distBeg=" << distBeg << " distEnd=" << distEnd
     757              :                             << " begOffset=" << begOffset << " endOffset=" << endOffset
     758              :                             << " lEnd=" << lengthenEnd << " lBeg=" << lengthenBeg
     759              :                             << "\n";
     760              : #endif
     761       136676 :                 if (lengthenBeg && lengthenEnd) {
     762              : #ifdef DEBUG_SMOOTH_GEOM
     763              :                     if (DEBUGCOND2(recordError)) {
     764              :                         std::cout << "   bezierControlPoints failed\n";
     765              :                     }
     766              : #endif
     767            0 :                     if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
     768              :                         // This should be fixable with minor stretching
     769            0 :                         recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
     770              :                     }
     771            0 :                     ok = false;
     772            0 :                     return PositionVector();
     773       136676 :                 } else if ((shapeFlag & FOUR_CONTROL_POINTS)) {
     774          109 :                     init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - extrapolateBeg));
     775          218 :                     init.push_back(endShapeBegLine.positionAtOffset2D(EXT - extrapolateEnd));
     776       136567 :                 } else if (lengthenBeg || lengthenEnd) {
     777          375 :                     init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - minControlLength));
     778          750 :                     init.push_back(endShapeBegLine.positionAtOffset2D(EXT - minControlLength));
     779       136192 :                 } else if ((shapeFlag & AVOID_WIDE_LEFT_TURN) != 0
     780              :                            // there are two reasons for enabling special geometry rules:
     781              :                            // 1) sharp edge angles which could cause overshoot
     782              :                            // 2) junction geometries with a large displacement between opposite left turns
     783              :                            //    which would cause the default geometry to overlap
     784       101253 :                            && ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) != 0
     785        96761 :                                || (angle > DEG2RAD(95) && (distBeg > 20 || distEnd > 20)))) {
     786              :                     //std::cout << "   bezierControlPoints intersect=" << intersect << " dist=" << dist << " distBeg=" << distBeg <<  " distEnd=" << distEnd << " angle=" << RAD2DEG(angle) << " flag=" << shapeFlag << "\n";
     787         5592 :                     const double factor = ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) == 0 ? 1
     788         4492 :                                            : MIN2(0.6, 16 / dist));
     789        10135 :                     init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg * factor / 1.2, dist * factor / 1.8)));
     790         9994 :                     init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd * factor / 1.2, dist * factor / 1.8)));
     791       136192 :                 } else if ((shapeFlag & AVOID_WIDE_RIGHT_TURN) != 0 && angle < DEG2RAD(-95) && (distBeg > 20 || distEnd > 20)) {
     792              :                     //std::cout << "   bezierControlPoints intersect=" << intersect << " distBeg=" << distBeg <<  " distEnd=" << distEnd << "\n";
     793            0 :                     init.push_back(begShapeEndLineRev.positionAtOffset2D(EXT - MIN2(distBeg / 1.4, dist / 2)));
     794            0 :                     init.push_back(endShapeBegLine.positionAtOffset2D(EXT - MIN2(distEnd / 1.4, dist / 2)));
     795              :                 } else {
     796              :                     double z;
     797       130600 :                     const double z1 = begShapeEndLineRev.positionAtOffset2D(begOffset).z();
     798       130600 :                     const double z2 = endShapeBegLine.positionAtOffset2D(endOffset).z();
     799       130600 :                     const double z3 = 0.5 * (beg.z() + end.z());
     800              :                     // if z1 and z2 are on the same side in regard to z3 then we
     801              :                     // can use their avarage. Otherwise, the intersection in 3D
     802              :                     // is not good and we are better of using z3
     803       130600 :                     if ((z1 <= z3 && z2 <= z3) || (z1 >= z3 && z2 >= z3)) {
     804       130548 :                         z = 0.5 * (z1 + z2);
     805              :                     } else {
     806              :                         z = z3;
     807              :                     }
     808              :                     intersect.set(intersect.x(), intersect.y(), z);
     809       130600 :                     init.push_back(intersect);
     810              :                 }
     811              :             }
     812       249368 :         }
     813       195035 :         init.push_back(end);
     814              :     }
     815              :     return init;
     816       283236 : }
     817              : 
     818              : PositionVector
     819           32 : NBNode::indirectLeftShape(const PositionVector& begShape, const PositionVector& endShape, int numPoints) const {
     820              :     UNUSED_PARAMETER(numPoints);
     821           32 :     PositionVector result;
     822           32 :     result.push_back(begShape.back());
     823              :     //const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
     824           32 :     PositionVector endShapeBegLine(endShape[0], endShape[1]);
     825           32 :     PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
     826           32 :     endShapeBegLine.extrapolate2D(100, true);
     827           32 :     begShapeEndLineRev.extrapolate2D(100, true);
     828           32 :     Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
     829              :     if (intersect == Position::INVALID) {
     830            0 :         WRITE_WARNINGF(TL("Could not compute indirect left turn shape at node '%'"), getID());
     831              :     } else {
     832           32 :         Position dir = intersect;
     833           32 :         dir.sub(endShape[0]);
     834           32 :         dir.norm2D();
     835           32 :         const double radius = myRadius == NBNode::UNSPECIFIED_RADIUS ? OptionsCont::getOptions().getFloat("default.junctions.radius") : myRadius;
     836              :         dir.mul(radius);
     837           32 :         result.push_back(intersect + dir);
     838              :     }
     839           32 :     result.push_back(endShape.front());
     840           32 :     return result;
     841           32 : }
     842              : 
     843              : PositionVector
     844       157562 : NBNode::computeInternalLaneShape(const NBEdge* fromE, const NBEdge::Connection& con, int numPoints, NBNode* recordError, int shapeFlag) const {
     845       157562 :     if (con.fromLane >= fromE->getNumLanes()) {
     846            0 :         throw ProcessError(TLF("Connection '%' starts at a non-existant lane.", con.getDescription(fromE)));
     847              :     }
     848       157562 :     if (con.toLane >= con.toEdge->getNumLanes()) {
     849            0 :         throw ProcessError(TLF("Connection '%' targets a non-existant lane.", con.getDescription(fromE)));
     850              :     }
     851       157562 :     PositionVector fromShape = fromE->getLaneShape(con.fromLane);
     852       157562 :     PositionVector toShape = con.toEdge->getLaneShape(con.toLane);
     853       157562 :     PositionVector ret;
     854       157562 :     bool useCustomShape = con.customShape.size() > 0;
     855       157562 :     if (useCustomShape) {
     856              :         // ensure that the shape starts and ends at the intersection boundary
     857          164 :         PositionVector startBorder = fromE->getNodeBorder(this);
     858          164 :         if (startBorder.size() == 0) {
     859          246 :             startBorder = fromShape.getOrthogonal(fromShape.back(), 1, true);
     860              :         }
     861          164 :         PositionVector tmp = NBEdge::startShapeAt(con.customShape, this, startBorder);
     862          164 :         if (tmp.size() < 2) {
     863            0 :             WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
     864              :             useCustomShape = false;
     865              :         } else {
     866          164 :             if (tmp.length2D() > con.customShape.length2D() + POSITION_EPS) {
     867              :                 // shape was lengthened at the start, make sure it attaches at the center of the lane
     868           80 :                 tmp[0] = fromShape.back();
     869           84 :             } else if (recordError != nullptr) {
     870           22 :                 const double offset = tmp[0].distanceTo2D(fromShape.back());
     871           22 :                 if (offset > fromE->getLaneWidth(con.fromLane) / 2) {
     872            6 :                     WRITE_WARNINGF(TL("Custom shape has distance % to incoming lane for connection %."), offset, con.getDescription(fromE));
     873              :                 }
     874              :             }
     875          164 :             PositionVector endBorder = con.toEdge->getNodeBorder(this);
     876          164 :             if (endBorder.size() == 0) {
     877          246 :                 endBorder = toShape.getOrthogonal(toShape.front(), 1, false);
     878              :             }
     879          328 :             ret = NBEdge::startShapeAt(tmp.reverse(), this, endBorder).reverse();
     880          164 :             if (ret.size() < 2) {
     881            0 :                 WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
     882              :                 useCustomShape = false;
     883          164 :             } else if (ret.length2D() > tmp.length2D() + POSITION_EPS) {
     884              :                 // shape was lengthened at the end, make sure it attaches at the center of the lane
     885           50 :                 ret[-1] = toShape.front();
     886          114 :             } else if (recordError != nullptr) {
     887           26 :                 const double offset = ret[-1].distanceTo2D(toShape.front());
     888           26 :                 if (offset > con.toEdge->getLaneWidth(con.toLane) / 2) {
     889            2 :                     WRITE_WARNINGF(TL("Custom shape has distance % to outgoing lane for connection %."), offset, con.getDescription(fromE));
     890              :                 }
     891              :             }
     892          164 :         }
     893          164 :     }
     894       157562 :     if (!useCustomShape) {
     895       157398 :         displaceShapeAtWidthChange(fromE, con, fromShape, toShape);
     896       157398 :         double extrapolateBeg = 5. * fromE->getNumLanes();
     897       157398 :         double extrapolateEnd = 5. * con.toEdge->getNumLanes();
     898       157398 :         LinkDirection dir = getDirection(fromE, con.toEdge);
     899       157398 :         if (dir == LinkDirection::LEFT || dir == LinkDirection::TURN) {
     900        60529 :             shapeFlag += AVOID_WIDE_LEFT_TURN;
     901              :         }
     902       157398 :         if (con.indirectLeft) {
     903           32 :             shapeFlag += INDIRECT_LEFT;
     904              :         }
     905              : #ifdef DEBUG_SMOOTH_GEOM
     906              :         if (DEBUGCOND) {
     907              :             std::cout << "computeInternalLaneShape node " << getID() << " fromE=" << fromE->getID() << " toE=" << con.toEdge->getID() << "\n";
     908              :         }
     909              : #endif
     910       157398 :         ret = computeSmoothShape(fromShape, toShape,
     911       157398 :                                  numPoints, fromE->getTurnDestination() == con.toEdge,
     912              :                                  extrapolateBeg, extrapolateEnd, recordError, shapeFlag);
     913              :     }
     914       157562 :     const NBEdge::Lane& lane = fromE->getLaneStruct(con.fromLane);
     915       157562 :     if (lane.endOffset > 0) {
     916           72 :         PositionVector beg = lane.shape.getSubpart(lane.shape.length() - lane.endOffset, lane.shape.length());
     917           72 :         beg.append(ret);
     918              :         ret = beg;
     919           72 :     }
     920       157562 :     if (con.toEdge->isBidiRail() && con.toEdge->getTurnDestination(true)->getEndOffset() > 0) {
     921            4 :         PositionVector end = toShape.getSubpart(0, con.toEdge->getTurnDestination(true)->getEndOffset());
     922            4 :         ret.append(end);
     923            4 :     }
     924       157562 :     return ret;
     925       157562 : }
     926              : 
     927              : 
     928              : bool
     929       255514 : NBNode::isConstantWidthTransition() const {
     930              :     return (myIncomingEdges.size() == 1
     931        23548 :             && myOutgoingEdges.size() == 1
     932        17834 :             && myIncomingEdges[0]->getNumLanes() != myOutgoingEdges[0]->getNumLanes()
     933       263564 :             && myIncomingEdges[0]->getTotalWidth() == myOutgoingEdges[0]->getTotalWidth());
     934              : }
     935              : 
     936              : void
     937       157398 : NBNode::displaceShapeAtWidthChange(const NBEdge* from, const NBEdge::Connection& con,
     938              :                                    PositionVector& fromShape, PositionVector& toShape) const {
     939       157398 :     if (isConstantWidthTransition()) {
     940              :         // displace shapes
     941           14 :         NBEdge* in = myIncomingEdges[0];
     942           14 :         NBEdge* out = myOutgoingEdges[0];
     943           14 :         double outCenter = out->getLaneWidth(con.toLane) / 2;
     944           27 :         for (int i = 0; i < con.toLane; ++i) {
     945           13 :             outCenter += out->getLaneWidth(i);
     946              :         }
     947           14 :         double inCenter = in->getLaneWidth(con.fromLane) / 2;
     948           25 :         for (int i = 0; i < con.fromLane; ++i) {
     949           11 :             inCenter += in->getLaneWidth(i);
     950              :         }
     951              :         //std::cout << "displaceShapeAtWidthChange inCenter=" << inCenter << " outCenter=" << outCenter << "\n";
     952              :         try {
     953           14 :             if (in->getNumLanes() > out->getNumLanes()) {
     954              :                 // shift toShape so the internal lane ends straight at the displaced entry point
     955            3 :                 toShape.move2side(outCenter - inCenter);
     956              :             } else {
     957              :                 // shift fromShape so the internal lane starts straight at the displaced exit point
     958           11 :                 fromShape.move2side(inCenter - outCenter);
     959              : 
     960              :             }
     961            0 :         } catch (InvalidArgument&) { }
     962              :     } else {
     963       157384 :         SVCPermissions fromP = from->getPermissions(con.fromLane);
     964       157384 :         SVCPermissions toP = con.toEdge->getPermissions(con.toLane);
     965       157384 :         if ((fromP & toP) == SVC_BICYCLE && (fromP | toP) != SVC_BICYCLE) {
     966         2519 :             double shift = (from->getLaneWidth(con.fromLane) - con.toEdge->getLaneWidth(con.toLane)) / 2;
     967         2519 :             if (toP == SVC_BICYCLE) {
     968              :                 // let connection to dedicated bicycle lane start on the right side of a mixed lane for straight an right-going connections
     969              :                 // (on the left side for left turns)
     970              :                 // XXX indirect left turns should also start on the right side
     971          967 :                 LinkDirection dir = getDirection(from, con.toEdge);
     972          967 :                 if ((dir == LinkDirection::LEFT) || (dir == LinkDirection::PARTLEFT) || (dir == LinkDirection::TURN)) {
     973          412 :                     fromShape.move2side(-shift);
     974              :                 } else {
     975          555 :                     fromShape.move2side(shift);
     976              :                 }
     977         1552 :             } else if (fromP == SVC_BICYCLE) {
     978              :                 // let connection from dedicated bicycle end on the right side of a mixed lane
     979          814 :                 toShape.move2side(-shift);
     980              :             }
     981              :         }
     982              :     }
     983       157398 : }
     984              : 
     985              : bool
     986       811506 : NBNode::needsCont(const NBEdge* fromE, const NBEdge* otherFromE,
     987              :                   const NBEdge::Connection& c, const NBEdge::Connection& otherC, bool checkOnlyTLS) const {
     988       811506 :     const NBEdge* toE = c.toEdge;
     989       811506 :     const NBEdge* otherToE = otherC.toEdge;
     990              : 
     991       811506 :     if (!checkOnlyTLS) {
     992       794873 :         if (myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT
     993              :                 || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT
     994              :                 || myType == SumoXMLNodeType::ALLWAY_STOP
     995       794873 :                 || myType == SumoXMLNodeType::ZIPPER) {
     996              :             return false;
     997              :         }
     998       720896 :         LinkDirection d1 = getDirection(fromE, toE);
     999       720896 :         const bool thisRight = (d1 == LinkDirection::RIGHT || d1 == LinkDirection::PARTRIGHT);
    1000       974938 :         const bool rightTurnConflict = (thisRight &&
    1001       254042 :                                         NBNode::rightTurnConflict(fromE, toE, c.fromLane, otherFromE, otherToE, otherC.fromLane));
    1002       720896 :         if (thisRight && !rightTurnConflict) {
    1003              :             return false;
    1004              :         }
    1005       467204 :         if (myRequest && myRequest->indirectLeftTurnConflict(fromE, c, otherFromE, otherC, false)) {
    1006              :             return true;
    1007              :         }
    1008       467172 :         if (!(foes(otherFromE, otherToE, fromE, toE) || myRequest == nullptr || rightTurnConflict)) {
    1009              :             // if they do not cross, no waiting place is needed
    1010              :             return false;
    1011              :         }
    1012       165749 :         LinkDirection d2 = getDirection(otherFromE, otherToE);
    1013       165749 :         if (d2 == LinkDirection::TURN) {
    1014              :             return false;
    1015              :         }
    1016       150281 :         if (fromE == otherFromE && !thisRight) {
    1017              :             // ignore same edge links except for right-turns
    1018              :             return false;
    1019              :         }
    1020       148785 :         if (thisRight && d2 != LinkDirection::STRAIGHT) {
    1021              :             return false;
    1022              :         }
    1023              :     }
    1024       165295 :     if (c.tlID != "") {
    1025              :         assert(myTrafficLights.size() > 0 || myType == SumoXMLNodeType::RAIL_CROSSING || myType == SumoXMLNodeType::RAIL_SIGNAL);
    1026        84214 :         for (std::set<NBTrafficLightDefinition*>::const_iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
    1027        51531 :             if ((*it)->needsCont(fromE, toE, otherFromE, otherToE)) {
    1028              :                 return true;
    1029              :             }
    1030              :         }
    1031              :         return false;
    1032              :     }
    1033       113962 :     if (fromE->getJunctionPriority(this) > 0 && otherFromE->getJunctionPriority(this) > 0) {
    1034        22513 :         return mustBrake(fromE, toE, c.fromLane, c.toLane, false);
    1035              :     }
    1036              :     return false;
    1037              : }
    1038              : 
    1039              : bool
    1040      1549690 : NBNode::tlsStrandedConflict(const NBEdge* from, const NBEdge::Connection& c,
    1041              :                             const NBEdge* foeFrom, const NBEdge::Connection& foe) const {
    1042       178912 :     return (foe.haveVia && isTLControlled() && c.tlLinkIndex >= 0 && foe.tlLinkIndex >= 0
    1043        75212 :             && !foeFrom->isTurningDirectionAt(foe.toEdge)
    1044        43156 :             && foes(from, c.toEdge, foeFrom, foe.toEdge)
    1045      1566323 :             && !needsCont(foeFrom, from, foe, c, true));
    1046              : }
    1047              : 
    1048              : 
    1049              : void
    1050        17433 : NBNode::removeJoinedTrafficLights() {
    1051              :     std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
    1052        17505 :     for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
    1053              :         // if this is the only controlled node we keep the tlDef as it is to generate a warning later
    1054           72 :         if ((*i)->getNodes().size() > 1) {
    1055              :             myTrafficLights.erase(*i);
    1056            7 :             (*i)->removeNode(this);
    1057            7 :             (*i)->setParticipantsInformation();
    1058            7 :             (*i)->setTLControllingInformation();
    1059              :         }
    1060              :     }
    1061        17433 : }
    1062              : 
    1063              : 
    1064              : void
    1065        70888 : NBNode::computeLogic(const NBEdgeCont& ec) {
    1066        70888 :     delete myRequest; // possibly recomputation step
    1067        70888 :     myRequest = nullptr;
    1068        70888 :     if (myIncomingEdges.size() == 0 || myOutgoingEdges.size() == 0) {
    1069              :         // no logic if nothing happens here
    1070         9864 :         myType = SumoXMLNodeType::DEAD_END;
    1071         9864 :         removeJoinedTrafficLights();
    1072         9864 :         return;
    1073              :     }
    1074              :     // compute the logic if necessary or split the junction
    1075        61024 :     if (myType != SumoXMLNodeType::NOJUNCTION && myType != SumoXMLNodeType::DISTRICT && myType != SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION) {
    1076              :         // build the request
    1077        60844 :         myRequest = new NBRequest(ec, this, myAllEdges, myIncomingEdges, myOutgoingEdges, myBlockedConnections);
    1078              :         // check whether it is not too large
    1079        60844 :         int numConnections = numNormalConnections();
    1080        60844 :         if (numConnections >= SUMO_MAX_CONNECTIONS) {
    1081              :             // yep -> make it untcontrolled, warn
    1082            0 :             delete myRequest;
    1083            0 :             myRequest = nullptr;
    1084            0 :             if (myType == SumoXMLNodeType::TRAFFIC_LIGHT) {
    1085            0 :                 myType = SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION;
    1086              :             } else {
    1087            0 :                 myType = SumoXMLNodeType::NOJUNCTION;
    1088              :             }
    1089            0 :             WRITE_WARNINGF(TL("Junction '%' is too complicated (% connections, max %); will be set to %."),
    1090              :                            getID(), numConnections, SUMO_MAX_CONNECTIONS, toString(myType));
    1091        60844 :         } else if (numConnections == 0) {
    1092         7569 :             delete myRequest;
    1093         7569 :             myRequest = nullptr;
    1094         7569 :             myType = SumoXMLNodeType::DEAD_END;
    1095         7569 :             removeJoinedTrafficLights();
    1096              :         } else {
    1097        53275 :             myRequest->buildBitfieldLogic();
    1098              :         }
    1099              :     }
    1100              : }
    1101              : 
    1102              : 
    1103              : void
    1104        70888 : NBNode::computeLogic2(bool checkLaneFoes) {
    1105        70888 :     if (myRequest != nullptr) {
    1106        53275 :         myRequest->computeLogic(checkLaneFoes);
    1107              :     }
    1108        70888 : }
    1109              : 
    1110              : void
    1111        70888 : NBNode::computeKeepClear() {
    1112        70888 :     if (hasConflict()) {
    1113        25390 :         if (!myKeepClear) {
    1114           10 :             for (NBEdge* incoming : myIncomingEdges) {
    1115              :                 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
    1116           40 :                 for (NBEdge::Connection& c : connections) {
    1117           32 :                     c.keepClear = KEEPCLEAR_FALSE;
    1118              :                 }
    1119              :             }
    1120        25388 :         } else if (geometryLike() && myCrossings.size() == 0 && !isTLControlled()) {
    1121              :             int linkIndex = 0;
    1122          918 :             for (NBEdge* incoming : myIncomingEdges) {
    1123              :                 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
    1124         2033 :                 for (NBEdge::Connection& c : connections) {
    1125         1427 :                     if (c.keepClear == KEEPCLEAR_UNSPECIFIED && myRequest->hasConflictAtLink(linkIndex)) {
    1126          578 :                         const LinkState linkState = getLinkState(incoming, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
    1127          578 :                         if (linkState == LINKSTATE_MAJOR) {
    1128          333 :                             c.keepClear = KEEPCLEAR_FALSE;
    1129              :                         }
    1130              :                     }
    1131              :                 }
    1132          606 :                 linkIndex++;
    1133              :             }
    1134              :         }
    1135              :     }
    1136        70888 : }
    1137              : 
    1138              : 
    1139              : bool
    1140        50571 : NBNode::writeLogic(OutputDevice& into) const {
    1141        50571 :     if (myRequest) {
    1142        50445 :         myRequest->writeLogic(into);
    1143        50445 :         return true;
    1144              :     }
    1145              :     return false;
    1146              : }
    1147              : 
    1148              : 
    1149              : const std::string
    1150            0 : NBNode::getFoes(int linkIndex) const {
    1151            0 :     if (myRequest == nullptr) {
    1152            0 :         return "";
    1153              :     } else {
    1154            0 :         return myRequest->getFoes(linkIndex);
    1155              :     }
    1156              : }
    1157              : 
    1158              : 
    1159              : const std::string
    1160            0 : NBNode::getResponse(int linkIndex) const {
    1161            0 :     if (myRequest == nullptr) {
    1162            0 :         return "";
    1163              :     } else {
    1164            0 :         return myRequest->getResponse(linkIndex);
    1165              :     }
    1166              : }
    1167              : 
    1168              : bool
    1169        71073 : NBNode::hasConflict() const {
    1170        71073 :     if (myRequest == nullptr) {
    1171              :         return false;
    1172              :     } else {
    1173        53457 :         return myRequest->hasConflict();
    1174              :     }
    1175              : }
    1176              : 
    1177              : 
    1178              : bool
    1179          439 : NBNode::hasConflict(const NBEdge* e) const {
    1180          439 :     if (myRequest == nullptr) {
    1181              :         return false;
    1182              :     }
    1183          507 :     for (const auto& con : e->getConnections()) {
    1184          505 :         const int index = getConnectionIndex(e, con);
    1185          505 :         if (myRequest->hasConflictAtLink(index)) {
    1186              :             return true;
    1187              :         }
    1188              :     }
    1189              :     return false;
    1190              : }
    1191              : 
    1192              : 
    1193              : void
    1194           57 : NBNode::updateSurroundingGeometry() {
    1195           57 :     NBTurningDirectionsComputer::computeTurnDirectionsForNode(this, false);
    1196           57 :     sortEdges(false);
    1197           57 :     computeNodeShape(-1);
    1198          443 :     for (NBEdge* edge : myAllEdges) {
    1199          386 :         edge->computeEdgeShape();
    1200              :     }
    1201           57 : }
    1202              : 
    1203              : void
    1204        94844 : NBNode::computeNodeShape(double mismatchThreshold) {
    1205        94844 :     if (myHaveCustomPoly) {
    1206              :         return;
    1207              :     }
    1208        94786 :     if (myIncomingEdges.size() == 0 && myOutgoingEdges.size() == 0) {
    1209              :         // may be an intermediate step during network editing
    1210              :         myPoly.clear();
    1211         2557 :         myPoly.push_back(myPosition);
    1212         2557 :         return;
    1213              :     }
    1214       184458 :     if (OptionsCont::getOptions().getFloat("default.junctions.radius") < 0) {
    1215              :         // skip shape computation by option
    1216              :         return;
    1217              :     }
    1218              :     try {
    1219        92224 :         NBNodeShapeComputer computer(*this);
    1220       184448 :         myPoly = computer.compute(OptionsCont::getOptions().getBool("junctions.minimal-shape"));
    1221       276483 :         if (myRadius == UNSPECIFIED_RADIUS && !OptionsCont::getOptions().isDefault("default.junctions.radius")) {
    1222           51 :             myRadius = computer.getRadius();
    1223              :         }
    1224        92224 :         if (myPoly.size() > 0) {
    1225              :             PositionVector tmp = myPoly;
    1226        92224 :             tmp.push_back_noDoublePos(tmp[0]); // need closed shape
    1227              :             if (mismatchThreshold >= 0
    1228        65370 :                     && !tmp.around(myPosition)
    1229       112526 :                     && tmp.distance2D(myPosition) > mismatchThreshold) {
    1230          297 :                 WRITE_WARNINGF(TL("Shape for junction '%' has distance % to its given position."), myID, tmp.distance2D(myPosition));
    1231              :             }
    1232        92224 :         }
    1233        92224 :     } catch (InvalidArgument&) {
    1234            0 :         WRITE_WARNINGF(TL("For junction '%': could not compute shape."), myID);
    1235              :         // make sure our shape is not empty because our XML schema forbids empty attributes
    1236              :         myPoly.clear();
    1237            0 :         myPoly.push_back(myPosition);
    1238            0 :     }
    1239              : }
    1240              : 
    1241              : 
    1242              : void
    1243        70892 : NBNode::computeLanes2Lanes() {
    1244              :     // special case a):
    1245              :     //  one in, one out, the outgoing has more lanes
    1246        70892 :     if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
    1247        19470 :         NBEdge* in = myIncomingEdges[0];
    1248        19470 :         NBEdge* out = myOutgoingEdges[0];
    1249              :         // check if it's not the turnaround
    1250        19470 :         if (in->getTurnDestination() == out) {
    1251              :             // will be added later or not...
    1252        12371 :             return;
    1253              :         }
    1254              :         int inOffset, inEnd, outOffset, outEnd, addedLanes;
    1255         7648 :         getReduction(out, in, outOffset, outEnd, inOffset, inEnd, addedLanes);
    1256              :         if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
    1257         4016 :                 && addedLanes > 0
    1258         8199 :                 && in->isConnectedTo(out)) {
    1259          549 :             const int addedRight = addedLanesRight(out, addedLanes);
    1260          549 :             const int addedLeft = addedLanes - addedRight;
    1261              : #ifdef DEBUG_CONNECTION_GUESSING
    1262              :             if (DEBUGCOND) {
    1263              :                 std::cout << "l2l node=" << getID() << " specialCase a. addedRight=" << addedRight << " addedLeft=" << addedLeft << " inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << "\n";
    1264              :             }
    1265              : #endif
    1266              :             // "straight" connections
    1267         1694 :             for (int i = inOffset; i < inEnd; ++i) {
    1268         2290 :                 in->setConnection(i, out, i - inOffset + outOffset + addedRight, NBEdge::Lane2LaneInfoType::COMPUTED);
    1269              :             }
    1270              :             // connect extra lane on the right
    1271          604 :             for (int i = 0; i < addedRight; ++i) {
    1272          110 :                 in->setConnection(inOffset, out, outOffset + i, NBEdge::Lane2LaneInfoType::COMPUTED);
    1273              :             }
    1274              :             // connect extra lane on the left
    1275          549 :             const int inLeftMost = inEnd - 1;;
    1276          549 :             const int outOffset2 = outOffset + addedRight + inEnd - inOffset;
    1277         1073 :             for (int i = 0; i < addedLeft; ++i) {
    1278         1048 :                 in->setConnection(inLeftMost, out, outOffset2 + i, NBEdge::Lane2LaneInfoType::COMPUTED);
    1279              :             }
    1280          549 :             if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
    1281           16 :                 recheckVClassConnections(out);
    1282              :             }
    1283          549 :             return;
    1284              :         }
    1285              :     }
    1286              :     // special case b):
    1287              :     //  two in, one out, the outgoing has the same number of lanes as the sum of the incoming
    1288              :     //  --> highway on-ramp
    1289        58521 :     if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 1) {
    1290         4392 :         NBEdge* const out = myOutgoingEdges[0];
    1291         4392 :         NBEdge* in1 = myIncomingEdges[0];
    1292         4392 :         NBEdge* in2 = myIncomingEdges[1];
    1293         4392 :         const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1294         4392 :         int in1Offset = MAX2(0, in1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1295         4392 :         int in2Offset = MAX2(0, in2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1296         4392 :         if (in1->getNumLanes() + in2->getNumLanes() - in1Offset - in2Offset == out->getNumLanes() - outOffset
    1297          189 :                 && (in1->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
    1298           48 :                 && (in2->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
    1299           48 :                 && in1 != out
    1300           48 :                 && in2 != out
    1301           48 :                 && in1->isConnectedTo(out)
    1302           47 :                 && in2->isConnectedTo(out)
    1303           47 :                 && in1->getSpecialLane(SVC_BICYCLE) == -1
    1304           45 :                 && in2->getSpecialLane(SVC_BICYCLE) == -1
    1305           45 :                 && out->getSpecialLane(SVC_BICYCLE) == -1
    1306           45 :                 && in1->getSpecialLane(SVC_TRAM) == -1
    1307           44 :                 && in2->getSpecialLane(SVC_TRAM) == -1
    1308           44 :                 && out->getSpecialLane(SVC_TRAM) == -1
    1309         4436 :                 && isLongEnough(out, MIN_WEAVE_LENGTH)) {
    1310              : #ifdef DEBUG_CONNECTION_GUESSING
    1311              :             if (DEBUGCOND) {
    1312              :                 std::cout << "l2l node=" << getID() << " specialCase b\n";
    1313              :             }
    1314              : #endif
    1315              :             // for internal: check which one is the rightmost
    1316           25 :             double a1 = in1->getAngleAtNode(this);
    1317           25 :             double a2 = in2->getAngleAtNode(this);
    1318           25 :             double ccw = GeomHelper::getCCWAngleDiff(a1, a2);
    1319           25 :             double cw = GeomHelper::getCWAngleDiff(a1, a2);
    1320           25 :             if (ccw > cw) {
    1321              :                 std::swap(in1, in2);
    1322              :                 std::swap(in1Offset, in2Offset);
    1323              :             }
    1324           25 :             in1->addLane2LaneConnections(in1Offset, out, outOffset, in1->getNumLanes() - in1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
    1325           25 :             in2->addLane2LaneConnections(in2Offset, out, in1->getNumLanes() + outOffset - in1Offset, in2->getNumLanes() - in2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
    1326           25 :             if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
    1327            0 :                 recheckVClassConnections(out);
    1328              :             }
    1329              :             return;
    1330              :         }
    1331              :     }
    1332              :     // special case c):
    1333              :     //  one in, two out, the incoming has the same number of lanes or only 1 lane less than the sum of the outgoing lanes
    1334              :     //  --> highway off-ramp
    1335        58496 :     if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 2) {
    1336         5781 :         NBEdge* in = myIncomingEdges[0];
    1337         5781 :         NBEdge* out1 = myOutgoingEdges[0];
    1338         5781 :         NBEdge* out2 = myOutgoingEdges[1];
    1339         5781 :         const int inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1340         5781 :         int out1Offset = MAX2(0, out1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1341         5781 :         int out2Offset = MAX2(0, out2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1342         5781 :         const int deltaLaneSum = (out2->getNumLanes() + out1->getNumLanes() - out1Offset - out2Offset) - (in->getNumLanes() - inOffset);
    1343        15916 :         if ((deltaLaneSum == 0 || (deltaLaneSum == 1 && in->getPermissionVariants(inOffset, in->getNumLanes()).size() == 1))
    1344         5484 :                 && (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES)
    1345         2573 :                 && in != out1
    1346         2573 :                 && in != out2
    1347         2573 :                 && in->isConnectedTo(out1)
    1348          958 :                 && in->isConnectedTo(out2)
    1349          782 :                 && !in->isTurningDirectionAt(out1)
    1350         6503 :                 && !in->isTurningDirectionAt(out2)
    1351              :            ) {
    1352              : #ifdef DEBUG_CONNECTION_GUESSING
    1353              :             if (DEBUGCOND) {
    1354              :                 std::cout << "l2l node=" << getID() << " specialCase c\n";
    1355              :             }
    1356              : #endif
    1357              :             // for internal: check which one is the rightmost
    1358          633 :             if (NBContHelper::relative_outgoing_edge_sorter(in)(out2, out1)) {
    1359              :                 std::swap(out1, out2);
    1360              :                 std::swap(out1Offset, out2Offset);
    1361              :             }
    1362          633 :             in->addLane2LaneConnections(inOffset, out1, out1Offset, out1->getNumLanes() - out1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
    1363          633 :             in->addLane2LaneConnections(out1->getNumLanes() + inOffset - out1Offset - deltaLaneSum, out2, out2Offset, out2->getNumLanes() - out2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, false);
    1364          633 :             if (in->getSpecialLane(SVC_BICYCLE) >= 0) {
    1365           10 :                 recheckVClassConnections(out1);
    1366           10 :                 recheckVClassConnections(out2);
    1367              :             }
    1368              :             return;
    1369              :         }
    1370              :     }
    1371              :     // special case d):
    1372              :     //  one in, one out, the outgoing has one lane less and node has type 'zipper'
    1373        57863 :     if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1 && myType == SumoXMLNodeType::ZIPPER) {
    1374            8 :         NBEdge* in = myIncomingEdges[0];
    1375            8 :         NBEdge* out = myOutgoingEdges[0];
    1376              :         // check if it's not the turnaround
    1377            8 :         if (in->getTurnDestination() == out) {
    1378              :             // will be added later or not...
    1379              :             return;
    1380              :         }
    1381              : #ifdef DEBUG_CONNECTION_GUESSING
    1382              :         if (DEBUGCOND) {
    1383              :             std::cout << "l2l node=" << getID() << " specialCase d\n";
    1384              :         }
    1385              : #endif
    1386            8 :         const int inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
    1387            8 :         const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
    1388              :         if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
    1389            2 :                 && in->getNumLanes() - inOffset == out->getNumLanes() - outOffset + 1
    1390            2 :                 && in != out
    1391           10 :                 && in->isConnectedTo(out)) {
    1392            7 :             for (int i = inOffset; i < in->getNumLanes(); ++i) {
    1393           10 :                 in->setConnection(i, out, MIN2(outOffset + i, out->getNumLanes() - 1), NBEdge::Lane2LaneInfoType::COMPUTED, true);
    1394              :             }
    1395              :             return;
    1396              :         }
    1397              :     }
    1398              :     // special case f):
    1399              :     //  one in, one out, out has reduced or same number of lanes
    1400        57861 :     if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
    1401         7097 :         NBEdge* in = myIncomingEdges[0];
    1402         7097 :         NBEdge* out = myOutgoingEdges[0];
    1403              :         // check if it's not the turnaround
    1404         7097 :         if (in->getTurnDestination() == out) {
    1405              :             // will be added later or not...
    1406         2145 :             return;
    1407              :         }
    1408              :         int inOffset, inEnd, outOffset, outEnd, reduction;
    1409         7097 :         getReduction(in, out, inOffset, inEnd, outOffset, outEnd, reduction);
    1410              :         if (in->getStep() <= NBEdge::EdgeBuildingStep::LANES2EDGES
    1411         3465 :                 && reduction >= 0
    1412         3463 :                 && in != out
    1413        10560 :                 && in->isConnectedTo(out)) {
    1414              : #ifdef DEBUG_CONNECTION_GUESSING
    1415              :             if (DEBUGCOND) {
    1416              :                 std::cout << "l2l node=" << getID() << " specialCase f inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << " reduction=" << reduction << "\n";
    1417              :             }
    1418              : #endif
    1419              :             // in case of reduced lane number, let the rightmost lanes end
    1420         2145 :             inOffset += reduction;
    1421         5574 :             for (int i = outOffset; i < outEnd; ++i) {
    1422         6858 :                 in->setConnection(i + inOffset - outOffset, out, i, NBEdge::Lane2LaneInfoType::COMPUTED);
    1423              :             }
    1424              :             //std::cout << " special case f at node=" << getID() << " inOffset=" << inOffset << " outOffset=" << outOffset << "\n";
    1425         2145 :             recheckVClassConnections(out);
    1426         2145 :             return;
    1427              :         }
    1428              :     }
    1429              : 
    1430              :     // go through this node's outgoing edges
    1431              :     //  for every outgoing edge, compute the distribution of the node's
    1432              :     //  incoming edges on this edge when approaching this edge
    1433              :     // the incoming edges' steps will then also be marked as LANE2LANE_RECHECK...
    1434              :     EdgeVector approaching;
    1435       167993 :     for (NBEdge* currentOutgoing : myOutgoingEdges) {
    1436              :         // get the information about edges that do approach this edge
    1437       112277 :         getEdgesThatApproach(currentOutgoing, approaching);
    1438       112277 :         const int numApproaching = (int)approaching.size();
    1439       112277 :         if (numApproaching != 0) {
    1440        92569 :             ApproachingDivider divider(approaching, currentOutgoing);
    1441        92569 :             Bresenham::compute(&divider, numApproaching, divider.numAvailableLanes());
    1442        92569 :         }
    1443              : #ifdef DEBUG_CONNECTION_GUESSING
    1444              :         if (DEBUGCOND) {
    1445              :             std::cout << "l2l node=" << getID() << " outgoing=" << currentOutgoing->getID() << " bresenham:\n";
    1446              :             for (NBEdge* e : myIncomingEdges) {
    1447              :                 const std::vector<NBEdge::Connection>& elv = e->getConnections();
    1448              :                 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
    1449              :                     std::cout << "  " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
    1450              :                 }
    1451              :             }
    1452              :         }
    1453              : #endif
    1454       112277 :         recheckVClassConnections(currentOutgoing);
    1455              : 
    1456              :         // in case of lane change restrictions on the outgoing edge, ensure that
    1457              :         // all its lanes can be reached from each connected incoming edge
    1458              :         bool targetProhibitsChange = false;
    1459       254644 :         for (int i = 0; i < currentOutgoing->getNumLanes(); i++) {
    1460       142389 :             const NBEdge::Lane& lane = currentOutgoing->getLanes()[i];
    1461          234 :             if ((lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && i + 1 < currentOutgoing->getNumLanes())
    1462       142603 :                     || (lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && i > 0)) {
    1463              :                 targetProhibitsChange = true;
    1464              :                 break;
    1465              :             }
    1466              :         }
    1467       112277 :         if (targetProhibitsChange) {
    1468              :             //std::cout << " node=" << getID() << " outgoing=" << currentOutgoing->getID() << " targetProhibitsChange\n";
    1469           56 :             for (NBEdge* incoming : myIncomingEdges) {
    1470           34 :                 if (incoming->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
    1471              :                     std::map<int, int> outToIn;
    1472          116 :                     for (const NBEdge::Connection& c : incoming->getConnections()) {
    1473           91 :                         if (c.toEdge == currentOutgoing) {
    1474           25 :                             outToIn[c.toLane] = c.fromLane;
    1475              :                         }
    1476              :                     }
    1477           80 :                     for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); toLane++) {
    1478              :                         if (outToIn.count(toLane) == 0) {
    1479              :                             bool added = false;
    1480              :                             // find incoming lane for neighboring outgoing
    1481           42 :                             for (int i = 0; i < toLane; i++) {
    1482              :                                 if (outToIn.count(i) != 0) {
    1483              : #ifdef DEBUG_CONNECTION_GUESSING
    1484              :                                     if (DEBUGCOND) {
    1485              :                                         std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, secondTarget)\n";
    1486              :                                     }
    1487              : #endif
    1488            8 :                                     incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
    1489              :                                     added = true;
    1490              :                                     break;
    1491              :                                 }
    1492              :                             }
    1493              :                             if (!added) {
    1494           64 :                                 for (int i = toLane; i < currentOutgoing->getNumLanes(); i++) {
    1495              :                                     if (outToIn.count(i) != 0) {
    1496              : #ifdef DEBUG_CONNECTION_GUESSING
    1497              :                                         if (DEBUGCOND) {
    1498              :                                             std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, newTarget)\n";
    1499              :                                         }
    1500              : #endif
    1501           10 :                                         incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
    1502              :                                         added = true;
    1503           10 :                                         break;
    1504              :                                     }
    1505              :                                 }
    1506              :                             }
    1507              :                         }
    1508              :                     }
    1509              :                 }
    1510              :             }
    1511              :         }
    1512              :     }
    1513              :     // special case e): rail_crossing
    1514              :     // there should only be straight connections here
    1515        55716 :     if (myType == SumoXMLNodeType::RAIL_CROSSING) {
    1516          298 :         for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    1517          233 :             const std::vector<NBEdge::Connection> cons = (*i)->getConnections();
    1518          430 :             for (std::vector<NBEdge::Connection>::const_iterator k = cons.begin(); k != cons.end(); ++k) {
    1519          197 :                 if (getDirection(*i, (*k).toEdge) == LinkDirection::TURN) {
    1520            0 :                     (*i)->removeFromConnections((*k).toEdge);
    1521              :                 }
    1522              :             }
    1523          233 :         }
    1524              :     }
    1525              : 
    1526              :     // ... but we may have the case that there are no outgoing edges
    1527              :     //  In this case, we have to mark the incoming edges as being in state
    1528              :     //   LANE2LANE( not RECHECK) by hand
    1529        55716 :     if (myOutgoingEdges.size() == 0) {
    1530        11766 :         for (NBEdge* incoming : myIncomingEdges) {
    1531         6279 :             incoming->markAsInLane2LaneState();
    1532              :         }
    1533              :     }
    1534              : 
    1535              : #ifdef DEBUG_CONNECTION_GUESSING
    1536              :     if (DEBUGCOND) {
    1537              :         std::cout << "final connections at " << getID() << "\n";
    1538              :         for (NBEdge* e : myIncomingEdges) {
    1539              :             const std::vector<NBEdge::Connection>& elv = e->getConnections();
    1540              :             for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
    1541              :                 std::cout << "  " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
    1542              :             }
    1543              :         }
    1544              :     }
    1545              : #endif
    1546        55716 : }
    1547              : 
    1548              : void
    1549       114488 : NBNode::recheckVClassConnections(NBEdge* currentOutgoing) {
    1550              :     // ensure that all modes have a connection if possible
    1551       408812 :     for (NBEdge* incoming : myIncomingEdges) {
    1552       498650 :         if (incoming->getConnectionLanes(currentOutgoing).size() > 0 && incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
    1553              :             // no connections are needed for pedestrians during this step
    1554              :             // no satisfaction is possible if the outgoing edge disallows
    1555        89998 :             SVCPermissions unsatisfied = incoming->getPermissions() & currentOutgoing->getPermissions() & ~SVC_PEDESTRIAN;
    1556              :             //std::cout << "initial unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
    1557              :             const std::vector<NBEdge::Connection>& elv = incoming->getConnections();
    1558       325083 :             for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
    1559              :                 const NBEdge::Connection& c = *k;
    1560       235085 :                 if (c.toEdge == currentOutgoing && c.toLane >= 0) {
    1561       100994 :                     const SVCPermissions satisfied = (incoming->getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane));
    1562              :                     //std::cout << "  from=" << incoming->getID() << "_" << c.fromLane << " to=" << c.toEdge->getID() << "_" << c.toLane << " satisfied=" << getVehicleClassNames(satisfied) << "\n";
    1563       100994 :                     unsatisfied &= ~satisfied;
    1564              :                 }
    1565              :             }
    1566        89998 :             if (unsatisfied != 0) {
    1567              : #ifdef DEBUG_CONNECTION_GUESSING
    1568              :                 if (DEBUGCOND) {
    1569              :                     std::cout << " unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
    1570              :                 }
    1571              : #endif
    1572              :                 int fromLane = 0;
    1573              :                 // first attempt: try to use a dedicated fromLane
    1574         3401 :                 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
    1575         2226 :                     if (incoming->getPermissions(fromLane) == unsatisfied) {
    1576          864 :                         unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
    1577              :                     }
    1578         2226 :                     fromLane++;
    1579              :                 }
    1580              :                 // second attempt: try to re-use a fromLane that already connects to currentOutgoing
    1581              :                 // (because we don't wont to create extra turn lanes)
    1582              :                 fromLane = 0;
    1583         1741 :                 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
    1584          566 :                     if ((incoming->getPermissions(fromLane) & unsatisfied) != 0
    1585          815 :                             && incoming->getConnectionsFromLane(fromLane, currentOutgoing, -1).size() > 0) {
    1586          249 :                         unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
    1587              :                     }
    1588          566 :                     fromLane++;
    1589              :                 }
    1590              :                 // third attempt: use any possible fromLane
    1591              :                 fromLane = 0;
    1592         1300 :                 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
    1593          125 :                     if ((incoming->getPermissions(fromLane) & unsatisfied) != 0) {
    1594           64 :                         unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
    1595              :                     }
    1596          125 :                     fromLane++;
    1597              :                 }
    1598              : #ifdef DEBUG_CONNECTION_GUESSING
    1599              :                 if (DEBUGCOND) {
    1600              :                     if (unsatisfied != 0) {
    1601              :                         std::cout << "     still unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
    1602              :                     }
    1603              :                 }
    1604              : #endif
    1605              :             }
    1606              :         }
    1607              :         // prevent dead-end bus and bicycle lanes (they were excluded by the ApproachingDivider)
    1608              :         // and the bus/bicycle class might already be satisfied by other lanes
    1609       294324 :         recheckSpecialConnections(incoming, currentOutgoing, SVC_BUS);
    1610       294324 :         recheckSpecialConnections(incoming, currentOutgoing, SVC_BICYCLE);
    1611              :     }
    1612       114488 : }
    1613              : 
    1614              : 
    1615              : void
    1616       588648 : NBNode::recheckSpecialConnections(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial) {
    1617              :     // assume that left-turns and turn-arounds are better satisfied from lanes to the left
    1618       588648 :     const int specialTarget = currentOutgoing->getSpecialLane(svcSpecial);
    1619       588648 :     const LinkDirection dir = getDirection(incoming, currentOutgoing);
    1620              :     if (incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE
    1621       588648 :             && ((specialTarget >= 0 && dir != LinkDirection::TURN)
    1622       322391 :                 || dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT)) {
    1623              :         bool builtConnection = false;
    1624       394107 :         for (int i = 0; i < (int)incoming->getNumLanes(); i++) {
    1625       223271 :             if (incoming->getPermissions(i) == svcSpecial
    1626       223794 :                     && incoming->getConnectionsFromLane(i, currentOutgoing).size() == 0) {
    1627              :                 // find a dedicated bike lane as target
    1628          523 :                 if (specialTarget >= 0) {
    1629          174 :                     incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
    1630              : #ifdef DEBUG_CONNECTION_GUESSING
    1631              :                     if (DEBUGCOND) {
    1632              :                         std::cout << "  extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (dedicated) to=" << currentOutgoing->getLaneID(specialTarget)  << "\n";
    1633              :                     }
    1634              : #endif
    1635              :                     builtConnection = true;
    1636              :                 } else {
    1637              :                     // do not create turns that create a conflict with neighboring lanes
    1638          436 :                     if (avoidConfict(incoming, currentOutgoing, svcSpecial, dir, i)) {
    1639           50 :                         continue;
    1640              :                     }
    1641              :                     // use any lane that allows the special class
    1642          780 :                     for (int i2 = 0; i2 < (int)currentOutgoing->getNumLanes(); i2++) {
    1643          527 :                         if ((currentOutgoing->getPermissions(i2) & svcSpecial) != 0) {
    1644              :                             // possibly a double-connection
    1645          133 :                             const bool allowDouble = (incoming->getPermissions(i) == svcSpecial
    1646          133 :                                                       && (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT || dir == LinkDirection::STRAIGHT));
    1647          133 :                             incoming->setConnection(i, currentOutgoing, i2, NBEdge::Lane2LaneInfoType::COMPUTED, allowDouble);
    1648              : #ifdef DEBUG_CONNECTION_GUESSING
    1649              :                             if (DEBUGCOND) {
    1650              :                                 std::cout << "  extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " to=" << currentOutgoing->getLaneID(i2)  << "\n";
    1651              :                             }
    1652              : #endif
    1653              :                             builtConnection = true;
    1654          133 :                             break;
    1655              :                         }
    1656              :                     }
    1657              :                 }
    1658              :             }
    1659              :         }
    1660       341672 :         if (!builtConnection && specialTarget >= 0
    1661       171295 :                 && incoming->getConnectionsFromLane(-1, currentOutgoing, specialTarget).size() == 0) {
    1662              :             // find origin lane that allows bicycles
    1663              :             int start = 0;
    1664              :             int end = incoming->getNumLanes();
    1665              :             int inc = 1;
    1666          459 :             if (dir == LinkDirection::TURN || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) {
    1667              :                 std::swap(start, end);
    1668              :                 inc = -1;
    1669              :             }
    1670          829 :             for (int i = start; i < end; i += inc) {
    1671          378 :                 if ((incoming->getPermissions(i) & svcSpecial) != 0) {
    1672            8 :                     incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
    1673              : #ifdef DEBUG_CONNECTION_GUESSING
    1674              :                     if (DEBUGCOND) {
    1675              :                         std::cout << "  extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (final) to=" << currentOutgoing->getLaneID(specialTarget)  << "\n";
    1676              :                     }
    1677              : #endif
    1678            8 :                     break;
    1679              :                 }
    1680              :             }
    1681              :         }
    1682              :     }
    1683       588648 : }
    1684              : 
    1685              : 
    1686              : bool
    1687          436 : NBNode::avoidConfict(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial, LinkDirection dir, int i) {
    1688         2007 :     for (const auto& c : incoming->getConnections()) {
    1689         1578 :         if (incoming->getPermissions(c.fromLane) == svcSpecial && c.toEdge == currentOutgoing) {
    1690              :             return true;
    1691              :         }
    1692              :     }
    1693          429 :     if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
    1694         1179 :         for (const auto& c : incoming->getConnections()) {
    1695          931 :             if (c.fromLane < i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
    1696              :                 return true;
    1697              :             }
    1698              :         }
    1699              :     } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
    1700              :         for (const auto& c : incoming->getConnections()) {
    1701              :             if (c.fromLane > i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
    1702              :                 return true;
    1703              :             }
    1704              :         }
    1705          157 :     } else if (svcSpecial != SVC_BICYCLE && dir == LinkDirection::STRAIGHT) {
    1706           70 :         for (const auto& c : incoming->getConnections()) {
    1707           59 :             const LinkDirection dir2 = getDirection(incoming, c.toEdge);
    1708           59 :             if (c.fromLane < i && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT)) {
    1709              :                 return true;
    1710           49 :             } else if (c.fromLane > i && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)) {
    1711              :                 return true;
    1712              :             }
    1713              :         }
    1714              :     }
    1715              :     return false;
    1716              : }
    1717              : 
    1718              : 
    1719              : void
    1720        14789 : NBNode::getReduction(const NBEdge* in, const NBEdge* out, int& inOffset, int& inEnd, int& outOffset, int& outEnd, int& reduction) const {
    1721        14789 :     inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1722        14789 :     outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1723        14789 :     inEnd = in->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
    1724        14789 :     outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
    1725        14789 :     reduction = (inEnd - inOffset) - (outEnd - outOffset);
    1726        14789 : }
    1727              : 
    1728              : 
    1729              : SVCPermissions
    1730         1177 : NBNode::findToLaneForPermissions(NBEdge* currentOutgoing, int fromLane, NBEdge* incoming, SVCPermissions unsatisfied) {
    1731         4528 :     for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); ++toLane) {
    1732         3351 :         const SVCPermissions satisfied = incoming->getPermissions(fromLane) & currentOutgoing->getPermissions(toLane) & unsatisfied;
    1733         3351 :         if (satisfied != 0 && !incoming->getLaneStruct(fromLane).connectionsDone) {
    1734         1177 :             if (incoming->hasConnectionTo(currentOutgoing, toLane)
    1735          285 :                     && unsatisfied == SVC_TRAM
    1736         1183 :                     && incoming->getPermissions(fromLane) == currentOutgoing->getPermissions(toLane)) {
    1737              :                 // avoid double tram connection by shifting an existing connection
    1738           11 :                 for (auto con : incoming->getConnections()) {
    1739           11 :                     if (con.toEdge == currentOutgoing && con.toLane == toLane) {
    1740              : #ifdef DEBUG_CONNECTION_GUESSING
    1741              :                         if (DEBUGCOND) {
    1742              :                             std::cout << "  shifting connection from=" << con.fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << ": newFromLane=" << fromLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
    1743              :                         }
    1744              : #endif
    1745            5 :                         incoming->getConnectionRef(con.fromLane, con.toEdge, toLane).fromLane = fromLane;
    1746              :                         unsatisfied &= ~satisfied;
    1747              :                         break;
    1748              :                     }
    1749           11 :                 }
    1750              :             } else {
    1751              :                 // other modes (i.e. bus) can fix lane permissions NBPTLineCont::fixPermissions but do not wish to create parallel tram tracks here
    1752         1172 :                 bool mayUseSameDestination = unsatisfied == SVC_TRAM || (unsatisfied & SVC_PASSENGER) != 0;
    1753         1172 :                 incoming->setConnection((int)fromLane, currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED, mayUseSameDestination);
    1754              : #ifdef DEBUG_CONNECTION_GUESSING
    1755              :                 if (DEBUGCOND) {
    1756              :                     std::cout << "  new connection from=" << fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
    1757              :                 }
    1758              : #endif
    1759         1172 :                 unsatisfied &= ~satisfied;
    1760              :             }
    1761              :         }
    1762              :     }
    1763         1177 :     return unsatisfied;
    1764              : }
    1765              : 
    1766              : 
    1767              : int
    1768          549 : NBNode::addedLanesRight(NBEdge* out, int addedLanes) const {
    1769          549 :     if (out->isOffRamp()) {
    1770              :         return addedLanes;
    1771              :     }
    1772              :     NBNode* to = out->getToNode();
    1773              :     // check whether a right lane ends
    1774              :     if (to->getIncomingEdges().size() == 1
    1775          541 :             && to->getOutgoingEdges().size() == 1) {
    1776              :         int inOffset, inEnd, outOffset, outEnd, reduction;
    1777           44 :         to->getReduction(out, to->getOutgoingEdges()[0], inOffset, inEnd, outOffset, outEnd, reduction);
    1778              : 
    1779           44 :         if (reduction > 0) {
    1780            8 :             return reduction;
    1781              :         }
    1782              :     }
    1783              :     // check for the presence of right and left turns at the next intersection
    1784              :     int outLanesRight = 0;
    1785              :     int outLanesLeft = 0;
    1786              :     int outLanesStraight = 0;
    1787         2374 :     for (NBEdge* succ : to->getOutgoingEdges()) {
    1788         1841 :         if (out->isConnectedTo(succ)) {
    1789         1705 :             const int outOffset = MAX2(0, succ->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1790         1705 :             const int usableLanes = succ->getNumLanes() - outOffset;
    1791         1705 :             LinkDirection dir = to->getDirection(out, succ);
    1792         1705 :             if (dir == LinkDirection::STRAIGHT) {
    1793          494 :                 outLanesStraight += usableLanes;
    1794         1211 :             } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
    1795          426 :                 outLanesRight += usableLanes;
    1796              :             } else {
    1797          785 :                 outLanesLeft += usableLanes;
    1798              :             }
    1799              :         }
    1800              :     }
    1801          533 :     const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
    1802          533 :     const int outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
    1803          533 :     const int usableLanes = outEnd - outOffset;
    1804          533 :     int addedTurnLanes = MIN3(
    1805              :                              addedLanes,
    1806              :                              MAX2(0, usableLanes - outLanesStraight),
    1807              :                              outLanesRight + outLanesLeft);
    1808              : #ifdef DEBUG_CONNECTION_GUESSING
    1809              :     if (DEBUGCOND) {
    1810              :         std::cout << "out=" << out->getID() << " usableLanes=" << usableLanes << " addedTurnLanes=" << addedTurnLanes << " addedLanes=" << addedLanes << " outLanesStraight=" << outLanesStraight << " outLanesLeft=" << outLanesLeft << " outLanesRight=" << outLanesRight << "\n";
    1811              :     }
    1812              : #endif
    1813          533 :     if (outLanesLeft == 0) {
    1814              :         return addedTurnLanes;
    1815              :     } else {
    1816          416 :         return MIN2(addedTurnLanes / 2, outLanesRight);
    1817              :     }
    1818              : }
    1819              : 
    1820              : 
    1821              : bool
    1822           44 : NBNode::isLongEnough(NBEdge* out, double minLength) {
    1823              :     double seen = out->getLoadedLength();
    1824           45 :     while (seen < minLength) {
    1825              :         // advance along trivial continuations
    1826              :         if (out->getToNode()->getOutgoingEdges().size() != 1
    1827           20 :                 || out->getToNode()->getIncomingEdges().size() != 1) {
    1828              :             return false;
    1829              :         } else {
    1830            1 :             out = out->getToNode()->getOutgoingEdges()[0];
    1831            1 :             seen += out->getLoadedLength();
    1832              :         }
    1833              :     }
    1834              :     return true;
    1835              : }
    1836              : 
    1837              : 
    1838              : void
    1839       112277 : NBNode::getEdgesThatApproach(NBEdge* currentOutgoing, EdgeVector& approaching) {
    1840              :     // get the position of the node to get the approaching nodes of
    1841       112277 :     EdgeVector::const_iterator i = std::find(myAllEdges.begin(),
    1842              :                                    myAllEdges.end(), currentOutgoing);
    1843              :     // get the first possible approaching edge
    1844       112277 :     NBContHelper::nextCW(myAllEdges, i);
    1845              :     // go through the list of edges clockwise and add the edges
    1846              :     approaching.clear();
    1847       599973 :     for (; *i != currentOutgoing;) {
    1848              :         // check only incoming edges
    1849       487696 :         if ((*i)->getToNode() == this && (*i)->getTurnDestination() != currentOutgoing) {
    1850       241376 :             std::vector<int> connLanes = (*i)->getConnectionLanes(currentOutgoing);
    1851       241376 :             if (connLanes.size() != 0) {
    1852       174771 :                 approaching.push_back(*i);
    1853              :             }
    1854       241376 :         }
    1855       487696 :         NBContHelper::nextCW(myAllEdges, i);
    1856              :     }
    1857       112277 : }
    1858              : 
    1859              : 
    1860              : void
    1861          811 : NBNode::replaceOutgoing(NBEdge* which, NBEdge* by, int laneOff) {
    1862              :     // replace the edge in the list of outgoing nodes
    1863          811 :     EdgeVector::iterator i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), which);
    1864          811 :     if (i != myOutgoingEdges.end()) {
    1865          811 :         (*i) = by;
    1866          811 :         i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
    1867          811 :         (*i) = by;
    1868              :     }
    1869              :     // replace the edge in connections of incoming edges
    1870         2015 :     for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); ++i) {
    1871         1204 :         (*i)->replaceInConnections(which, by, laneOff);
    1872              :     }
    1873              :     // replace within the connetion prohibition dependencies
    1874          811 :     replaceInConnectionProhibitions(which, by, 0, laneOff);
    1875          811 : }
    1876              : 
    1877              : 
    1878              : void
    1879           11 : NBNode::replaceOutgoing(const EdgeVector& which, NBEdge* by) {
    1880              :     // replace edges
    1881              :     int laneOff = 0;
    1882           33 :     for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
    1883           22 :         replaceOutgoing(*i, by, laneOff);
    1884           22 :         laneOff += (*i)->getNumLanes();
    1885              :     }
    1886              :     // removed double occurrences
    1887           11 :     removeDoubleEdges();
    1888              :     // check whether this node belongs to a district and the edges
    1889              :     //  must here be also remapped
    1890           11 :     if (myDistrict != nullptr) {
    1891            0 :         myDistrict->replaceOutgoing(which, by);
    1892              :     }
    1893           11 : }
    1894              : 
    1895              : 
    1896              : void
    1897         6323 : NBNode::replaceIncoming(NBEdge* which, NBEdge* by, int laneOff) {
    1898              :     // replace the edge in the list of incoming nodes
    1899         6323 :     EdgeVector::iterator i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), which);
    1900         6323 :     if (i != myIncomingEdges.end()) {
    1901         6323 :         (*i) = by;
    1902         6323 :         i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
    1903         6323 :         (*i) = by;
    1904              :     }
    1905              :     // replace within the connetion prohibition dependencies
    1906         6323 :     replaceInConnectionProhibitions(which, by, laneOff, 0);
    1907         6323 : }
    1908              : 
    1909              : 
    1910              : void
    1911           11 : NBNode::replaceIncoming(const EdgeVector& which, NBEdge* by) {
    1912              :     // replace edges
    1913              :     int laneOff = 0;
    1914           33 :     for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
    1915           22 :         replaceIncoming(*i, by, laneOff);
    1916           22 :         laneOff += (*i)->getNumLanes();
    1917              :     }
    1918              :     // removed double occurrences
    1919           11 :     removeDoubleEdges();
    1920              :     // check whether this node belongs to a district and the edges
    1921              :     //  must here be also remapped
    1922           11 :     if (myDistrict != nullptr) {
    1923            0 :         myDistrict->replaceIncoming(which, by);
    1924              :     }
    1925           11 : }
    1926              : 
    1927              : 
    1928              : 
    1929              : void
    1930         7134 : NBNode::replaceInConnectionProhibitions(NBEdge* which, NBEdge* by,
    1931              :                                         int whichLaneOff, int byLaneOff) {
    1932              :     // replace in keys
    1933              :     NBConnectionProhibits::iterator j = myBlockedConnections.begin();
    1934         7334 :     while (j != myBlockedConnections.end()) {
    1935              :         bool changed = false;
    1936          200 :         NBConnection c = (*j).first;
    1937          200 :         if (c.replaceFrom(which, whichLaneOff, by, byLaneOff)) {
    1938              :             changed = true;
    1939              :         }
    1940          200 :         if (c.replaceTo(which, whichLaneOff, by, byLaneOff)) {
    1941              :             changed = true;
    1942              :         }
    1943          200 :         if (changed) {
    1944           17 :             myBlockedConnections[c] = (*j).second;
    1945              :             myBlockedConnections.erase(j);
    1946              :             j = myBlockedConnections.begin();
    1947              :         } else {
    1948              :             j++;
    1949              :         }
    1950          200 :     }
    1951              :     // replace in values
    1952         7232 :     for (j = myBlockedConnections.begin(); j != myBlockedConnections.end(); j++) {
    1953              :         NBConnectionVector& prohibiting = (*j).second;
    1954          322 :         for (NBConnectionVector::iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
    1955              :             NBConnection& sprohibiting = *k;
    1956          224 :             sprohibiting.replaceFrom(which, whichLaneOff, by, byLaneOff);
    1957          224 :             sprohibiting.replaceTo(which, whichLaneOff, by, byLaneOff);
    1958              :         }
    1959              :     }
    1960         7134 : }
    1961              : 
    1962              : 
    1963              : 
    1964              : void
    1965         1600 : NBNode::removeDoubleEdges() {
    1966              :     // check incoming
    1967         3439 :     for (int i = 0; myIncomingEdges.size() > 0 && i < (int)myIncomingEdges.size() - 1; i++) {
    1968         1839 :         int j = i + 1;
    1969         5188 :         while (j < (int)myIncomingEdges.size()) {
    1970         3349 :             if (myIncomingEdges[i] == myIncomingEdges[j]) {
    1971          811 :                 myIncomingEdges.erase(myIncomingEdges.begin() + j);
    1972              :             } else {
    1973         2538 :                 j++;
    1974              :             }
    1975              :         }
    1976              :     }
    1977              :     // check outgoing
    1978         3463 :     for (int i = 0; myOutgoingEdges.size() > 0 && i < (int)myOutgoingEdges.size() - 1; i++) {
    1979         1863 :         int j = i + 1;
    1980         4947 :         while (j < (int)myOutgoingEdges.size()) {
    1981         3084 :             if (myOutgoingEdges[i] == myOutgoingEdges[j]) {
    1982          811 :                 myOutgoingEdges.erase(myOutgoingEdges.begin() + j);
    1983              :             } else {
    1984         2273 :                 j++;
    1985              :             }
    1986              :         }
    1987              :     }
    1988              :     // check all
    1989         6244 :     for (int i = 0; myAllEdges.size() > 0 && i < (int)myAllEdges.size() - 1; i++) {
    1990         4644 :         int j = i + 1;
    1991        18879 :         while (j < (int)myAllEdges.size()) {
    1992        14235 :             if (myAllEdges[i] == myAllEdges[j]) {
    1993         1622 :                 myAllEdges.erase(myAllEdges.begin() + j);
    1994              :             } else {
    1995        12613 :                 j++;
    1996              :             }
    1997              :         }
    1998              :     }
    1999         1600 : }
    2000              : 
    2001              : 
    2002              : bool
    2003       100885 : NBNode::hasIncoming(const NBEdge* const e) const {
    2004       100885 :     return std::find(myIncomingEdges.begin(), myIncomingEdges.end(), e) != myIncomingEdges.end();
    2005              : }
    2006              : 
    2007              : 
    2008              : bool
    2009        14782 : NBNode::hasOutgoing(const NBEdge* const e) const {
    2010        14782 :     return std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), e) != myOutgoingEdges.end();
    2011              : }
    2012              : 
    2013              : 
    2014              : NBEdge*
    2015        20423 : NBNode::getOppositeIncoming(NBEdge* e) const {
    2016        20423 :     EdgeVector edges = myIncomingEdges;
    2017        20423 :     if (find(edges.begin(), edges.end(), e) != edges.end()) {
    2018        20423 :         edges.erase(find(edges.begin(), edges.end(), e));
    2019              :     }
    2020        20423 :     if (edges.size() == 0) {
    2021              :         return nullptr;
    2022              :     }
    2023        20423 :     if (e->getToNode() == this) {
    2024        20423 :         sort(edges.begin(), edges.end(), NBContHelper::edge_opposite_direction_sorter(e, this, false));
    2025              :     } else {
    2026            0 :         sort(edges.begin(), edges.end(), NBContHelper::edge_similar_direction_sorter(e));
    2027              :     }
    2028        20423 :     return edges[0];
    2029        20423 : }
    2030              : 
    2031              : 
    2032              : void
    2033          199 : NBNode::addSortedLinkFoes(const NBConnection& mayDrive,
    2034              :                           const NBConnection& mustStop) {
    2035          398 :     if (mayDrive.getFrom() == nullptr ||
    2036          398 :             mayDrive.getTo() == nullptr ||
    2037          597 :             mustStop.getFrom() == nullptr ||
    2038          199 :             mustStop.getTo() == nullptr) {
    2039              : 
    2040            0 :         WRITE_WARNING(TL("Something went wrong during the building of a connection..."));
    2041            0 :         return; // !!! mark to recompute connections
    2042              :     }
    2043          199 :     NBConnectionVector conn = myBlockedConnections[mustStop];
    2044          199 :     conn.push_back(mayDrive);
    2045          199 :     myBlockedConnections[mustStop] = conn;
    2046          199 : }
    2047              : 
    2048              : 
    2049              : NBEdge*
    2050          266 : NBNode::getPossiblySplittedIncoming(const std::string& edgeid) {
    2051          266 :     int size = (int) edgeid.length();
    2052         1042 :     for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    2053         1042 :         std::string id = (*i)->getID();
    2054         1042 :         if (id.substr(0, size) == edgeid) {
    2055          266 :             return *i;
    2056              :         }
    2057              :     }
    2058              :     return nullptr;
    2059              : }
    2060              : 
    2061              : 
    2062              : NBEdge*
    2063          266 : NBNode::getPossiblySplittedOutgoing(const std::string& edgeid) {
    2064          266 :     int size = (int) edgeid.length();
    2065          605 :     for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
    2066          549 :         std::string id = (*i)->getID();
    2067          549 :         if (id.substr(0, size) == edgeid) {
    2068          210 :             return *i;
    2069              :         }
    2070              :     }
    2071              :     return nullptr;
    2072              : }
    2073              : 
    2074              : 
    2075              : void
    2076        52971 : NBNode::removeEdge(NBEdge* edge, bool removeFromConnections) {
    2077        52971 :     EdgeVector::iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), edge);
    2078        52971 :     if (i != myAllEdges.end()) {
    2079        45807 :         myAllEdges.erase(i);
    2080        45807 :         i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge);
    2081        45807 :         if (i != myOutgoingEdges.end()) {
    2082        25746 :             myOutgoingEdges.erase(i);
    2083              :             // potential self-loop
    2084        25746 :             i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
    2085        25746 :             if (i != myIncomingEdges.end()) {
    2086            0 :                 myIncomingEdges.erase(i);
    2087              :             }
    2088              :         } else {
    2089        20061 :             i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
    2090        20061 :             if (i != myIncomingEdges.end()) {
    2091        20061 :                 myIncomingEdges.erase(i);
    2092              :             } else {
    2093              :                 // edge must have been either incoming or outgoing
    2094              :                 assert(false);
    2095              :             }
    2096              :         }
    2097        45807 :         if (removeFromConnections) {
    2098       105716 :             for (i = myAllEdges.begin(); i != myAllEdges.end(); ++i) {
    2099        66246 :                 (*i)->removeFromConnections(edge);
    2100              :             }
    2101              :         }
    2102              :         // invalidate controlled connections for loaded traffic light plans
    2103        45807 :         const bool incoming = edge->getToNode() == this;
    2104        49472 :         for (NBTrafficLightDefinition* const tld : myTrafficLights) {
    2105         3665 :             tld->replaceRemoved(edge, -1, nullptr, -1, incoming);
    2106              :         }
    2107              :     }
    2108        52971 : }
    2109              : 
    2110              : 
    2111              : Position
    2112            0 : NBNode::getEmptyDir() const {
    2113              :     Position pos(0, 0);
    2114            0 :     for (const NBEdge* const in : myIncomingEdges) {
    2115            0 :         Position toAdd = in->getFromNode()->getPosition();
    2116              :         toAdd.sub(myPosition);
    2117            0 :         toAdd.norm2D();
    2118              :         pos.add(toAdd);
    2119              :     }
    2120            0 :     for (const NBEdge* const out : myOutgoingEdges) {
    2121            0 :         Position toAdd = out->getToNode()->getPosition();
    2122              :         toAdd.sub(myPosition);
    2123            0 :         toAdd.norm2D();
    2124              :         pos.add(toAdd);
    2125              :     }
    2126            0 :     pos.mul(-1. / (double)(myIncomingEdges.size() + myOutgoingEdges.size()));
    2127            0 :     if (pos.x() == 0. && pos.y() == 0.) {
    2128            0 :         pos = Position(1, 0);
    2129              :     }
    2130            0 :     pos.norm2D();
    2131            0 :     return pos;
    2132              : }
    2133              : 
    2134              : 
    2135              : 
    2136              : void
    2137            0 : NBNode::invalidateIncomingConnections(bool reallowSetting) {
    2138            0 :     for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    2139            0 :         (*i)->invalidateConnections(reallowSetting);
    2140              :     }
    2141            0 : }
    2142              : 
    2143              : 
    2144              : void
    2145           27 : NBNode::invalidateOutgoingConnections(bool reallowSetting) {
    2146           93 :     for (EdgeVector::const_iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
    2147           66 :         (*i)->invalidateConnections(reallowSetting);
    2148              :     }
    2149           27 : }
    2150              : 
    2151              : 
    2152              : bool
    2153       271558 : NBNode::mustBrake(const NBEdge* const from, const NBEdge* const to, int fromLane, int toLane, bool includePedCrossings) const {
    2154              :     // unregulated->does not need to brake
    2155       271558 :     if (myRequest == nullptr) {
    2156              :         return false;
    2157              :     }
    2158              :     // vehicles which do not have a following lane must always decelerate to the end
    2159       270526 :     if (to == nullptr) {
    2160              :         return true;
    2161              :     }
    2162              :     // maybe we need to brake due to entering a bidi-edge
    2163       270526 :     if (to->isBidiEdge() && !from->isBidiEdge()) {
    2164              :         return true;
    2165              :     }
    2166              :     // check whether any other connection on this node prohibits this connection
    2167       270337 :     return myRequest->mustBrake(from, to, fromLane, toLane, includePedCrossings);
    2168              : }
    2169              : 
    2170              : bool
    2171         7882 : NBNode::mustBrakeForCrossing(const NBEdge* const from, const NBEdge* const to, const NBNode::Crossing& crossing) const {
    2172         7882 :     return NBRequest::mustBrakeForCrossing(this, from, to, crossing);
    2173              : }
    2174              : 
    2175              : bool
    2176        17294 : NBNode::brakeForCrossingOnExit(const NBEdge* to, LinkDirection dir, bool indirect) const {
    2177              :     // code is called for connections exiting after an internal junction.
    2178              :     // If the connection is turning we do not check for crossing priority anymore.
    2179        17294 :     if (dir == LinkDirection::STRAIGHT && !indirect) {
    2180              :         return false;
    2181              :     }
    2182        19991 :     for (auto& c : myCrossings) {
    2183         4115 :         if (std::find(c->edges.begin(), c->edges.end(), to) != c->edges.end()) {
    2184              :             return true;
    2185              :         }
    2186              :     }
    2187              :     return false;
    2188              : }
    2189              : 
    2190              : 
    2191              : bool
    2192      4945097 : NBNode::rightTurnConflict(const NBEdge* from, const NBEdge* to, int fromLane,
    2193              :                           const NBEdge* prohibitorFrom, const NBEdge* prohibitorTo, int prohibitorFromLane) {
    2194      4945097 :     if (from != prohibitorFrom) {
    2195              :         return false;
    2196              :     }
    2197      1460258 :     if (from->isTurningDirectionAt(to)
    2198      1460258 :             || prohibitorFrom->isTurningDirectionAt(prohibitorTo)) {
    2199              :         // XXX should warn if there are any non-turning connections left of this
    2200       422558 :         return false;
    2201              :     }
    2202              :     // conflict if to is between prohibitorTo and from when going clockwise
    2203      1037700 :     if (to->getStartAngle() == prohibitorTo->getStartAngle()) {
    2204              :         // reduce rounding errors
    2205              :         return false;
    2206              :     }
    2207       555223 :     const LinkDirection d1 = from->getToNode()->getDirection(from, to);
    2208              :     // must be a right turn to qualify as rightTurnConflict
    2209       555223 :     if (d1 == LinkDirection::STRAIGHT) {
    2210              :         // no conflict for straight going connections
    2211              :         // XXX actually this should check the main direction (which could also
    2212              :         // be a turn)
    2213              :         return false;
    2214              :     } else {
    2215       402982 :         const LinkDirection d2 = prohibitorFrom->getToNode()->getDirection(prohibitorFrom, prohibitorTo);
    2216              :         /* std::cout
    2217              :             << "from=" << from->getID() << " to=" << to->getID() << " fromLane=" << fromLane
    2218              :             << " pFrom=" << prohibitorFrom->getID() << " pTo=" << prohibitorTo->getID() << " pFromLane=" << prohibitorFromLane
    2219              :             << " d1=" << toString(d1) << " d2=" << toString(d2)
    2220              :             << "\n"; */
    2221              :         bool flip = false;
    2222       402982 :         if (d1 == LinkDirection::LEFT || d1 == LinkDirection::PARTLEFT) {
    2223              :             // check for leftTurnConflicht
    2224              :             flip = !flip;
    2225       191274 :             if (d2 == LinkDirection::RIGHT || d2 == LinkDirection::PARTRIGHT) {
    2226              :                 // assume that the left-turning bicycle goes straight at first
    2227              :                 // and thus gets precedence over a right turning vehicle
    2228              :                 return false;
    2229              :             }
    2230              :         }
    2231       327208 :         if ((!flip && fromLane <= prohibitorFromLane) ||
    2232       118728 :                 (flip && fromLane >= prohibitorFromLane)) {
    2233              :             return false;
    2234              :         }
    2235         5357 :         const double toAngleAtNode = fmod(to->getStartAngle() + 180, (double)360.0);
    2236         5357 :         const double prohibitorToAngleAtNode = fmod(prohibitorTo->getStartAngle() + 180, (double)360.0);
    2237         5357 :         return (flip != (GeomHelper::getCWAngleDiff(from->getEndAngle(), toAngleAtNode) <
    2238         5357 :                          GeomHelper::getCWAngleDiff(from->getEndAngle(), prohibitorToAngleAtNode)));
    2239              :     }
    2240              : }
    2241              : 
    2242              : bool
    2243          384 : NBNode::mergeConflictYields(const NBEdge* from, int fromLane, int fromLaneFoe, NBEdge* to, int toLane) const {
    2244          384 :     if (myRequest == nullptr) {
    2245              :         return false;
    2246              :     }
    2247          384 :     const NBEdge::Connection& con = from->getConnection(fromLane, to, toLane);
    2248          384 :     const NBEdge::Connection& prohibitorCon = from->getConnection(fromLaneFoe, to, toLane);
    2249          384 :     return myRequest->mergeConflict(from, con, from, prohibitorCon, false);
    2250              : }
    2251              : 
    2252              : 
    2253              : bool
    2254      1594482 : NBNode::mergeConflict(const NBEdge* from, const NBEdge::Connection& con,
    2255              :                       const NBEdge* prohibitorFrom,  const NBEdge::Connection& prohibitorCon, bool foes) const {
    2256      1594482 :     if (myRequest == nullptr) {
    2257              :         return false;
    2258              :     }
    2259      1545086 :     return myRequest->mergeConflict(from, con, prohibitorFrom, prohibitorCon, foes);
    2260              : }
    2261              : 
    2262              : bool
    2263       797241 : NBNode::bidiConflict(const NBEdge* from, const NBEdge::Connection& con,
    2264              :                      const NBEdge* prohibitorFrom,  const NBEdge::Connection& prohibitorCon, bool foes) const {
    2265       797241 :     if (myRequest == nullptr) {
    2266              :         return false;
    2267              :     }
    2268       772543 :     return myRequest->bidiConflict(from, con, prohibitorFrom, prohibitorCon, foes);
    2269              : }
    2270              : 
    2271              : bool
    2272      1780620 : NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
    2273              :                  const NBEdge* from2, const NBEdge* to2, int fromLane2,
    2274              :                  bool lefthand) const {
    2275              :     UNUSED_PARAMETER(lefthand);
    2276      1780620 :     if (from != from2 || to == to2 || fromLane == fromLane2) {
    2277              :         return false;
    2278              :     }
    2279        84594 :     if (from->isTurningDirectionAt(to)
    2280        84594 :             || from2->isTurningDirectionAt(to2)) {
    2281              :         // XXX should warn if there are any non-turning connections left of this
    2282        24978 :         return false;
    2283              :     }
    2284              :     bool result = false;
    2285        59616 :     EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
    2286        59616 :     if (fromLane < fromLane2) {
    2287              :         // conflict if 'to' comes before 'to2' going clockwise starting at 'from'
    2288       120408 :         while (*it != to2) {
    2289        90524 :             if (*it == to) {
    2290              :                 result = true;
    2291              :             }
    2292        90524 :             NBContHelper::nextCW(myAllEdges, it);
    2293              :         }
    2294              :     } else {
    2295              :         // conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
    2296        79932 :         while (*it != to2) {
    2297        50200 :             if (*it == to) {
    2298              :                 result = true;
    2299              :             }
    2300        50200 :             NBContHelper::nextCCW(myAllEdges, it);
    2301              :         }
    2302              :     }
    2303              :     /*
    2304              :     if (result) {
    2305              :         std::cout << "turnFoes node=" << getID()
    2306              :         << " from=" << from->getLaneID(fromLane)
    2307              :         << " to=" << to->getID()
    2308              :         << " from2=" << from2->getLaneID(fromLane2)
    2309              :         << " to2=" << to2->getID()
    2310              :         << "\n";
    2311              :     }
    2312              :     */
    2313              :     return result;
    2314              : }
    2315              : 
    2316              : 
    2317              : bool
    2318         4812 : NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
    2319              :     // when the junction has only one incoming edge, there are no
    2320              :     //  problems caused by left blockings
    2321         4812 :     if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
    2322              :         return false;
    2323              :     }
    2324         4764 :     double fromAngle = from->getAngleAtNode(this);
    2325         4764 :     double toAngle = to->getAngleAtNode(this);
    2326         4764 :     double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
    2327         4764 :     double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
    2328         4764 :     std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
    2329              :     do {
    2330        14292 :         NBContHelper::nextCW(myAllEdges, i);
    2331        19056 :     } while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
    2332         4764 :     return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
    2333              : }
    2334              : 
    2335              : 
    2336              : bool
    2337      1583134 : NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
    2338              :                 const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
    2339              :                 bool regardNonSignalisedLowerPriority) const {
    2340      1583134 :     return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
    2341              :             possProhibitedFrom, possProhibitedTo,
    2342      1583134 :             regardNonSignalisedLowerPriority);
    2343              : }
    2344              : 
    2345              : 
    2346              : bool
    2347      1560506 : NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
    2348              :              const NBEdge* const from2, const NBEdge* const to2) const {
    2349      1560506 :     return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
    2350              : }
    2351              : 
    2352              : 
    2353              : void
    2354            0 : NBNode::remapRemoved(NBTrafficLightLogicCont& tc,
    2355              :                      NBEdge* removed, const EdgeVector& incoming,
    2356              :                      const EdgeVector& outgoing) {
    2357              :     assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
    2358              :     bool changed = true;
    2359            0 :     while (changed) {
    2360              :         changed = false;
    2361              :         NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
    2362              :         NBConnectionProhibits blockedConnectionsNew;
    2363              :         // remap in connections
    2364            0 :         for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
    2365            0 :             const NBConnection& blocker = (*i).first;
    2366            0 :             const NBConnectionVector& blocked = (*i).second;
    2367              :             // check the blocked connections first
    2368              :             // check whether any of the blocked must be changed
    2369              :             bool blockedChanged = false;
    2370              :             NBConnectionVector newBlocked;
    2371              :             NBConnectionVector::const_iterator j;
    2372            0 :             for (j = blocked.begin(); j != blocked.end(); j++) {
    2373              :                 const NBConnection& sblocked = *j;
    2374            0 :                 if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
    2375              :                     blockedChanged = true;
    2376              :                 }
    2377              :             }
    2378              :             // adapt changes if so
    2379            0 :             for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
    2380              :                 const NBConnection& sblocked = *j;
    2381            0 :                 if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
    2382              :                     /*                    for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
    2383              :                     !!!                        newBlocked.push_back(NBConnection(*k, *k));
    2384              :                                         }*/
    2385            0 :                 } else if (sblocked.getFrom() == removed) {
    2386              :                     assert(sblocked.getTo() != removed);
    2387            0 :                     for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
    2388            0 :                         newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
    2389              :                     }
    2390            0 :                 } else if (sblocked.getTo() == removed) {
    2391              :                     assert(sblocked.getFrom() != removed);
    2392            0 :                     for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
    2393            0 :                         newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
    2394              :                     }
    2395              :                 } else {
    2396            0 :                     newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
    2397              :                 }
    2398              :             }
    2399            0 :             if (blockedChanged) {
    2400            0 :                 blockedConnectionsNew[blocker] = newBlocked;
    2401              :                 changed = true;
    2402              :             }
    2403              :             // if the blocked were kept
    2404              :             else {
    2405            0 :                 if (blocker.getFrom() == removed && blocker.getTo() == removed) {
    2406              :                     changed = true;
    2407              :                     /*                    for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
    2408              :                     !!!                        blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
    2409              :                                         }*/
    2410            0 :                 } else if (blocker.getFrom() == removed) {
    2411              :                     assert(blocker.getTo() != removed);
    2412              :                     changed = true;
    2413            0 :                     for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
    2414            0 :                         blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
    2415              :                     }
    2416            0 :                 } else if (blocker.getTo() == removed) {
    2417              :                     assert(blocker.getFrom() != removed);
    2418              :                     changed = true;
    2419            0 :                     for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
    2420            0 :                         blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
    2421              :                     }
    2422              :                 } else {
    2423            0 :                     blockedConnectionsNew[blocker] = blocked;
    2424              :                 }
    2425              :             }
    2426            0 :         }
    2427              :         myBlockedConnections = blockedConnectionsNew;
    2428              :     }
    2429              :     // remap in traffic lights
    2430            0 :     tc.remapRemoved(removed, incoming, outgoing);
    2431            0 : }
    2432              : 
    2433              : 
    2434              : NBEdge*
    2435      4167313 : NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
    2436      4167313 :     EdgeVector::const_iterator i = itOut;
    2437      8633064 :     while (*i != incoming) {
    2438      6873488 :         if (clockwise) {
    2439      2970305 :             NBContHelper::nextCW(myAllEdges, i);
    2440              :         } else {
    2441      3903183 :             NBContHelper::nextCCW(myAllEdges, i);
    2442              :         }
    2443      6873488 :         if ((*i)->getFromNode() != this) {
    2444              :             // only look for outgoing edges
    2445              :             // @note we use myAllEdges to stop at the incoming edge
    2446      4296762 :             continue;
    2447              :         }
    2448      2576726 :         if (incoming->isTurningDirectionAt(*i)) {
    2449              :             return nullptr;
    2450              :         }
    2451      1368766 :         if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
    2452      1199777 :             return *i;
    2453              :         }
    2454              :     }
    2455              :     return nullptr;
    2456              : }
    2457              : 
    2458              : 
    2459              : bool
    2460      1305386 : NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
    2461      1305386 :     if (candidate != nullptr) {
    2462       861250 :         const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
    2463              :         // they are too similar it does not matter
    2464       861250 :         if (fabs(angle - candAngle) < 5.) {
    2465              :             return false;
    2466              :         }
    2467              :         // the other edge is at least 5 degree straighter
    2468       833423 :         if (fabs(candAngle) < fabs(angle) - 5.) {
    2469              :             return true;
    2470              :         }
    2471       730019 :         if (fabs(angle) < fabs(candAngle) - 5.) {
    2472              :             return false;
    2473              :         }
    2474        31996 :         if (fabs(candAngle) < 44.) {
    2475              :             // the lane count for the same modes is larger
    2476        30469 :             const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
    2477        30469 :             if (candModeLanes > modeLanes) {
    2478              :                 return true;
    2479              :             }
    2480        28505 :             if (candModeLanes < modeLanes) {
    2481              :                 return false;
    2482              :             }
    2483              :             // we would create a left turn
    2484        24362 :             if (candAngle < 0 && angle > 0) {
    2485              :                 return true;
    2486              :             }
    2487              :             if (angle < 0 && candAngle > 0) {
    2488              :                 return false;
    2489              :             }
    2490              :         }
    2491              :     }
    2492              :     return false;
    2493              : }
    2494              : 
    2495              : EdgeVector
    2496         8585 : NBNode::getPassengerEdges(bool incoming) const {
    2497              :     EdgeVector result;
    2498        26463 :     for (NBEdge* e : (incoming ? myIncomingEdges : myOutgoingEdges)) {
    2499        17878 :         if ((e->getPermissions() & SVC_PASSENGER) != 0) {
    2500        12614 :             result.push_back(e);
    2501              :         }
    2502              :     }
    2503         8585 :     return result;
    2504            0 : }
    2505              : 
    2506              : LinkDirection
    2507      9053912 : NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
    2508              :     // ok, no connection at all -> dead end
    2509      9053912 :     if (outgoing == nullptr) {
    2510              :         return LinkDirection::NODIR;
    2511              :     }
    2512              :     assert(incoming->getToNode() == this);
    2513              :     assert(outgoing->getFromNode() == this);
    2514      9053911 :     if (incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT && outgoing->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
    2515              :         return LinkDirection::STRAIGHT;
    2516              :     }
    2517              :     // turning direction
    2518      9035540 :     if (incoming->isTurningDirectionAt(outgoing)) {
    2519      1505281 :         if (isExplicitRailNoBidi(incoming, outgoing)) {
    2520              :             return LinkDirection::STRAIGHT;
    2521              :         }
    2522      3008775 :         return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
    2523              :     }
    2524              :     // get the angle between incoming/outgoing at the junction
    2525      7530259 :     const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
    2526              :     // ok, should be a straight connection
    2527      7530259 :     EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
    2528      7530259 :     SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
    2529      7530259 :     if (vehPerm != SVC_PEDESTRIAN) {
    2530      7422802 :         vehPerm &= ~SVC_PEDESTRIAN;
    2531              :     }
    2532      7530259 :     const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
    2533      7530259 :     if (fabs(angle) < 44.) {
    2534      2827302 :         if (fabs(angle) > 6.) {
    2535       680444 :             if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
    2536       109423 :                 return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
    2537              :             }
    2538       624942 :             if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
    2539        66877 :                 return angle > 0 ? LinkDirection::PARTRIGHT : LinkDirection::PARTLEFT;
    2540              :             }
    2541              :         }
    2542      2706640 :         if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
    2543         5866 :             return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
    2544              :         }
    2545      2703606 :         return LinkDirection::STRAIGHT;
    2546              :     }
    2547              : 
    2548      4702957 :     if (angle > 0) {
    2549              :         // check whether any other edge goes further to the right
    2550      2519784 :         if (angle > 90 + NUMERICAL_EPS) {
    2551              :             return LinkDirection::RIGHT;
    2552              :         }
    2553      1549233 :         NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
    2554      1549233 :         if (outCW != nullptr) {
    2555              :             return LinkDirection::PARTRIGHT;
    2556              :         } else {
    2557              :             return LinkDirection::RIGHT;
    2558              :         }
    2559              :     } else {
    2560              :         // check whether any other edge goes further to the left
    2561      2183173 :         if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
    2562         1160 :             if (isExplicitRailNoBidi(incoming, outgoing)) {
    2563              :                 return LinkDirection::STRAIGHT;
    2564              :             }
    2565         2304 :             return leftHand ? LinkDirection::TURN_LEFTHAND : LinkDirection::TURN;
    2566      2182013 :         } else if (angle < -(90 + NUMERICAL_EPS)) {
    2567              :             return LinkDirection::LEFT;
    2568              :         }
    2569      1312694 :         NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
    2570      1312694 :         if (outCCW != nullptr) {
    2571              :             return LinkDirection::PARTLEFT;
    2572              :         } else {
    2573              :             return LinkDirection::LEFT;
    2574              :         }
    2575              :     }
    2576              : }
    2577              : 
    2578              : 
    2579              : bool
    2580      1506441 : NBNode::isExplicitRailNoBidi(const NBEdge* incoming, const NBEdge* outgoing) {
    2581              :     // assume explicit connections at sharp turn-arounds are either for reversal or due to a geometry glitch
    2582              :     // (but should not have been guessed)
    2583              :     // @note this function is also called from NBAlgorithms when there aren't any connections ready
    2584              :     return (incoming->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_RECHECK
    2585      1436295 :             && isRailway(incoming->getPermissions())
    2586        12316 :             && isRailway(outgoing->getPermissions())
    2587      1518703 :             && incoming->getBidiEdge() != outgoing);
    2588              : }
    2589              : 
    2590              : 
    2591              : LinkState
    2592       236222 : NBNode::getLinkState(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane,
    2593              :                      bool mayDefinitelyPass, const std::string& tlID) const {
    2594       236222 :     if (myType == SumoXMLNodeType::RAIL_CROSSING && isRailway(incoming->getPermissions())) {
    2595              :         return LINKSTATE_MAJOR; // the trains must run on time
    2596              :     }
    2597       236075 :     if (tlID != "") {
    2598        26594 :         if (getRightOfWay() == RightOfWay::ALLWAYSTOP) {
    2599              :             return LINKSTATE_ALLWAY_STOP;
    2600              :         }
    2601        26411 :         return mustBrake(incoming, outgoing, fromLane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
    2602              :     }
    2603       209481 :     if (outgoing == nullptr) { // always off
    2604              :         return LINKSTATE_TL_OFF_NOSIGNAL;
    2605              :     }
    2606       209481 :     if ((myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
    2607       209481 :             && mustBrake(incoming, outgoing, fromLane, toLane, true)) {
    2608              :         return LINKSTATE_EQUAL; // all the same
    2609              :     }
    2610       190198 :     if (myType == SumoXMLNodeType::ALLWAY_STOP) {
    2611              :         return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
    2612              :     }
    2613       190162 :     if (myType == SumoXMLNodeType::ZIPPER && zipperConflict(incoming, outgoing, fromLane, toLane)) {
    2614              :         return LINKSTATE_ZIPPER;
    2615              :     }
    2616              :     if (!mayDefinitelyPass
    2617       190005 :             && mustBrake(incoming, outgoing, fromLane, toLane, true)
    2618              :             // legacy mode
    2619        82061 :             && (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
    2620              :             // avoid linkstate minor at pure railway nodes
    2621       272078 :             && (!NBNodeTypeComputer::isRailwayNode(this) || unsignalizedOperation())) {
    2622        80828 :         return myType == SumoXMLNodeType::PRIORITY_STOP && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::MINOR_ROAD ? LINKSTATE_STOP : LINKSTATE_MINOR; // minor road
    2623              :     }
    2624              :     // traffic lights are not regarded here
    2625              :     return LINKSTATE_MAJOR;
    2626              : }
    2627              : 
    2628              : 
    2629              : bool
    2630          172 : NBNode::zipperConflict(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane) const {
    2631          172 :     if (mustBrake(incoming, outgoing, fromLane, toLane, false)) {
    2632              :         // there should be another connection with the same target (not just some intersecting trajectories)
    2633          207 :         for (const NBEdge* in : getIncomingEdges()) {
    2634          289 :             for (const NBEdge::Connection& c : in->getConnections()) {
    2635          220 :                 if ((in != incoming || c.fromLane != fromLane) && c.toEdge == outgoing && c.toLane == toLane) {
    2636              :                     return true;
    2637              :                 }
    2638              :             }
    2639              :         }
    2640              :     }
    2641              :     return false;
    2642              : }
    2643              : 
    2644              : 
    2645              : bool
    2646         2035 : NBNode::unsignalizedOperation() const {
    2647              :     SVCPermissions railClasses = 0;
    2648         7186 :     for (NBEdge* e : myIncomingEdges) {
    2649         5151 :         railClasses |= (e->getPermissions() & SVC_RAIL_CLASSES);
    2650              :     }
    2651              :     assert(railClasses != 0);
    2652         2035 :     return ((railClasses & myPermitUnsignalizedClasses) == railClasses
    2653         2035 :             && (railClasses & myHaveRailSignalClasses) == 0);
    2654              : }
    2655              : 
    2656              : 
    2657              : void
    2658         2241 : NBNode::initRailSignalClasses(const NBNodeCont& nc) {
    2659         4482 :     myPermitUnsignalizedClasses = parseVehicleClasses(toString(OptionsCont::getOptions().getStringVector("railway.signal.permit-unsignalized")));
    2660         2241 :     myHaveRailSignalClasses = 0;
    2661        73133 :     for (auto it : nc) {
    2662              :         const NBNode* n = it.second;
    2663        70892 :         if (n->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2664         4607 :             for (const NBEdge* in : n->getIncomingEdges()) {
    2665         2843 :                 myHaveRailSignalClasses |= in->getPermissions();
    2666              :             }
    2667              :         }
    2668              :     }
    2669         2241 : }
    2670              : 
    2671              : 
    2672              : bool
    2673        17483 : NBNode::checkIsRemovable() const {
    2674              :     std::string reason;
    2675        34966 :     return checkIsRemovableReporting(reason);
    2676              : }
    2677              : 
    2678              : bool
    2679        17483 : NBNode::checkIsRemovableReporting(std::string& reason) const {
    2680        17483 :     if (getEdges().empty()) {
    2681              :         return true;
    2682              :     }
    2683              :     // check whether this node is included in a traffic light or crossing
    2684        17483 :     if (myTrafficLights.size() != 0) {
    2685              :         reason = "TLS";
    2686          514 :         return false;
    2687              :     }
    2688        16969 :     if (myType == SumoXMLNodeType::RAIL_SIGNAL) {
    2689              :         reason = "rail_signal";
    2690          220 :         return false;
    2691              :     }
    2692        16749 :     if (myCrossings.size() != 0) {
    2693              :         reason = "crossing";
    2694            0 :         return false;
    2695              :     }
    2696              :     EdgeVector::const_iterator i;
    2697              :     // one in, one out -> just a geometry ...
    2698        16749 :     if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
    2699              :         // ... if types match ...
    2700         4997 :         if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
    2701         2355 :             reason = "edges incompatible: " + reason;
    2702         2355 :             return false;
    2703              :         }
    2704         2642 :         if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
    2705              :             reason = "turnaround";
    2706           30 :             return false;
    2707              :         }
    2708              :         return true;
    2709              :     }
    2710              :     // two in, two out -> may be something else
    2711        11752 :     if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
    2712              :         // check whether the origin nodes of the incoming edges differ
    2713              :         std::set<NBNode*> origSet;
    2714         8934 :         for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    2715         5956 :             origSet.insert((*i)->getFromNode());
    2716              :         }
    2717         2978 :         if (origSet.size() < 2) {
    2718              :             // overlapping case
    2719          217 :             if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
    2720           75 :                     myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
    2721          137 :                 return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
    2722           67 :                          myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
    2723           71 :                         || (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
    2724            1 :                             myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
    2725              :             }
    2726              :         }
    2727              :         // check whether this node is an intermediate node of
    2728              :         //  a two-directional street
    2729         5776 :         for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    2730              :             // each of the edges must have an opposite direction edge
    2731         4372 :             NBEdge* opposite = (*i)->getTurnDestination(true);
    2732         4372 :             if (opposite != nullptr) {
    2733              :                 // the other outgoing edges must be the continuation of the current
    2734         3373 :                 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
    2735              :                 // check whether the types allow joining
    2736         3373 :                 if (!(*i)->expandableBy(continuation, reason)) {
    2737          505 :                     reason = "edges incompatible: " + reason;
    2738          505 :                     return false;
    2739              :                 }
    2740              :             } else {
    2741              :                 // ok, at least one outgoing edge is not an opposite
    2742              :                 //  of an incoming one
    2743              :                 reason = "not opposites";
    2744              :                 return false;
    2745              :             }
    2746              :         }
    2747              :         return true;
    2748              :     }
    2749              :     // ok, a real node
    2750              :     reason = "intersection";
    2751              :     return false;
    2752              : }
    2753              : 
    2754              : 
    2755              : std::vector<std::pair<NBEdge*, NBEdge*> >
    2756        11314 : NBNode::getEdgesToJoin() const {
    2757              :     assert(checkIsRemovable());
    2758              :     std::vector<std::pair<NBEdge*, NBEdge*> > ret;
    2759              :     // one in, one out-case
    2760        11314 :     if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
    2761         2578 :         ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
    2762         2578 :         return ret;
    2763              :     }
    2764         8736 :     if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
    2765              :         // two in, two out-case
    2766         1535 :         if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
    2767           68 :                 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
    2768              :             // overlapping edges
    2769              :             std::string reason;
    2770           68 :             if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
    2771           67 :                 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
    2772           67 :                 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
    2773              :             } else {
    2774            1 :                 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
    2775            1 :                 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
    2776              :             }
    2777              :             return ret;
    2778              :         }
    2779              :     }
    2780        11466 :     for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
    2781              :         // join with the edge that is not a turning direction
    2782         2798 :         NBEdge* opposite = (*i)->getTurnDestination(true);
    2783              :         assert(opposite != 0);
    2784         2798 :         NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
    2785         2798 :         ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
    2786              :     }
    2787              :     return ret;
    2788            0 : }
    2789              : 
    2790              : 
    2791              : const PositionVector&
    2792      2979282 : NBNode::getShape() const {
    2793      2979282 :     return myPoly;
    2794              : }
    2795              : 
    2796              : 
    2797              : void
    2798           56 : NBNode::setCustomShape(const PositionVector& shape) {
    2799              :     myPoly = shape;
    2800           56 :     myHaveCustomPoly = (myPoly.size() > 1);
    2801           56 :     if (myHaveCustomPoly) {
    2802           55 :         for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
    2803            0 :             (*i)->resetNodeBorder(this);
    2804              :         }
    2805              :     }
    2806           56 : }
    2807              : 
    2808              : 
    2809              : NBEdge*
    2810       175515 : NBNode::getConnectionTo(NBNode* n) const {
    2811       437471 :     for (NBEdge* e : myOutgoingEdges) {
    2812       298268 :         if (e->getToNode() == n && e->getPermissions() != 0) {
    2813              :             return e;
    2814              :         }
    2815              :     }
    2816              :     return nullptr;
    2817              : }
    2818              : 
    2819              : 
    2820              : bool
    2821            0 : NBNode::isNearDistrict() const {
    2822            0 :     if (isDistrict()) {
    2823              :         return false;
    2824              :     }
    2825            0 :     for (const NBEdge* const t : getEdges()) {
    2826            0 :         const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
    2827            0 :         for (const NBEdge* const k : other->getEdges()) {
    2828            0 :             if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
    2829              :                 return true;
    2830              :             }
    2831              :         }
    2832              :     }
    2833              :     return false;
    2834              : }
    2835              : 
    2836              : 
    2837              : bool
    2838            0 : NBNode::isDistrict() const {
    2839            0 :     return myType == SumoXMLNodeType::DISTRICT;
    2840              : }
    2841              : 
    2842              : 
    2843              : int
    2844         3402 : NBNode::guessCrossings() {
    2845              : #ifdef DEBUG_PED_STRUCTURES
    2846              :     gDebugFlag1 = DEBUGCOND;
    2847              : #endif
    2848              :     int numGuessed = 0;
    2849         3402 :     if (myCrossings.size() > 0 || myDiscardAllCrossings) {
    2850              :         // user supplied crossings, do not guess
    2851              :         return numGuessed;
    2852              :     }
    2853              :     DEBUGCOUT(gDebugFlag1, "guess crossings for " << getID() << "\n")
    2854         3399 :     EdgeVector allEdges = getEdgesSortedByAngleAtNodeCenter();
    2855              :     // check for pedestrial lanes going clockwise around the node
    2856              :     std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
    2857        15169 :     for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
    2858        11770 :         NBEdge* edge = *it;
    2859              :         const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
    2860        11770 :         if (edge->getFromNode() == this) {
    2861        14490 :             for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
    2862         8605 :                 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
    2863              :             }
    2864              :         } else {
    2865        14490 :             for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
    2866         8605 :                 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
    2867              :             }
    2868              :         }
    2869              :     }
    2870              :     // do we even have a pedestrian lane?
    2871              :     int firstSidewalk = -1;
    2872         5576 :     for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
    2873         5222 :         if (normalizedLanes[i].second) {
    2874              :             firstSidewalk = i;
    2875              :             break;
    2876              :         }
    2877              :     }
    2878              :     int hadCandidates = 0;
    2879              :     std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
    2880         3399 :     if (firstSidewalk != -1) {
    2881              :         // rotate lanes to ensure that the first one allows pedestrians
    2882              :         std::vector<std::pair<NBEdge*, bool> > tmp;
    2883              :         copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
    2884              :         copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
    2885         3045 :         normalizedLanes = tmp;
    2886              :         // find candidates
    2887              :         EdgeVector candidates;
    2888        19031 :         for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
    2889        15986 :             NBEdge* edge = normalizedLanes[i].first;
    2890        15986 :             const bool allowsPed = normalizedLanes[i].second;
    2891              :             DEBUGCOUT(gDebugFlag1, "  cands=" << toString(candidates) << "  edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n")
    2892        15986 :             if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
    2893         4160 :                 candidates.push_back(edge);
    2894        11826 :             } else if (allowsPed) {
    2895         9889 :                 if (candidates.size() > 0) {
    2896         1642 :                     if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
    2897         1554 :                         hadCandidates++;
    2898         1554 :                         const int n = checkCrossing(candidates);
    2899         1554 :                         numGuessed += n;
    2900         1554 :                         if (n > 0) {
    2901         1341 :                             connectedCandidates.push_back(n);
    2902              :                         }
    2903              :                     }
    2904              :                     candidates.clear();
    2905              :                 }
    2906              :             }
    2907              :         }
    2908         3045 :         if (hadCandidates > 0 && candidates.size() > 0) {
    2909              :             // avoid wrapping around to the same sidewalk
    2910          211 :             hadCandidates++;
    2911          211 :             const int n = checkCrossing(candidates);
    2912          211 :             numGuessed += n;
    2913          211 :             if (n > 0) {
    2914          165 :                 connectedCandidates.push_back(n);
    2915              :             }
    2916              :         }
    2917         3045 :     }
    2918              :     // Avoid duplicate crossing between the same pair of walkingareas
    2919              :     DEBUGCOUT(gDebugFlag1, "  hadCandidates=" << hadCandidates << "  connectedCandidates=" << toString(connectedCandidates) << "\n")
    2920         3045 :     if (hadCandidates == 2 && connectedCandidates.size() == 2) {
    2921              :         // One or both of them might be split: remove the one with less splits
    2922          395 :         if (connectedCandidates.back() <= connectedCandidates.front()) {
    2923          386 :             numGuessed -= connectedCandidates.back();
    2924          386 :             myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
    2925              :         } else {
    2926            9 :             numGuessed -= connectedCandidates.front();
    2927            9 :             myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
    2928              :         }
    2929              :     }
    2930         3399 :     std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, myAllEdges));
    2931              : #ifdef DEBUG_PED_STRUCTURES
    2932              :     if (gDebugFlag1) {
    2933              :         std::cout << "guessedCrossings:\n";
    2934              :         for (auto& crossing : myCrossings) {
    2935              :             std::cout << "  edges=" << toString(crossing->edges) << "\n";
    2936              :         }
    2937              :     }
    2938              : #endif
    2939         3399 :     if (numGuessed > 0 && isSimpleContinuation(true, true)) {
    2940              :         // avoid narrow node shape when there is a crossing
    2941           18 :         computeNodeShape(-1);
    2942           90 :         for (NBEdge* e : myAllEdges) {
    2943           72 :             e->computeEdgeShape();
    2944              :         }
    2945              :     }
    2946              :     return numGuessed;
    2947         3399 : }
    2948              : 
    2949              : 
    2950              : int
    2951         1971 : NBNode::checkCrossing(EdgeVector candidates, bool checkOnly) {
    2952              :     DEBUGCOUT(gDebugFlag1, "checkCrossing candidates=" << toString(candidates) << "\n")
    2953         1971 :     if (candidates.size() == 0) {
    2954              :         DEBUGCOUT(gDebugFlag1, "no crossing added (numCandidates=" << candidates.size() << ")\n")
    2955              :         return 0;
    2956              :     } else {
    2957              :         // check whether the edges may be part of a common crossing due to having similar angle
    2958              :         double prevAngle = -100000; // dummy
    2959         5100 :         for (int i = 0; i < (int)candidates.size(); ++i) {
    2960         3388 :             NBEdge* edge = candidates[i];
    2961         3388 :             double angle = edge->getCrossingAngle(this);
    2962              :             // edges should be sorted by angle but this only holds true approximately
    2963         3388 :             if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
    2964              :                 DEBUGCOUT(gDebugFlag1, "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n")
    2965              :                 return 0;
    2966              :             }
    2967         8379 :             if (!checkOnly && !isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
    2968              :                 DEBUGCOUT(gDebugFlag1, "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n")
    2969              :                 return 0;
    2970              :             }
    2971              :             prevAngle = angle;
    2972              :         }
    2973         1712 :         if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
    2974          547 :             if (!checkOnly) {
    2975          547 :                 addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
    2976         1094 :                             || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
    2977              :                 DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
    2978              :             }
    2979          547 :             return 1;
    2980              :         } else {
    2981              :             // check for intermediate walking areas
    2982              :             prevAngle = -100000; // dummy
    2983         3473 :             for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
    2984         2411 :                 double angle = (*it)->getCrossingAngle(this);
    2985         2411 :                 if (it != candidates.begin()) {
    2986         1246 :                     NBEdge* prev = *(it - 1);
    2987         1246 :                     NBEdge* curr = *it;
    2988              :                     Position prevPos, currPos;
    2989              :                     int laneI;
    2990              :                     // compute distance between candiate edges
    2991              :                     double intermediateWidth = 0;
    2992         1246 :                     if (prev->getToNode() == this) {
    2993         1185 :                         laneI = prev->getNumLanes() - 1;
    2994         1185 :                         prevPos = prev->getLanes()[laneI].shape[-1];
    2995              :                     } else {
    2996              :                         laneI = 0;
    2997           61 :                         prevPos = prev->getLanes()[laneI].shape[0];
    2998              :                     }
    2999         1246 :                     intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
    3000         1246 :                     if (curr->getFromNode() == this) {
    3001         1182 :                         laneI = curr->getNumLanes() - 1;
    3002         1182 :                         currPos = curr->getLanes()[laneI].shape[0];
    3003              :                     } else {
    3004              :                         laneI = 0;
    3005           64 :                         currPos = curr->getLanes()[laneI].shape[-1];
    3006              :                     }
    3007         1246 :                     intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
    3008         1246 :                     intermediateWidth += currPos.distanceTo2D(prevPos);
    3009              :                     DEBUGCOUT(gDebugFlag1, " prevAngle=" << prevAngle << " angle=" << angle << " intermediateWidth=" << intermediateWidth << "\n")
    3010         1246 :                     if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
    3011         1246 :                             || (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
    3012          206 :                         return checkCrossing(EdgeVector(candidates.begin(), it), checkOnly)
    3013          103 :                                + checkCrossing(EdgeVector(it, candidates.end()), checkOnly);
    3014              :                     }
    3015              :                 }
    3016              :                 prevAngle = angle;
    3017              :             }
    3018         1062 :             if (!checkOnly) {
    3019         1062 :                 addCrossing(candidates, NBEdge::UNSPECIFIED_WIDTH, isTLControlled()
    3020         2132 :                             || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
    3021              :                 DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
    3022              :             }
    3023         1062 :             return 1;
    3024              :         }
    3025              :     }
    3026              : }
    3027              : 
    3028              : 
    3029              : bool
    3030          317 : NBNode::checkCrossingDuplicated(EdgeVector edges) {
    3031              :     // sort edge vector
    3032          317 :     std::sort(edges.begin(), edges.end());
    3033              :     // iterate over crossing to find a crossing with the same edges
    3034          845 :     for (auto& crossing : myCrossings) {
    3035              :         // sort edges of crossing before compare
    3036          531 :         EdgeVector edgesOfCrossing = crossing->edges;
    3037          531 :         std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
    3038          531 :         if (edgesOfCrossing == edges) {
    3039              :             return true;
    3040              :         }
    3041          531 :     }
    3042              :     return false;
    3043              : }
    3044              : 
    3045              : 
    3046              : bool
    3047          809 : NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
    3048         2446 :     for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
    3049         2358 :         if (!normalizedLanes[i].second) {
    3050              :             return true;
    3051              :         }
    3052              :     }
    3053              :     return false;
    3054              : }
    3055              : 
    3056              : 
    3057              : void
    3058         6965 : NBNode::buildCrossingsAndWalkingAreas() {
    3059         6965 :     buildCrossings();
    3060        13930 :     buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
    3061         6965 :                       OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
    3062         6965 :     buildCrossingOutlines();
    3063              :     // ensure that all crossings are properly connected
    3064         6965 :     bool recheck = myCrossings.size() > 0;
    3065         8413 :     while (recheck) {
    3066              :         recheck = false;
    3067              :         std::set<std::string> waIDs;
    3068              :         int numSidewalks = 0;
    3069         5421 :         for (WalkingArea& wa : myWalkingAreas) {
    3070         3973 :             waIDs.insert(wa.id);
    3071         3973 :             numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
    3072              :         }
    3073         1448 :         if (numSidewalks < 2) {
    3074              :             // all crossings are invalid if there are fewer than 2 sidewalks involved
    3075              :             waIDs.clear();
    3076              :         }
    3077         4383 :         for (auto& crossing : myCrossings) {
    3078         2935 :             if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
    3079           40 :                 if (crossing->valid) {
    3080           30 :                     WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
    3081              :                                    crossing->id, getID(), toString(crossing->edges));
    3082              :                     recheck = true;
    3083              :                 }
    3084          151 :                 for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
    3085              :                     WalkingArea& wa = *waIt;
    3086          111 :                     std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
    3087          111 :                     if (it_nc != wa.nextCrossings.end()) {
    3088           11 :                         wa.nextCrossings.erase(it_nc);
    3089              :                     }
    3090          111 :                     if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
    3091            9 :                         waIt = myWalkingAreas.erase(waIt);
    3092              :                         recheck = true;
    3093              :                     } else {
    3094              :                         waIt++;
    3095              :                     }
    3096              :                 }
    3097           40 :                 crossing->valid = false;
    3098           40 :                 crossing->prevWalkingArea = "";
    3099           40 :                 crossing->nextWalkingArea = "";
    3100              :             }
    3101              :         }
    3102              :     }
    3103         6965 : }
    3104              : 
    3105              : 
    3106              : std::vector<NBNode::Crossing*>
    3107      1262640 : NBNode::getCrossings() const {
    3108              :     std::vector<Crossing*> result;
    3109      1601174 :     for (auto& c : myCrossings) {
    3110       338534 :         if (c->valid) {
    3111       335478 :             result.push_back(c.get());
    3112              :         }
    3113              :     }
    3114              :     //if (myCrossings.size() > 0) {
    3115              :     //    std::cout << "valid crossings at " << getID() << "\n";
    3116              :     //    for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
    3117              :     //        std::cout << "  " << toString((*it)->edges) << "\n";
    3118              :     //    }
    3119              :     //}
    3120      1262640 :     return result;
    3121            0 : }
    3122              : 
    3123              : 
    3124              : void
    3125        39536 : NBNode::discardAllCrossings(bool rejectAll) {
    3126              :     myCrossings.clear();
    3127              :     // also discard all further crossings
    3128        39536 :     if (rejectAll) {
    3129            1 :         myDiscardAllCrossings = true;
    3130              :     }
    3131        39536 : }
    3132              : 
    3133              : 
    3134              : void
    3135        63927 : NBNode::discardWalkingareas() {
    3136              :     myWalkingAreas.clear();
    3137        63927 : }
    3138              : 
    3139              : 
    3140              : double
    3141        31366 : NBNode::buildInnerEdges() {
    3142              :     // myDisplacementError is computed during this operation. reset first
    3143        31366 :     myDisplacementError = 0.;
    3144              :     // build inner edges for vehicle movements across the junction
    3145              :     int noInternalNoSplits = 0;
    3146        85365 :     for (const NBEdge* const edge : myIncomingEdges) {
    3147       156534 :         for (const NBEdge::Connection& con : edge->getConnections()) {
    3148       102535 :             if (con.toEdge == nullptr) {
    3149            0 :                 continue;
    3150              :             }
    3151       102535 :             noInternalNoSplits++;
    3152              :         }
    3153              :     }
    3154        31366 :     int lno = 0;
    3155        31366 :     int splitNo = 0;
    3156              :     double maxCrossingSeconds = 0.;
    3157        85365 :     for (NBEdge* const edge : myIncomingEdges) {
    3158        53999 :         maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
    3159              :     }
    3160        31366 :     return maxCrossingSeconds;
    3161              : }
    3162              : 
    3163              : 
    3164              : int
    3165         6965 : NBNode::buildCrossings() {
    3166              : #ifdef DEBUG_PED_STRUCTURES
    3167              :     gDebugFlag1 = DEBUGCOND;
    3168              : #endif
    3169              :     DEBUGCOUT(gDebugFlag1, "build crossings for " << getID() << ":\n")
    3170         6965 :     if (myDiscardAllCrossings) {
    3171              :         myCrossings.clear();
    3172              :     }
    3173              :     int index = 0;
    3174        13930 :     const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
    3175         9862 :     for (auto& c : myCrossings) {
    3176         2897 :         c->valid = true;
    3177         2897 :         c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
    3178         8691 :         c->id = ":" + getID() + "_c" + toString(index++);
    3179         2897 :         c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
    3180              :         // reset fields, so repeated computation (Netedit) will successfully perform the checks
    3181              :         // in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
    3182         2897 :         c->nextWalkingArea = "";
    3183         2897 :         c->prevWalkingArea = "";
    3184              :         EdgeVector& edges = c->edges;
    3185              :         DEBUGCOUT(gDebugFlag1, "  crossing=" << c->id << " edges=" << toString(edges))
    3186              :         // sorting the edges in the right way is imperative. We want to sort
    3187              :         // them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
    3188         2897 :         std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
    3189              :         DEBUGCOUT(gDebugFlag1, " sortedEdges=" << toString(edges) << "\n")
    3190              :         // rotate the edges so that the largest relative angle difference comes at the end
    3191              :         std::vector<double> rawAngleDiffs;
    3192              :         double maxAngleDiff = 0;
    3193              :         int maxAngleDiffIndex = 0; // index before maxDist
    3194         7908 :         for (int i = 0; i < (int) edges.size(); i++) {
    3195         5011 :             double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
    3196         5011 :                                               edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
    3197         5011 :             if (diff < 0) {
    3198         1926 :                 diff += 360;
    3199              :             }
    3200         5011 :             const double rawDiff = NBHelpers::relAngle(
    3201              :                                        edges[i]->getAngleAtNodeNormalized(this),
    3202         5011 :                                        edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
    3203         5011 :             rawAngleDiffs.push_back(fabs(rawDiff));
    3204              : 
    3205              :             DEBUGCOUT(gDebugFlag1, "   i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n")
    3206         5011 :             if (diff > maxAngleDiff) {
    3207              :                 maxAngleDiff = diff;
    3208              :                 maxAngleDiffIndex = i;
    3209              :             }
    3210              :         }
    3211         2897 :         if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
    3212              :             // if the angle differences is too small, we better not rotate
    3213         1818 :             std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
    3214              :             DEBUGCOUT(gDebugFlag1, " rotatedEdges=" << toString(edges))
    3215              :         }
    3216              :         bool diagonalCrossing = false;
    3217         2897 :         std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
    3218         2897 :         if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
    3219              :             diagonalCrossing = true;
    3220              : #ifdef DEBUG_PED_STRUCTURES
    3221              :             if (gDebugFlag1) {
    3222              :                 std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
    3223              :                 for (auto e : edges) {
    3224              :                     std::cout << "  e=" << e->getID()
    3225              :                               << " aC=" << e->getAngleAtNodeToCenter(this)
    3226              :                               << " a=" << e->getAngleAtNode(this)
    3227              :                               << " aN=" << e->getAngleAtNodeNormalized(this)
    3228              :                               << "\n";
    3229              :                 }
    3230              :             }
    3231              : #endif
    3232              :         }
    3233              :         // reverse to get them in CCW order (walking direction around the node)
    3234              :         std::reverse(edges.begin(), edges.end());
    3235              :         // compute shape
    3236              :         c->shape.clear();
    3237         2897 :         const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
    3238         2897 :         const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
    3239         2897 :         int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
    3240         2897 :         int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
    3241              :         DEBUGCOUT(gDebugFlag1, " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n")
    3242         2897 :         if (firstNonPedLane < 0 || lastNonPedLane < 0) {
    3243              :             // invalid crossing
    3244           12 :             WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
    3245            4 :             c->valid = false;
    3246              :             // compute surrogate shape to make it visible in netedit
    3247            4 :             firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
    3248            4 :             lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
    3249              :         }
    3250         2897 :         if (c->customShape.size() != 0) {
    3251              :             c->shape = c->customShape;
    3252              :         } else {
    3253         2878 :             NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
    3254         2878 :             NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
    3255         2878 :             crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
    3256         2878 :             crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
    3257         2878 :             crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
    3258         2878 :             crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
    3259         2878 :             double offset = c->width / 2;
    3260         2878 :             patchOffset_pathAcrossStreet(offset);
    3261         2878 :             crossingBeg.shape.extrapolate(offset);
    3262         2878 :             crossingEnd.shape.extrapolate(offset);
    3263              :             // check if after all changes shape are NAN (in these case, discard)
    3264         2878 :             if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
    3265            0 :                 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
    3266            0 :                 c->valid = false;
    3267              :             } else {
    3268         3346 :                 c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
    3269         3288 :                 c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
    3270              :             }
    3271         2878 :             if (diagonalCrossing) {
    3272            7 :                 c->shape.move2side(-c->width);
    3273              :             }
    3274         2878 :         }
    3275         2897 :     }
    3276         6965 :     return index;
    3277              : }
    3278              : 
    3279              : 
    3280              : void
    3281         2878 : NBNode::patchOffset_pathAcrossStreet(double& offset) {
    3282         2878 :     if (myCrossings.size() == 1 && myAllEdges.size() >= 3) {
    3283              :         EdgeVector nonPedIncoming;
    3284              :         EdgeVector nonPedOutgoing;
    3285              :         EdgeVector pedIncoming;
    3286              :         EdgeVector pedOutgoing;
    3287         3204 :         for (NBEdge* e : getIncomingEdges()) {
    3288         2319 :             if (e->getPermissions() != SVC_PEDESTRIAN) {
    3289         1910 :                 nonPedIncoming.push_back(e);
    3290              :             } else {
    3291          409 :                 pedIncoming.push_back(e);
    3292              :             }
    3293              :         }
    3294         3184 :         for (NBEdge* e : getOutgoingEdges()) {
    3295         2299 :             if (e->getPermissions() != SVC_PEDESTRIAN) {
    3296         1902 :                 nonPedOutgoing.push_back(e);
    3297              :             } else {
    3298          397 :                 pedOutgoing.push_back(e);
    3299              :             }
    3300              :         }
    3301          885 :         if (geometryLike(nonPedIncoming, nonPedOutgoing) && (pedIncoming.size() > 0 || pedOutgoing.size() > 0)) {
    3302              :             double maxAngle = 0;
    3303          412 :             const NBEdge* in = nonPedIncoming.front();
    3304          412 :             const NBEdge* out = nonPedOutgoing.front();
    3305          412 :             if (nonPedIncoming.size() == 1) {
    3306          165 :                 maxAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(this), out->getAngleAtNode(this)));
    3307              :             } else {
    3308          741 :                 for (const NBEdge* const in2 : nonPedIncoming) {
    3309              :                     double minAngle = 180;
    3310         1482 :                     for (const NBEdge* const out2 : nonPedOutgoing) {
    3311          988 :                         double angle = fabs(NBHelpers::relAngle(in2->getAngleAtNode(this), out2->getAngleAtNode(this)));
    3312          988 :                         if (angle < minAngle) {
    3313              :                             minAngle = angle;
    3314              :                             in = in2;
    3315              :                             out = out2;
    3316              :                         }
    3317              :                     }
    3318              :                     maxAngle = MAX2(maxAngle, minAngle);
    3319              :                 }
    3320              :             }
    3321              :             // changing the offset only handles the simple case where the road stays straight
    3322          412 :             if (maxAngle < 15) {
    3323          401 :                 const int inLane = in->getFirstNonPedestrianLaneIndex(FORWARD);
    3324          401 :                 const int outLane = out->getFirstNonPedestrianLaneIndex(FORWARD);
    3325          401 :                 if (inLane >= 0 && outLane >= 0) {
    3326          401 :                     const Position& p0 = in->getLaneShape(inLane).back();
    3327          401 :                     const Position& p1 = out->getLaneShape(outLane).front();
    3328          401 :                     PositionVector road;
    3329          401 :                     road.push_back(p0);
    3330          401 :                     road.push_back(p1);
    3331              :                     Position mid = (p0 + p1) / 2;
    3332              :                     double maxPathDist = 0;
    3333          731 :                     for (NBEdge* e : pedIncoming) {
    3334          330 :                         const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).back()));
    3335              :                         maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
    3336              :                     }
    3337          691 :                     for (NBEdge* e : pedOutgoing) {
    3338          290 :                         const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).front()));
    3339              :                         maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
    3340              :                     }
    3341              :                     // if the junction is stretched, the crossing should stay close to the paths
    3342          800 :                     if (maxPathDist < MAX2(myCrossings.front()->width, 4.0)) {
    3343          380 :                         offset = p0.distanceTo2D(p1) / 2;
    3344              :                     }
    3345          401 :                 }
    3346              :             }
    3347              :         }
    3348          885 :     }
    3349         2878 : }
    3350              : 
    3351              : 
    3352              : void
    3353         6965 : NBNode::buildWalkingAreas(int cornerDetail, double joinMinDist) {
    3354              : #ifdef DEBUG_PED_STRUCTURES
    3355              :     gDebugFlag1 = DEBUGCOND;
    3356              : #endif
    3357              :     int index = 0;
    3358              :     myWalkingAreas.clear();
    3359              :     DEBUGCOUT(gDebugFlag1, "build walkingAreas for " << getID() << ":\n")
    3360         6965 :     if (myAllEdges.size() == 0) {
    3361            0 :         return;
    3362              :     }
    3363         6965 :     EdgeVector allEdges = getEdgesSortedByAngleAtNodeCenter();
    3364              :     // shapes are all pointing away from the intersection
    3365              :     std::vector<std::pair<NBEdge*, NBEdge::Lane> > normalizedLanes;
    3366        30641 :     for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
    3367        23676 :         NBEdge* edge = *it;
    3368              :         const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
    3369              :         std::vector<NBEdge::Lane> tmp;
    3370              :         bool hadSidewalk = false;
    3371              :         bool hadNonSidewalk = false;
    3372        60190 :         for (int i = 0; i < (int)lanes.size(); i++) {
    3373        36514 :             NBEdge::Lane l = lanes[i];
    3374        36514 :             const bool sidewalk = (l.permissions & SVC_PEDESTRIAN) != 0;
    3375        36514 :             if (sidewalk) {
    3376        19912 :                 if (hadSidewalk && hadNonSidewalk) {
    3377            4 :                     if (edge->getFromNode() == this) {
    3378            6 :                         WRITE_WARNINGF(TL("Ignoring additional sidewalk lane % on edge '%' for walkingareas."),
    3379              :                                        i, edge->getID());
    3380              :                     }
    3381              :                     continue;
    3382              :                 }
    3383              :                 hadSidewalk = true;
    3384              :             } else {
    3385              :                 hadNonSidewalk = true;
    3386              :             }
    3387        36510 :             tmp.push_back(l);
    3388        36514 :         }
    3389        23676 :         if (edge->getFromNode() == this) {
    3390              :             std::reverse(tmp.begin(), tmp.end());
    3391              :         } else {
    3392        30093 :             for (NBEdge::Lane& l : tmp) {
    3393        36510 :                 l.shape = l.shape.reverse();
    3394              :             }
    3395              :         }
    3396        60186 :         for (NBEdge::Lane& l : tmp) {
    3397        73020 :             l.shape = l.shape.getSubpartByIndex(0, 2);
    3398        55996 :             l.width = (l.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : l.width);
    3399        36510 :             normalizedLanes.push_back(std::make_pair(edge, l));
    3400              :         }
    3401        23676 :     }
    3402              :     //if (gDebugFlag1) std::cout << "  normalizedLanes=" << normalizedLanes.size() << "\n";
    3403              :     // collect [start,count[ indices in normalizedLanes that belong to a walkingArea
    3404              :     std::vector<std::pair<int, int> > waIndices;
    3405              :     int start = -1;
    3406         6965 :     NBEdge* prevEdge = normalizedLanes.back().first;
    3407        43475 :     for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
    3408        36510 :         NBEdge* edge = normalizedLanes[i].first;
    3409              :         NBEdge::Lane& l = normalizedLanes[i].second;
    3410        36510 :         if (start == -1) {
    3411        21858 :             if ((l.permissions & SVC_PEDESTRIAN) != 0) {
    3412              :                 start = i;
    3413              :             }
    3414              :         } else {
    3415        14652 :             if ((l.permissions & SVC_PEDESTRIAN) == 0
    3416        10039 :                     || crossingBetween(edge, prevEdge)
    3417        10033 :                     || alreadyConnectedPaths(edge, prevEdge, joinMinDist)
    3418        24568 :                     || crossesFringe(edge, prevEdge)
    3419              :                ) {
    3420         4748 :                 waIndices.push_back(std::make_pair(start, i - start));
    3421         4748 :                 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
    3422              :                     start = i;
    3423              :                 } else {
    3424              :                     start = -1;
    3425              :                 }
    3426              : 
    3427              :             }
    3428              :         }
    3429              :         DEBUGCOUT(gDebugFlag1, "     i=" << i << " edge=" << edge->getID() << " start=" << start << " ped=" << ((l.permissions & SVC_PEDESTRIAN) != 0)
    3430              :                   << " waI=" << waIndices.size() << " crossingBetween=" << crossingBetween(edge, prevEdge) << "\n")
    3431              :         prevEdge = edge;
    3432              :     }
    3433              :     // deal with wrap-around issues
    3434         6965 :     if (start != - 1) {
    3435         5256 :         const int waNumLanes = (int)normalizedLanes.size() - start;
    3436         5256 :         if (waIndices.size() == 0) {
    3437         3744 :             waIndices.push_back(std::make_pair(start, waNumLanes));
    3438              :             DEBUGCOUT(gDebugFlag1, "  single wa, end at wrap-around\n")
    3439              :         } else {
    3440         1512 :             if (waIndices.front().first == 0) {
    3441         1341 :                 NBEdge* edge = normalizedLanes.front().first;
    3442         1341 :                 if (crossingBetween(edge, normalizedLanes.back().first)
    3443         1341 :                         || crossesFringe(edge, normalizedLanes.back().first)) {
    3444              :                     // do not wrap-around (see above)
    3445            7 :                     waIndices.push_back(std::make_pair(start, waNumLanes));
    3446              :                     DEBUGCOUT(gDebugFlag1, "  do not wrap around\n")
    3447              :                 } else {
    3448              :                     // first walkingArea wraps around
    3449         1334 :                     waIndices.front().first = start;
    3450         1334 :                     waIndices.front().second = waNumLanes + waIndices.front().second;
    3451              :                     DEBUGCOUT(gDebugFlag1, "  wrapping around\n")
    3452              :                 }
    3453              :             } else {
    3454              :                 // last walkingArea ends at the wrap-around
    3455          171 :                 waIndices.push_back(std::make_pair(start, waNumLanes));
    3456              :                 DEBUGCOUT(gDebugFlag1, "  end at wrap-around\n")
    3457              :             }
    3458              :         }
    3459              :     }
    3460              : #ifdef DEBUG_PED_STRUCTURES
    3461              :     if (gDebugFlag1) {
    3462              :         std::cout << "  normalizedLanes=" << normalizedLanes.size() << " waIndices:\n";
    3463              :         for (int i = 0; i < (int)waIndices.size(); ++i) {
    3464              :             std::cout << "   " << waIndices[i].first << ", " << waIndices[i].second << "\n";
    3465              :         }
    3466              :     }
    3467              : #endif
    3468              :     // build walking areas connected to a sidewalk
    3469        15635 :     for (int i = 0; i < (int)waIndices.size(); ++i) {
    3470         8670 :         const bool buildExtensions = waIndices[i].second != (int)normalizedLanes.size();
    3471         8670 :         int startIdx = waIndices[i].first;
    3472         8670 :         const int prev = startIdx > 0 ? startIdx - 1 : (int)normalizedLanes.size() - 1;
    3473              :         const int count = waIndices[i].second;
    3474         8670 :         const int end = (startIdx + count) % normalizedLanes.size();
    3475         8670 :         int lastIdx = (startIdx + count - 1) % normalizedLanes.size();
    3476              : 
    3477        26010 :         WalkingArea wa(":" + getID() + "_w" + toString(index++), 1);
    3478              :         DEBUGCOUT(gDebugFlag1, "build walkingArea " << wa.id << " start=" << startIdx << " end=" << end << " count=" << count << " prev=" << prev << ":\n")
    3479              :         double endCrossingWidth = 0;
    3480              :         double startCrossingWidth = 0;
    3481         8670 :         PositionVector endCrossingShape;
    3482         8670 :         PositionVector startCrossingShape;
    3483              :         // check for connected crossings
    3484              :         bool connectsCrossing = false;
    3485              :         bool crossingNearSidewalk = false;
    3486              :         int numCrossings = 0;
    3487              :         std::vector<Position> connectedPoints;
    3488        18197 :         for (auto c : getCrossings()) {
    3489              :             DEBUGCOUT(gDebugFlag1, "  crossing=" << c->id << " sortedEdges=" << toString(c->edges) << "\n")
    3490         9527 :             if (c->edges.back() == normalizedLanes[end].first
    3491         9527 :                     && (normalizedLanes[end].second.permissions & SVC_PEDESTRIAN) == 0) {
    3492              :                 // crossing ends
    3493         2677 :                 if (c->nextWalkingArea != "") {
    3494            3 :                     WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' targets '%' and '%'."),
    3495              :                                    getID(), c->id, c->nextWalkingArea, wa.id);
    3496            1 :                     c->valid = false;
    3497              :                 }
    3498              :                 c->nextWalkingArea = wa.id;
    3499         2677 :                 wa.prevCrossings.push_back(c->id);
    3500         2677 :                 if ((int)c->edges.size() < wa.minPrevCrossingEdges) {
    3501              :                     // if there are multiple crossings, use the shape of the one that crosses fewer edges
    3502         2677 :                     endCrossingWidth = c->width;
    3503              :                     endCrossingShape = c->shape;
    3504         2677 :                     wa.width = MAX2(wa.width, endCrossingWidth);
    3505              :                     connectsCrossing = true;
    3506         2677 :                     connectedPoints.push_back(c->shape[-1]);
    3507         2677 :                     wa.minPrevCrossingEdges = (int)c->edges.size();
    3508         2677 :                     numCrossings++;
    3509         2677 :                     if (normalizedLanes[lastIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < endCrossingWidth) {
    3510              :                         crossingNearSidewalk = true;
    3511              :                         DEBUGCOUT(gDebugFlag1, "    nearSidewalk\n")
    3512              :                     }
    3513              :                 }
    3514              :                 DEBUGCOUT(gDebugFlag1, "    crossing " << c->id << " ends\n")
    3515              :             }
    3516         9527 :             if (c->edges.front() == normalizedLanes[prev].first
    3517         9527 :                     && (normalizedLanes[prev].second.permissions & SVC_PEDESTRIAN) == 0) {
    3518              :                 // crossing starts
    3519         2677 :                 if (c->prevWalkingArea != "") {
    3520            0 :                     WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' is targeted by '%' and '%'."),
    3521              :                                    getID(), c->id, c->prevWalkingArea, wa.id);
    3522            0 :                     c->valid = false;
    3523              :                 }
    3524         2677 :                 if (c->valid && std::find(wa.prevCrossings.begin(), wa.prevCrossings.end(), c->id) != wa.prevCrossings.end()) {
    3525            9 :                     WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' starts and ends at walkingarea '%'."),
    3526              :                                    getID(), c->id, wa.id);
    3527            3 :                     c->valid = false;
    3528              :                 }
    3529              :                 c->prevWalkingArea = wa.id;
    3530         2677 :                 wa.nextCrossings.push_back(c->id);
    3531         2677 :                 if ((int)c->edges.size() < wa.minNextCrossingEdges) {
    3532              :                     // if there are multiple crossings, use the shape of the one that crosses fewer edges
    3533         2677 :                     startCrossingWidth = c->width;
    3534              :                     startCrossingShape = c->shape;
    3535         2677 :                     wa.width = MAX2(wa.width, startCrossingWidth);
    3536              :                     connectsCrossing = true;
    3537         2677 :                     connectedPoints.push_back(c->shape[0]);
    3538         2677 :                     wa.minNextCrossingEdges = (int)c->edges.size();
    3539         2677 :                     numCrossings++;
    3540         2677 :                     if (normalizedLanes[startIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < startCrossingWidth) {
    3541              :                         crossingNearSidewalk = true;
    3542              :                         DEBUGCOUT(gDebugFlag1, "    nearSidewalk\n")
    3543              :                     }
    3544              :                 }
    3545              :                 DEBUGCOUT(gDebugFlag1, "    crossing " << c->id << " starts\n")
    3546              :             }
    3547              :             DEBUGCOUT(gDebugFlag1, "  check connections to crossing " << c->id
    3548              :                       << " cFront=" << c->edges.front()->getID() << " cBack=" << c->edges.back()->getID()
    3549              :                       << " wEnd=" << normalizedLanes[end].first->getID() << " wStart=" << normalizedLanes[startIdx].first->getID()
    3550              :                       << " wStartPrev=" << normalizedLanes[prev].first->getID()
    3551              :                       << "\n")
    3552         8670 :         }
    3553         8670 :         if (count < 2 && !connectsCrossing) {
    3554              :             // not relevant for walking
    3555              :             DEBUGCOUT(gDebugFlag1, "    not relevant for walking: count=" << count << " connectsCrossing=" << connectsCrossing << "\n")
    3556         1177 :             continue;
    3557              :         }
    3558              :         // build shape and connections
    3559              :         std::set<const NBEdge*, ComparatorIdLess>& connected = wa.refEdges;
    3560        26224 :         for (int j = 0; j < count; ++j) {
    3561        18731 :             const int nlI = (startIdx + j) % normalizedLanes.size();
    3562        18731 :             NBEdge* edge = normalizedLanes[nlI].first;
    3563        18731 :             NBEdge::Lane l = normalizedLanes[nlI].second;
    3564        29359 :             wa.width = MAX2(wa.width, l.width);
    3565              :             if (connected.count(edge) == 0) {
    3566        18679 :                 if (edge->getFromNode() == this) {
    3567         9420 :                     wa.nextSidewalks.push_back(edge->getSidewalkID());
    3568         9420 :                     connectedPoints.push_back(edge->getLaneShape(0)[0]);
    3569              :                 } else {
    3570         9259 :                     wa.prevSidewalks.push_back(edge->getSidewalkID());
    3571         9259 :                     connectedPoints.push_back(edge->getLaneShape(0)[-1]);
    3572              :                 }
    3573              :                 DEBUGCOUT(gDebugFlag1, "    connectedEdge=" << edge->getID() << " connectedPoint=" << connectedPoints.back() << "\n")
    3574              :                 connected.insert(edge);
    3575              :             }
    3576        18731 :             l.shape.move2side(-l.width / 2);
    3577        18731 :             wa.shape.push_back_noDoublePos(l.shape[0]);
    3578        18731 :             l.shape.move2side(l.width);
    3579        18731 :             wa.shape.push_back(l.shape[0]);
    3580        18731 :         }
    3581         7493 :         if (buildExtensions) {
    3582              :             // extension at starting crossing
    3583         4659 :             if (startCrossingShape.size() > 0) {
    3584         2667 :                 startCrossingShape.move2side(startCrossingWidth / 2);
    3585         2667 :                 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // right corner
    3586         2667 :                 startCrossingShape.move2side(-startCrossingWidth);
    3587         2667 :                 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // left corner goes first
    3588              :                 DEBUGCOUT(gDebugFlag1, "  extension at startCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
    3589              :             }
    3590              :             // extension at ending crossing
    3591         4659 :             if (endCrossingShape.size() > 0) {
    3592         2667 :                 endCrossingShape.move2side(endCrossingWidth / 2);
    3593         2667 :                 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
    3594         2667 :                 endCrossingShape.move2side(-endCrossingWidth);
    3595         2667 :                 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
    3596              :                 DEBUGCOUT(gDebugFlag1, "  extension at endCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
    3597              :             }
    3598              :         }
    3599         4075 :         if (connected.size() == 2 && !connectsCrossing && wa.nextSidewalks.size() == 1 && wa.prevSidewalks.size() == 1
    3600         9062 :                 && normalizedLanes.size() == 2) {
    3601              :             // do not build a walkingArea since a normal connection exists
    3602          783 :             const NBEdge* e1 = *connected.begin();
    3603          783 :             const NBEdge* e2 = *(++connected.begin());
    3604          783 :             if (e1->hasConnectionTo(e2, 0, 0) || e2->hasConnectionTo(e1, 0, 0)) {
    3605              :                 DEBUGCOUT(gDebugFlag1, "    not building a walkingarea since normal connections exist\n")
    3606          224 :                 continue;
    3607              :             }
    3608              :         }
    3609         7269 :         if (count == (int)normalizedLanes.size()) {
    3610              :             // junction is covered by the whole walkingarea
    3611              :             wa.shape = myPoly;
    3612              :             // increase walking width if the walkingare is wider than a single lane
    3613         6681 :             for (const NBEdge* in : myIncomingEdges) {
    3614        11117 :                 for (const NBEdge* out : myOutgoingEdges) {
    3615         8197 :                     if (in->getFromNode() == out->getToNode() && in->getInnerGeometry().reverse() == out->getInnerGeometry()
    3616         1151 :                             && (in->getPermissions() & SVC_PEDESTRIAN)
    3617         8197 :                             && (out->getPermissions() & SVC_PEDESTRIAN)) {
    3618              :                         // doesn't catch all cases but probably most
    3619         2271 :                         wa.width = MAX2(wa.width, in->getTotalWidth() + out->getTotalWidth());
    3620              :                     }
    3621              :                 }
    3622              :             }
    3623         4659 :         } else if (cornerDetail > 0) {
    3624              :             // build smooth inner curve (optional)
    3625              :             int smoothEnd = end;
    3626              :             int smoothPrev = prev;
    3627              :             // extend to green verge
    3628         4483 :             if (endCrossingWidth > 0 && normalizedLanes[smoothEnd].second.permissions == 0) {
    3629          154 :                 smoothEnd = (smoothEnd + 1) % normalizedLanes.size();
    3630              :             }
    3631         4483 :             if (startCrossingWidth > 0 && normalizedLanes[smoothPrev].second.permissions == 0) {
    3632          150 :                 if (smoothPrev == 0) {
    3633            0 :                     smoothPrev = (int)normalizedLanes.size() - 1;
    3634              :                 } else {
    3635          150 :                     smoothPrev--;
    3636              :                 }
    3637              :             }
    3638         4483 :             PositionVector begShape = normalizedLanes[smoothEnd].second.shape;
    3639         8966 :             begShape = begShape.reverse();
    3640              :             double shiftBegExtra = 0;
    3641              :             double shiftEndExtra = 0;
    3642         4483 :             if (lastIdx == startIdx) {
    3643          777 :                 lastIdx = (startIdx + 1) % normalizedLanes.size();
    3644              :                 DEBUGCOUT(gDebugFlag1, "    new lastIdx=" << lastIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
    3645          777 :                 if (normalizedLanes[startIdx].first == normalizedLanes[lastIdx].first) {
    3646              :                     lastIdx = startIdx;
    3647          195 :                     startIdx--;
    3648          195 :                     if (startIdx < 0) {
    3649           49 :                         startIdx = (int)normalizedLanes.size() - 1;
    3650              :                     }
    3651              :                     DEBUGCOUT(gDebugFlag1, "    new startIdx=" << startIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
    3652          195 :                     shiftEndExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
    3653              :                 } else {
    3654          582 :                     shiftBegExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
    3655              :                 }
    3656              :             }
    3657         4483 :             PositionVector begShapeOuter = normalizedLanes[lastIdx].second.shape;
    3658         8966 :             begShapeOuter = begShapeOuter.reverse();
    3659              :             //begShape.extrapolate(endCrossingWidth);
    3660         4483 :             begShape.move2side(normalizedLanes[smoothEnd].second.width / 2);
    3661         4483 :             begShapeOuter.move2side(normalizedLanes[lastIdx].second.width / 2 + shiftBegExtra);
    3662         4483 :             PositionVector endShape = normalizedLanes[smoothPrev].second.shape;
    3663         4483 :             PositionVector endShapeOuter = normalizedLanes[startIdx].second.shape;;
    3664         4483 :             endShape.move2side(normalizedLanes[smoothPrev].second.width / 2);
    3665         4483 :             endShapeOuter.move2side(normalizedLanes[startIdx].second.width / 2 + shiftEndExtra);
    3666              :             //endShape.extrapolate(startCrossingWidth);
    3667         4483 :             PositionVector curve;
    3668         4483 :             if (count != (int)normalizedLanes.size() || count == 2) {
    3669         4483 :                 const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
    3670         4483 :                 if (count == 1 && angle > 0 && crossingNearSidewalk && numCrossings < 2) {
    3671              :                     // do not build smooth shape for an unconnected left turn
    3672              :                     // (the walkingArea would get bigger without a reason to
    3673              :                     // walk there)
    3674         4321 :                 } else if ((normalizedLanes[smoothEnd].first->getPermissions() & normalizedLanes[smoothPrev].first->getPermissions() &
    3675              :                             ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
    3676              :                     DEBUGCOUT(gDebugFlag1, "   traffic curve\n")
    3677        11721 :                     curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, gDebugFlag1 ? this : nullptr);
    3678         3907 :                     if (curve.length2D() - begShape.back().distanceTo2D(endShape.front()) > 5) {
    3679              :                         DEBUGCOUT(gDebugFlag1, "   reduceBulge directLength=" << begShape.back().distanceTo2D(endShape.front())
    3680              :                                   << " curveLength=" << curve.length2D()
    3681              :                                   << " delta=" << curve.length2D() - begShape.back().distanceTo2D(endShape.front())
    3682              :                                   << "\n")
    3683           76 :                         curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
    3684              :                     }
    3685              :                 } else {
    3686              :                     DEBUGCOUT(gDebugFlag1, "   nonTraffic curve\n")
    3687          414 :                     const double extend = MIN2(10.0, begShape.back().distanceTo2D(endShape.front()) / 2);
    3688          828 :                     curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
    3689              :                 }
    3690         4483 :                 if (curve.size() > 2) {
    3691              :                     curve.erase(curve.begin());
    3692              :                     curve.pop_back();
    3693         2071 :                     if (endCrossingWidth > 0) {
    3694              :                         wa.shape.pop_back();
    3695              :                     }
    3696         2071 :                     if (startCrossingWidth > 0) {
    3697              :                         wa.shape.erase(wa.shape.begin());
    3698              :                     }
    3699         2071 :                     if (count == (int)normalizedLanes.size()) {
    3700            0 :                         curve = curve.reverse();
    3701              :                     }
    3702         2071 :                     wa.shape.append(curve, 0);
    3703              :                 }
    3704              :                 DEBUGCOUT(gDebugFlag1, " end=" << smoothEnd << " prev=" << smoothPrev
    3705              :                           << " endCrossingWidth=" << endCrossingWidth << " startCrossingWidth=" << startCrossingWidth
    3706              :                           << "  begShape=" << begShape << " endShape=" << endShape << " smooth curve=" << curve
    3707              :                           << "  begShapeOuter=" << begShapeOuter << " endShapeOuter=" << endShapeOuter
    3708              :                           << "  waShape=" << wa.shape
    3709              :                           << "\n")
    3710              :             }
    3711         4483 :             if (curve.size() > 2 && (count == 2 || (count == 1 && numCrossings > 0))) {
    3712         1855 :                 const double innerDist = begShape.back().distanceTo2D(endShape[0]);
    3713         1855 :                 const double outerDist = begShapeOuter.back().distanceTo2D(endShapeOuter[0]);
    3714              :                 DEBUGCOUT(gDebugFlag1, " innerDist=" << innerDist << " outerDist=" << outerDist << "\n")
    3715         1855 :                 if (outerDist > innerDist) {
    3716              :                     // we also need a rounded outer curve (unless we have only a single walkingarea)
    3717          177 :                     const double extend = MIN2(10.0, begShapeOuter.back().distanceTo2D(endShapeOuter.front()) / 2);
    3718          354 :                     curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, extend, extend, nullptr);
    3719          177 :                     if (curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front()) > 5) {
    3720              :                         DEBUGCOUT(gDebugFlag1, "   reduceBulge directLength=" << begShapeOuter.back().distanceTo2D(endShapeOuter.front())
    3721              :                                   << " curveLength=" << curve.length2D()
    3722              :                                   << " delta=" << curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front())
    3723              :                                   << "\n")
    3724           44 :                         curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
    3725              :                     }
    3726          354 :                     curve = curve.reverse();
    3727              :                     // keep the points in case of extraShift
    3728          177 :                     if (shiftBegExtra != 0) {
    3729           25 :                         curve.push_front_noDoublePos(wa.shape[1]);
    3730           25 :                         curve.push_back_noDoublePos(wa.shape[2]);
    3731          152 :                     } else if (shiftEndExtra != 0) {
    3732            3 :                         curve.push_back_noDoublePos(wa.shape[1]);
    3733            3 :                         curve.push_back_noDoublePos(wa.shape[2]);
    3734              :                     }
    3735              :                     DEBUGCOUT(gDebugFlag1, " outerCurveRaw=" << curve << " wa1=" << wa.shape[1] << " wa2=" << wa.shape[2] << "\n")
    3736              :                     wa.shape.erase(wa.shape.begin() + 1, wa.shape.begin() + 3);
    3737          177 :                     wa.shape.insert(wa.shape.begin() + 1, curve.begin(), curve.end());
    3738              :                     DEBUGCOUT(gDebugFlag1, " outerCurve=" << curve << "\n")
    3739              :                 }
    3740              :             }
    3741         4483 :         }
    3742              :         // apply custom shapes
    3743         7269 :         if (myWalkingAreaCustomShapes.size() > 0) {
    3744           72 :             for (auto wacs : myWalkingAreaCustomShapes) {
    3745              :                 // every edge in wasc.edges must be part of connected
    3746           44 :                 if ((wacs.shape.size() != 0 || wacs.width != NBEdge::UNSPECIFIED_WIDTH) && includes(connected, wacs.edges)) {
    3747            5 :                     if (wacs.shape.size() != 0) {
    3748              :                         wa.shape = wacs.shape;
    3749              :                     }
    3750            5 :                     if (wacs.width != NBEdge::UNSPECIFIED_WIDTH) {
    3751            2 :                         wa.width = wacs.width;
    3752              :                     }
    3753            5 :                     wa.hasCustomShape = true;
    3754              :                 }
    3755              :             }
    3756              :         }
    3757              :         // determine length (average of all possible connections)
    3758              :         double lengthSum = 0;
    3759              :         int combinations = 0;
    3760        30854 :         for (std::vector<Position>::const_iterator it1 = connectedPoints.begin(); it1 != connectedPoints.end(); ++it1) {
    3761       109452 :             for (std::vector<Position>::const_iterator it2 = connectedPoints.begin(); it2 != connectedPoints.end(); ++it2) {
    3762              :                 const Position& p1 = *it1;
    3763              :                 const Position& p2 = *it2;
    3764              :                 if (p1 != p2) {
    3765        62082 :                     lengthSum += p1.distanceTo2D(p2);
    3766        62082 :                     combinations += 1;
    3767              :                 }
    3768              :             }
    3769              :         }
    3770              :         DEBUGCOUT(gDebugFlag1, "  combinations=" << combinations << " connectedPoints=" << connectedPoints << "\n")
    3771         7269 :         wa.length = POSITION_EPS;
    3772         7269 :         if (combinations > 0) {
    3773        14357 :             wa.length = MAX2(POSITION_EPS, lengthSum / combinations);
    3774              :         }
    3775         7269 :         myWalkingAreas.push_back(wa);
    3776         8670 :     }
    3777              :     // build walkingAreas between split crossings
    3778         6965 :     std::vector<Crossing*> validCrossings = getCrossings();
    3779         9854 :     for (std::vector<Crossing*>::iterator it = validCrossings.begin(); it != validCrossings.end(); ++it) {
    3780         2889 :         Crossing& prev = **it;
    3781         2889 :         Crossing& next = (it !=  validCrossings.begin() ? **(it - 1) :** (validCrossings.end() - 1));
    3782              :         DEBUGCOUT(gDebugFlag1, "  checkIntermediate: prev=" << prev.id << " next=" << next.id << " prev.nextWA=" << prev.nextWalkingArea << " next.prevWA=" << next.prevWalkingArea << "\n")
    3783         2889 :         if (prev.nextWalkingArea == "") {
    3784          217 :             if (next.prevWalkingArea != "" || &prev == &next) {
    3785           12 :                 WRITE_WARNINGF(TL("Invalid pedestrian topology: crossing '%' across [%] has no target."), prev.id, toString(prev.edges));
    3786            4 :                 prev.valid = false;
    3787            4 :                 continue;
    3788              :             }
    3789          639 :             WalkingArea wa(":" + getID() + "_w" + toString(index++), prev.width);
    3790              :             prev.nextWalkingArea = wa.id;
    3791          213 :             wa.nextCrossings.push_back(next.id);
    3792              :             next.prevWalkingArea = wa.id;
    3793              :             // back of previous crossing
    3794              :             PositionVector tmp = prev.shape;
    3795          213 :             tmp.move2side(-prev.width / 2);
    3796          213 :             wa.shape.push_back(tmp[-1]);
    3797          213 :             tmp.move2side(prev.width);
    3798          213 :             wa.shape.push_back(tmp[-1]);
    3799              :             // front of next crossing
    3800              :             tmp = next.shape;
    3801          213 :             tmp.move2side(prev.width / 2);
    3802          213 :             wa.shape.push_back(tmp[0]);
    3803          213 :             tmp.move2side(-prev.width);
    3804          213 :             wa.shape.push_back(tmp[0]);
    3805              :             wa.refEdges.insert(prev.edges.begin(), prev.edges.end());
    3806              :             wa.refEdges.insert(next.edges.begin(), next.edges.end());
    3807              :             // apply custom shapes
    3808          213 :             if (myWalkingAreaCustomShapes.size() > 0) {
    3809           48 :                 for (auto wacs : myWalkingAreaCustomShapes) {
    3810              :                     // every edge in wacs.edges must be part of crossed
    3811           30 :                     if (wacs.shape.size() != 0 && wacs.edges.size() > 1 && includes(wa.refEdges, wacs.edges)) {
    3812              :                         wa.shape = wacs.shape;
    3813            6 :                         wa.hasCustomShape = true;
    3814              :                     }
    3815              :                 }
    3816              :             }
    3817              :             // length (special case)
    3818          213 :             wa.length = MAX2(POSITION_EPS, prev.shape.back().distanceTo2D(next.shape.front()));
    3819          213 :             myWalkingAreas.push_back(wa);
    3820              :             DEBUGCOUT(gDebugFlag1, "     build wa=" << wa.id << "\n")
    3821          213 :         }
    3822              :     }
    3823         6965 : }
    3824              : 
    3825              : 
    3826              : void
    3827         6965 : NBNode::buildCrossingOutlines() {
    3828              : #ifdef DEBUG_CROSSING_OUTLINE
    3829              :     if (myCrossings.size() > 0) {
    3830              :         std::cerr << "<add>\n";
    3831              :     }
    3832              : #endif
    3833              :     std::map<std::string, PositionVector> waShapes;
    3834        14447 :     for (auto wa : myWalkingAreas) {
    3835         7482 :         waShapes[wa.id] = wa.shape;
    3836         7482 :     }
    3837         9850 :     for (auto c : getCrossings()) {
    3838         2885 :         PositionVector wa1 = waShapes[c->prevWalkingArea];
    3839         2885 :         PositionVector wa2 = waShapes[c->nextWalkingArea];
    3840         2885 :         if (wa1.empty() || wa2.empty()) {
    3841              :             continue;
    3842              :         }
    3843         2884 :         wa1.closePolygon();
    3844         2884 :         wa2.closePolygon();
    3845              :         PositionVector side1 = c->shape;
    3846         2884 :         PositionVector side2 = c->shape.reverse();
    3847         2884 :         side1.move2side(c->width / 2);
    3848         2884 :         side2.move2side(c->width / 2);
    3849              :         PositionVector side1default = side1;
    3850              :         PositionVector side2default = side2;
    3851         2884 :         side1.extrapolate(POSITION_EPS);
    3852         2884 :         side2.extrapolate(c->width);
    3853         5768 :         side1 = cutAtShapes(side1, wa1, wa2, side1default);
    3854         5768 :         side2 = cutAtShapes(side2, wa1, wa2, side2default);
    3855              :         PositionVector side1ex = side1;
    3856              :         PositionVector side2ex = side2;
    3857         2884 :         side1ex.extrapolate(POSITION_EPS);
    3858         2884 :         side2ex.extrapolate(side2 == side2default ? c->width / 2 : POSITION_EPS);
    3859         2884 :         PositionVector side3 = cutAtShapes(wa2, side1ex, side2ex, PositionVector());
    3860         2884 :         PositionVector side4 = cutAtShapes(wa1, side1ex, side2ex, PositionVector());
    3861              :         c->outlineShape = side1;
    3862         2884 :         c->outlineShape.append(side3, POSITION_EPS);
    3863         2884 :         c->outlineShape.append(side2, POSITION_EPS);
    3864         2884 :         c->outlineShape.append(side4, POSITION_EPS);
    3865         2884 :         c->outlineShape.removeDoublePoints();
    3866         2884 :         if (c->outlineShape.back().almostSame(c->outlineShape.front())) {
    3867              :             c->outlineShape.pop_back();
    3868              :         }
    3869              :         // DEBUG
    3870              : #ifdef DEBUG_CROSSING_OUTLINE
    3871              :         std::cout << "  side1=" << side1 << "\n  side2=" << side2 << "\n  side3=" << side3 << "\n  side4=" << side4 << "\n";
    3872              :         std::cerr << "<poly id=\"" << c->id << "\" shape=\"" << c->outlineShape << "\" color=\"blue\" lineWidth=\"0.2\" layer=\"100\"/>\n";
    3873              : #endif
    3874         9850 :     }
    3875              : #ifdef DEBUG_CROSSING_OUTLINE
    3876              :     if (myCrossings.size() > 0) {
    3877              :         std::cerr << "</add>\n";
    3878              :     }
    3879              : #endif
    3880         6965 : }
    3881              : 
    3882              : 
    3883              : PositionVector
    3884        11536 : NBNode::cutAtShapes(const PositionVector& cut, const PositionVector& border1, const PositionVector& border2, const PositionVector& def) {
    3885        11536 :     std::vector<double> is1 = cut.intersectsAtLengths2D(border1);
    3886        11536 :     std::vector<double> is2 = cut.intersectsAtLengths2D(border2);
    3887              : #ifdef DEBUG_CROSSING_OUTLINE
    3888              :     std::cout << "is1=" << is1 << " is2=" << is2 << " cut=" << cut << " border1=" << border1 << " border2=" << border2 << "\n";
    3889              : #endif
    3890        11536 :     if (is1.size() == 0 && border1.size() == 2) {
    3891         2066 :         const double d1 = cut.distance2D(border1.front());
    3892         2066 :         const double d2 = cut.distance2D(border1.back());
    3893         2066 :         Position closer = d1 < d2 ? border1.front() : border1.back();
    3894         2066 :         double nOp = cut.nearest_offset_to_point2D(closer, false);
    3895              : #ifdef DEBUG_CROSSING_OUTLINE
    3896              :         std::cout << " closer=" << closer << " nOp=" << nOp << "\n";
    3897              : #endif
    3898         2066 :         if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
    3899          544 :             is1.push_back(cut.length2D());
    3900              :         } else {
    3901         1522 :             is1.push_back(nOp);
    3902              :         }
    3903              :     }
    3904        11536 :     if (is2.size() == 0 && border2.size() == 2) {
    3905         1654 :         const double d1 = cut.distance2D(border2.front());
    3906         1654 :         const double d2 = cut.distance2D(border2.back());
    3907         1654 :         Position closer = d1 < d2 ? border2.front() : border2.back();
    3908         1654 :         double nOp = cut.nearest_offset_to_point2D(closer, false);
    3909         1654 :         if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
    3910            6 :             is2.push_back(cut.length2D());
    3911              :         } else {
    3912         1648 :             is2.push_back(nOp);
    3913              :         }
    3914              :     }
    3915        11536 :     if (is1.size() > 0 && is2.size() > 0) {
    3916              :         double of1 = VectorHelper<double>::maxValue(is1);
    3917              :         double of2 = VectorHelper<double>::minValue(is2);
    3918              : #ifdef DEBUG_CROSSING_OUTLINE
    3919              :         std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
    3920              : #endif
    3921         8460 :         if (of1 > of2) {
    3922              :             of1 = VectorHelper<double>::maxValue(is2);
    3923              :             of2 = VectorHelper<double>::minValue(is1);
    3924              : #ifdef DEBUG_CROSSING_OUTLINE
    3925              :             std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
    3926              : #endif
    3927              :         }
    3928         8460 :         if (of1 > of2) {
    3929              :             of2 = VectorHelper<double>::maxValue(is1);
    3930              :             of1 = VectorHelper<double>::minValue(is2);
    3931              : #ifdef DEBUG_CROSSING_OUTLINE
    3932              :             std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
    3933              : #endif
    3934              :         }
    3935              :         assert(of1 <= of2);
    3936         8460 :         return cut.getSubpart(of1, of2);
    3937              :     } else {
    3938              :         return def;
    3939              :     }
    3940        11536 : }
    3941              : 
    3942              : 
    3943              : bool
    3944           62 : NBNode::includes(const std::set<const NBEdge*, ComparatorIdLess>& super,
    3945              :                  const std::set<const NBEdge*, ComparatorIdLess>& sub) {
    3946              :     // for some reason std::include does not work reliably
    3947           85 :     for (const NBEdge* e : sub) {
    3948          148 :         if (super.count(const_cast<NBEdge*>(e)) == 0) {
    3949              :             return false;
    3950              :         }
    3951              :     }
    3952              :     return true;
    3953              : }
    3954              : 
    3955              : 
    3956              : bool
    3957        11380 : NBNode::crossingBetween(const NBEdge* e1, const NBEdge* e2) const {
    3958        11380 :     if (e1 == e2) {
    3959              :         return false;
    3960              :     }
    3961        11328 :     if (myAllEdges.size() > 3) {
    3962              :         // pedestrian scramble
    3963              :         return false;
    3964              :     }
    3965         4139 :     for (auto c : getCrossings()) {
    3966              :         const EdgeVector& edges = c->edges;
    3967          107 :         EdgeVector::const_iterator it1 = std::find(edges.begin(), edges.end(), e1);
    3968          107 :         EdgeVector::const_iterator it2 = std::find(edges.begin(), edges.end(), e2);
    3969          107 :         if (it1 != edges.end() && it2 != edges.end()) {
    3970            6 :             return true;
    3971              :         }
    3972         4038 :     }
    3973         4032 :     return false;
    3974              : }
    3975              : 
    3976              : 
    3977              : bool
    3978        10033 : NBNode::alreadyConnectedPaths(const NBEdge* e1, const NBEdge* e2, double dist) const {
    3979        10033 :     if (e1 == e2) {
    3980              :         return false;
    3981              :     }
    3982         9981 :     if (e1->getPermissions() != SVC_PEDESTRIAN
    3983         9981 :             || e2->getPermissions() != SVC_PEDESTRIAN) {
    3984              :         // no paths
    3985         6145 :         return false;
    3986              :     }
    3987         4963 :     if (e1->getFinalLength() > dist &&
    3988         1127 :             e2->getFinalLength() > dist) {
    3989              :         // too long
    3990              :         return false;
    3991              :     }
    3992         3291 :     NBNode* other1 = e1->getFromNode() == this ? e1->getToNode() : e1->getFromNode();
    3993         3291 :     NBNode* other2 = e2->getFromNode() == this ? e2->getToNode() : e2->getFromNode();
    3994         3291 :     return other1 == other2;
    3995              : }
    3996              : 
    3997              : 
    3998              : bool
    3999        11257 : NBNode::crossesFringe(const NBEdge* e1, const NBEdge* e2) const {
    4000        11257 :     return myFringeType != FringeType::DEFAULT
    4001           19 :            && myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1
    4002        11276 :            && (e1->isTurningDirectionAt(e2) || e2->isTurningDirectionAt(e1));
    4003              : }
    4004              : 
    4005              : 
    4006              : EdgeVector
    4007            0 : NBNode::edgesBetween(const NBEdge* e1, const NBEdge* e2) const {
    4008              :     EdgeVector result;
    4009            0 :     EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), e1);
    4010              :     assert(it != myAllEdges.end());
    4011            0 :     NBContHelper::nextCW(myAllEdges, it);
    4012            0 :     EdgeVector::const_iterator it_end = std::find(myAllEdges.begin(), myAllEdges.end(), e2);
    4013              :     assert(it_end != myAllEdges.end());
    4014            0 :     while (it != it_end) {
    4015            0 :         result.push_back(*it);
    4016            0 :         NBContHelper::nextCW(myAllEdges, it);
    4017              :     }
    4018            0 :     return result;
    4019            0 : }
    4020              : 
    4021              : 
    4022              : void
    4023           11 : NBNode::addWalkingAreaShape(EdgeVector edges, const PositionVector& shape, double width) {
    4024           11 :     WalkingAreaCustomShape wacs;
    4025              :     wacs.edges.insert(edges.begin(), edges.end());
    4026              :     wacs.shape = shape;
    4027           11 :     wacs.width = width;
    4028           11 :     myWalkingAreaCustomShapes.push_back(wacs);
    4029           11 : }
    4030              : 
    4031              : 
    4032              : bool
    4033       340564 : NBNode::geometryLike() const {
    4034       340564 :     return geometryLike(myIncomingEdges, myOutgoingEdges);
    4035              : }
    4036              : 
    4037              : bool
    4038       376166 : NBNode::geometryLike(const EdgeVector& incoming, const EdgeVector& outgoing) {
    4039       376166 :     if (incoming.size() == 1 && outgoing.size() == 1) {
    4040        74770 :         return incoming.front()->getBidiEdge() != outgoing.front();
    4041              :     }
    4042       301396 :     if (incoming.size() == 2 && outgoing.size() == 2) {
    4043              :         // check whether the incoming and outgoing edges are pairwise (near) parallel and
    4044              :         // thus the only cross-connections could be turn-arounds
    4045        62732 :         NBEdge* in0 = incoming[0];
    4046        62732 :         NBEdge* in1 = incoming[1];
    4047        62732 :         NBEdge* out0 = outgoing[0];
    4048        62732 :         NBEdge* out1 = outgoing[1];
    4049       112505 :         if ((in0->isTurningDirectionAt(out0) || in0->isTurningDirectionAt(out1))
    4050        64701 :                 && (in1->isTurningDirectionAt(out0) || in1->isTurningDirectionAt(out1))) {
    4051        11411 :             return true;
    4052              :         }
    4053        51321 :         if (in0->getGeometry() == in1->getGeometry() && out0->getGeometry() == out1->getGeometry()) {
    4054              :             // overlapping edges
    4055              :             return true;
    4056              :         }
    4057       106819 :         for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
    4058        84546 :             NBEdge* inEdge = *it;
    4059        84546 :             double angle0 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out0->getAngleAtNode(out0->getFromNode())));
    4060        84546 :             double angle1 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out1->getAngleAtNode(out1->getFromNode())));
    4061        84546 :             if (MAX2(angle0, angle1) <= 160) {
    4062              :                 // neither of the outgoing edges is parallel to inEdge
    4063              :                 return false;
    4064              :             }
    4065              :         }
    4066              :         return true;
    4067              :     }
    4068              :     return false;
    4069              : }
    4070              : 
    4071              : void
    4072          605 : NBNode::setRoundabout() {
    4073          605 :     if (myType == SumoXMLNodeType::RIGHT_BEFORE_LEFT || myType == SumoXMLNodeType::LEFT_BEFORE_RIGHT) {
    4074           10 :         myType = SumoXMLNodeType::PRIORITY;
    4075              :     }
    4076          605 : }
    4077              : 
    4078              : bool
    4079         1457 : NBNode::isRoundabout() const {
    4080         5715 :     for (NBEdge* out : myOutgoingEdges) {
    4081         4276 :         if (out->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
    4082              :             return true;
    4083              :         }
    4084              :     }
    4085              :     return false;
    4086              : }
    4087              : 
    4088              : NBNode::Crossing*
    4089         3305 : NBNode::addCrossing(EdgeVector edges, double width, bool priority, int tlIndex, int tlIndex2,
    4090              :                     const PositionVector& customShape, bool fromSumoNet, const Parameterised* params) {
    4091         3305 :     Crossing* c = new Crossing(this, edges, width, priority, tlIndex, tlIndex2, customShape);
    4092         3305 :     if (params != nullptr) {
    4093         1381 :         c->updateParameters(params->getParametersMap());
    4094              :     }
    4095         3305 :     myCrossings.push_back(std::unique_ptr<Crossing>(c));
    4096         3305 :     if (fromSumoNet) {
    4097         1381 :         myCrossingsLoadedFromSumoNet += 1;
    4098              :     }
    4099         3305 :     return c;
    4100              : }
    4101              : 
    4102              : 
    4103              : void
    4104            4 : NBNode::removeCrossing(const EdgeVector& edges) {
    4105            4 :     EdgeSet edgeSet(edges.begin(), edges.end());
    4106           20 :     for (auto it = myCrossings.begin(); it != myCrossings.end();) {
    4107           16 :         EdgeSet edgeSet2((*it)->edges.begin(), (*it)->edges.end());
    4108           16 :         if (edgeSet == edgeSet2) {
    4109            4 :             it = myCrossings.erase(it);
    4110              :         } else {
    4111              :             ++it;
    4112              :         }
    4113              :     }
    4114            4 : }
    4115              : 
    4116              : 
    4117              : NBNode::Crossing*
    4118         1581 : NBNode::getCrossing(const std::string& id) const {
    4119         3413 :     for (auto& c : myCrossings) {
    4120         3413 :         if (c->id == id) {
    4121         1581 :             return c.get();
    4122              :         }
    4123              :     }
    4124            0 :     throw ProcessError(TLF("Request for unknown crossing '%'", id));
    4125              : }
    4126              : 
    4127              : 
    4128              : NBNode::Crossing*
    4129            3 : NBNode::getCrossing(const EdgeVector& edges, bool hardFail) const {
    4130            3 :     const EdgeSet edgeSet(edges.begin(), edges.end());
    4131           13 :     for (auto& crossing : myCrossings) {
    4132           13 :         const EdgeSet edgeSet2(crossing->edges.begin(), crossing->edges.end());
    4133           13 :         if (edgeSet == edgeSet2) {
    4134              :             return crossing.get();
    4135              :         }
    4136              :     }
    4137            0 :     if (!hardFail) {
    4138              :         return nullptr;
    4139              :     }
    4140            0 :     throw ProcessError(TL("Request for unknown crossing for the given Edges"));
    4141              : }
    4142              : 
    4143              : 
    4144              : NBNode::WalkingArea&
    4145            0 : NBNode::getWalkingArea(const std::string& id) {
    4146            0 :     for (auto& walkingArea : myWalkingAreas) {
    4147            0 :         if (walkingArea.id == id) {
    4148              :             return walkingArea;
    4149              :         }
    4150              :     }
    4151              :     // not found, maybe we need to rebuild
    4152            0 :     updateSurroundingGeometry();
    4153            0 :     sortEdges(true);
    4154            0 :     buildCrossingsAndWalkingAreas();
    4155            0 :     for (auto& walkingArea : myWalkingAreas) {
    4156            0 :         if (walkingArea.id == id) {
    4157              :             return walkingArea;
    4158              :         }
    4159              :     }
    4160            0 :     if (myWalkingAreas.size() > 0) {
    4161              :         // don't crash
    4162            0 :         WRITE_WARNINGF("Could not retrieve walkingarea '%' (edge ordering changed after recompute).", id);
    4163            0 :         return myWalkingAreas.front();
    4164              :     }
    4165            0 :     throw ProcessError(TLF("Request for unknown walkingarea '%'.", id));
    4166              : }
    4167              : 
    4168              : 
    4169              : bool
    4170         8322 : NBNode::setCrossingTLIndices(const std::string& tlID, int startIndex, bool ignoreCustom) {
    4171              :     bool usedCustom = false;
    4172        10689 :     for (auto c : getCrossings()) {
    4173         2367 :         c->tlLinkIndex = startIndex++;
    4174         2367 :         c->tlID = tlID;
    4175         2367 :         if (c->customTLIndex != -1 && !ignoreCustom) {
    4176          892 :             usedCustom |= (c->tlLinkIndex != c->customTLIndex);
    4177          892 :             c->tlLinkIndex = c->customTLIndex;
    4178              :         }
    4179         2367 :         if (c->customTLIndex2 != -1 && !ignoreCustom) {
    4180              :             usedCustom = true;
    4181           49 :             c->tlLinkIndex2 = c->customTLIndex2;
    4182              :         }
    4183         8322 :     }
    4184         8322 :     return usedCustom;
    4185              : }
    4186              : 
    4187              : 
    4188              : int
    4189        61572 : NBNode::numNormalConnections() const {
    4190        61572 :     if (myRequest == nullptr) {
    4191              :         // could be an uncontrolled type
    4192              :         int result = 0;
    4193          534 :         for (const NBEdge* const edge : myIncomingEdges) {
    4194          252 :             result += (int)edge->getConnections().size();
    4195              :         }
    4196          282 :         return result;
    4197              :     } else {
    4198        61290 :         return myRequest->getSizes().second;
    4199              :     }
    4200              : }
    4201              : 
    4202              : 
    4203              : int
    4204       270842 : NBNode::getConnectionIndex(const NBEdge* from, const NBEdge::Connection& con) const {
    4205              :     int result = 0;
    4206       546834 :     for (const NBEdge* const e : myIncomingEdges) {
    4207      1812204 :         for (const NBEdge::Connection& cand : e->getConnections()) {
    4208      1536212 :             if (e == from && cand.fromLane == con.fromLane && cand.toLane == con.toLane && cand.toEdge == con.toEdge) {
    4209              :                 return result;
    4210              :             }
    4211      1265370 :             result++;
    4212              :         }
    4213              :     }
    4214              :     return -1;
    4215              : }
    4216              : 
    4217              : 
    4218              : Position
    4219       152662 : NBNode::getCenter() const {
    4220              :     /* Conceptually, the center point would be identical with myPosition.
    4221              :     * However, if the shape is influenced by custom geometry endpoints of the adjoining edges,
    4222              :     * myPosition may fall outside the shape. In this case it is better to use
    4223              :     * the center of the shape
    4224              :     **/
    4225              :     PositionVector tmp = myPoly;
    4226       152662 :     tmp.closePolygon();
    4227              :     //std::cout << getID() << " around=" << tmp.around(myPosition) << " dist=" << tmp.distance2D(myPosition) << "\n";
    4228       152662 :     if (tmp.size() < 3 || tmp.around(myPosition) || tmp.distance2D(myPosition) < POSITION_EPS) {
    4229       146889 :         return myPosition;
    4230              :     }
    4231         5773 :     return myPoly.getPolygonCenter();
    4232       152662 : }
    4233              : 
    4234              : 
    4235              : EdgeVector
    4236        10364 : NBNode::getEdgesSortedByAngleAtNodeCenter() const {
    4237        10364 :     EdgeVector result = myAllEdges;
    4238              : #ifdef DEBUG_PED_STRUCTURES
    4239              :     if (gDebugFlag1) {
    4240              :         std::cout << "  angles:\n";
    4241              :         for (EdgeVector::const_iterator it = result.begin(); it != result.end(); ++it) {
    4242              :             std::cout << "    edge=" << (*it)->getID() << " edgeAngle=" << (*it)->getAngleAtNode(this) << " angleToShape=" << (*it)->getAngleAtNodeToCenter(this) << "\n";
    4243              :         }
    4244              :         std::cout << "  allEdges before: " << toString(result) << "\n";
    4245              :     }
    4246              : #endif
    4247        10364 :     sort(result.begin(), result.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
    4248              :     // let the first edge in myAllEdges remain the first
    4249              :     DEBUGCOUT(gDebugFlag1, "  allEdges sorted: " << toString(result) << "\n")
    4250        10364 :     rotate(result.begin(), std::find(result.begin(), result.end(), *myAllEdges.begin()), result.end());
    4251              :     DEBUGCOUT(gDebugFlag1, "  allEdges rotated: " << toString(result) << "\n")
    4252        10364 :     return result;
    4253            0 : }
    4254              : 
    4255              : 
    4256              : void
    4257        33341 : NBNode::avoidOverlap() {
    4258              :     // simple case: edges with LaneSpreadFunction::CENTER and a (possible) turndirection at the same node
    4259              :     bool haveModifications = false;
    4260        87516 :     for (EdgeVector::iterator it = myIncomingEdges.begin(); it != myIncomingEdges.end(); it++) {
    4261        54175 :         NBEdge* edge = *it;
    4262        54175 :         NBEdge* turnDest = edge->getTurnDestination(true);
    4263        54175 :         if (turnDest != nullptr) {
    4264        33860 :             haveModifications |= edge->shiftPositionAtNode(this, turnDest);
    4265        33860 :             haveModifications |= turnDest->shiftPositionAtNode(this, edge);
    4266              :         }
    4267              :     }
    4268        33341 :     if (haveModifications) {
    4269         2399 :         NBTurningDirectionsComputer::computeTurnDirectionsForNode(this, false);
    4270              :     }
    4271              :     // @todo: edges in the same direction with sharp angles starting/ending at the same position
    4272        33341 : }
    4273              : 
    4274              : 
    4275              : bool
    4276       375021 : NBNode::isTrafficLight(SumoXMLNodeType type) {
    4277              :     return type == SumoXMLNodeType::TRAFFIC_LIGHT
    4278              :            || type == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION
    4279       375021 :            || type == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED;
    4280              : }
    4281              : 
    4282              : 
    4283              : bool
    4284      1549730 : NBNode::extraConflict(int index, int foeIndex) const {
    4285      1796241 :     for (NBTrafficLightDefinition* def : myTrafficLights) {
    4286       246552 :         if (def->extraConflict(index, foeIndex)) {
    4287              :             return true;
    4288              :         }
    4289              :     }
    4290              :     return false;
    4291              : }
    4292              : 
    4293              : 
    4294              : void
    4295       230497 : NBNode::sortEdges(bool useNodeShape) {
    4296       230497 :     if (myAllEdges.size() == 0) {
    4297         2557 :         return;
    4298              :     }
    4299       227940 :     EdgeVector allEdgesOriginal = myAllEdges;
    4300              :     EdgeVector& allEdges = myAllEdges;
    4301              :     EdgeVector& incoming = myIncomingEdges;
    4302              :     EdgeVector& outgoing = myOutgoingEdges;
    4303              : 
    4304              :     // sort the edges by angle (this is the canonical sorting)
    4305       227940 :     std::sort(allEdges.begin(), allEdges.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
    4306       227940 :     std::sort(incoming.begin(), incoming.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
    4307       227940 :     std::sort(outgoing.begin(), outgoing.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
    4308              :     std::vector<NBEdge*>::iterator j;
    4309       804720 :     for (j = allEdges.begin(); j != allEdges.end() - 1 && j != allEdges.end(); ++j) {
    4310       576780 :         NBNodesEdgesSorter::swapWhenReversed(this, j, j + 1);
    4311              :     }
    4312       227940 :     if (allEdges.size() > 1 && j != allEdges.end()) {
    4313       199454 :         NBNodesEdgesSorter::swapWhenReversed(this, allEdges.end() - 1, allEdges.begin());
    4314              :     }
    4315              : 
    4316              :     // sort again using additional geometry information
    4317       227940 :     NBEdge* firstOfAll = allEdges.front();
    4318       227940 :     NBEdge* firstOfIncoming = incoming.size() > 0 ? incoming.front() : 0;
    4319       227940 :     NBEdge* firstOfOutgoing = outgoing.size() > 0 ? outgoing.front() : 0;
    4320              :     // sort by the angle between the node shape center and the point where the edge meets the node shape
    4321       227940 :     std::sort(allEdges.begin(), allEdges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
    4322       227940 :     std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
    4323       227940 :     std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
    4324              :     // let the first edge remain the first
    4325       227940 :     rotate(allEdges.begin(), std::find(allEdges.begin(), allEdges.end(), firstOfAll), allEdges.end());
    4326       227940 :     if (firstOfIncoming != nullptr) {
    4327       213464 :         rotate(incoming.begin(), std::find(incoming.begin(), incoming.end(), firstOfIncoming), incoming.end());
    4328              :     }
    4329       227940 :     if (firstOfOutgoing != nullptr) {
    4330       209912 :         rotate(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), firstOfOutgoing), outgoing.end());
    4331              :     }
    4332              : #ifdef DEBUG_EDGE_SORTING
    4333              :     if (DEBUGCOND) {
    4334              :         std::cout << "sortedEdges (useNodeShape=" << useNodeShape << "):\n";
    4335              :         for (NBEdge* e : allEdges) {
    4336              :             std::cout << "  " << e->getID()
    4337              :                       << " angleToCenter=" << e->getAngleAtNodeToCenter(this)
    4338              :                       << " junctionAngle=" << e->getAngleAtNode(this) << "\n";
    4339              :         }
    4340              :     }
    4341              : #endif
    4342              : 
    4343              :     // fixing some pathological all edges orderings
    4344              :     // if every of the edges a,b,c has a turning edge a',b',c' the all edges ordering should be a,a',b,b',c,c'
    4345       227940 :     if (incoming.size() == outgoing.size() && incoming.front() == allEdges.front()) {
    4346              :         std::vector<NBEdge*>::const_iterator in, out;
    4347              :         std::vector<NBEdge*> allTmp;
    4348       250910 :         for (in = incoming.begin(), out = outgoing.begin(); in != incoming.end(); ++in, ++out) {
    4349       195498 :             if ((*in)->isTurningDirectionAt(*out)) {
    4350       167756 :                 allTmp.push_back(*in);
    4351       167756 :                 allTmp.push_back(*out);
    4352              :             } else {
    4353              :                 break;
    4354              :             }
    4355              :         }
    4356        83154 :         if (allTmp.size() == allEdges.size()) {
    4357        55412 :             allEdges = allTmp;
    4358              :         }
    4359        83154 :     }
    4360              :     // sort the crossings
    4361       227940 :     std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, allEdges));
    4362              :     //if (crossings.size() > 0) {
    4363              :     //    std::cout << " crossings at " << getID() << "\n";
    4364              :     //    for (std::vector<NBNode::Crossing*>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
    4365              :     //        std::cout << "  " << toString((*it)->edges) << "\n";
    4366              :     //    }
    4367              :     //}
    4368              : 
    4369       227940 :     if (useNodeShape && myAllEdges != allEdgesOriginal) {
    4370              :         // sorting order changed after node shape was computed.
    4371          590 :         computeNodeShape(-1);
    4372         4703 :         for (NBEdge* e : myAllEdges) {
    4373         4113 :             e->computeEdgeShape();
    4374              :         }
    4375              :     }
    4376       227940 : }
    4377              : 
    4378              : std::vector<std::pair<Position, std::string> >
    4379            0 : NBNode::getEndPoints() const {
    4380              :     // using a set would be nicer but we want to have some slack in position identification
    4381              :     std::vector<std::pair<Position, std::string> >result;
    4382            0 :     for (NBEdge* e : myAllEdges) {
    4383            0 :         Position pos = this == e->getFromNode() ? e->getGeometry().front() : e->getGeometry().back();
    4384            0 :         const std::string origID = e->getParameter(this == e->getFromNode() ? "origFrom" : "origTo");
    4385              :         bool unique = true;
    4386            0 :         for (const auto& pair : result) {
    4387            0 :             if (pos.almostSame(pair.first) || (origID != "" && pair.second == origID)) {
    4388              :                 unique = false;
    4389              :                 break;
    4390              :             }
    4391              :         }
    4392            0 :         if (unique) {
    4393            0 :             result.push_back(std::make_pair(pos, origID));
    4394              :         }
    4395              :     }
    4396            0 :     return result;
    4397            0 : }
    4398              : 
    4399              : 
    4400              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1