LCOV - code coverage report
Current view: top level - src/netbuild - NBNode.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.6 % 1841 1687
Test Date: 2024-12-21 15:45:41 Functions: 91.5 % 129 118

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

Generated by: LCOV version 2.0-1