LCOV - code coverage report
Current view: top level - src/netbuild - NBNodeCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 94.9 % 1237 1174
Test Date: 2024-12-21 15:45:41 Functions: 93.8 % 65 61

            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    NBNodeCont.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Yun-Pang Floetteroed
      18              : /// @author  Walter Bamberger
      19              : /// @author  Laura Bieker
      20              : /// @author  Michael Behrisch
      21              : /// @author  Sascha Krieg
      22              : /// @date    Tue, 20 Nov 2001
      23              : ///
      24              : // Container for nodes during the netbuilding process
      25              : /****************************************************************************/
      26              : #include <config.h>
      27              : 
      28              : #include <string>
      29              : #include <map>
      30              : #include <algorithm>
      31              : #include <cmath>
      32              : #include <utils/options/OptionsCont.h>
      33              : #include <utils/geom/Boundary.h>
      34              : #include <utils/geom/GeomHelper.h>
      35              : #include <utils/common/MsgHandler.h>
      36              : #include <utils/common/UtilExceptions.h>
      37              : #include <utils/common/StringTokenizer.h>
      38              : #include <utils/common/StringUtils.h>
      39              : #include <utils/common/StdDefs.h>
      40              : #include <utils/common/ToString.h>
      41              : #include <utils/common/StringUtils.h>
      42              : #include <utils/common/IDSupplier.h>
      43              : #include <utils/xml/SUMOXMLDefinitions.h>
      44              : #include <utils/geom/GeoConvHelper.h>
      45              : #include <utils/iodevices/OutputDevice.h>
      46              : #include "NBHelpers.h"
      47              : #include "NBAlgorithms.h"
      48              : #include "NBDistrict.h"
      49              : #include "NBEdgeCont.h"
      50              : #include "NBTrafficLightLogicCont.h"
      51              : #include "NBOwnTLDef.h"
      52              : #include "NBPTStop.h"
      53              : #include "NBNodeCont.h"
      54              : #include "NBPTStopCont.h"
      55              : #include "NBPTLineCont.h"
      56              : #include "NBParking.h"
      57              : 
      58              : // ===========================================================================
      59              : // Algorithm constants
      60              : // ===========================================================================
      61              : #define MAX_SLIPLANE_LENGTH 1000
      62              : 
      63              : // ===========================================================================
      64              : // Debug Flags
      65              : // ===========================================================================
      66              : 
      67              : //#define DEBUG_JOINJUNCTIONS
      68              : //#define DEBUG_JOINJUNCTIONS_CONNECTIONS
      69              : //#define DEBUG_GUESSSIGNALS
      70              : #define DEBUGNODEID ""
      71              : #define DEBUGNODEID2 ""
      72              : //#define DEBUGNODEID "5548037023"
      73              : #define DEBUGCOND(obj) ((obj) != 0 && ((obj)->getID() == DEBUGNODEID || (obj)->getID() == DEBUGNODEID2))
      74              : //#define DEBUGCOND(obj) (true)
      75              : 
      76              : 
      77              : // ===========================================================================
      78              : // method definitions
      79              : // ===========================================================================
      80         2002 : NBNodeCont::~NBNodeCont() {
      81         2002 :     clear();
      82         4004 : }
      83              : 
      84              : 
      85              : // ----------- Insertion/removal/retrieval of nodes
      86              : bool
      87         1254 : NBNodeCont::insert(const std::string& id, const Position& position,
      88              :                    NBDistrict* district) {
      89              :     NodeCont::iterator i = myNodes.find(id);
      90         1254 :     if (i != myNodes.end()) {
      91              :         return false;
      92              :     }
      93         1212 :     NBNode* node = new NBNode(id, position, district);
      94         1212 :     myNodes[id] = node;
      95         1212 :     const float pos[2] = {(float)position.x(), (float)position.y()};
      96         1212 :     myRTree.Insert(pos, pos, node);
      97         1212 :     return true;
      98              : }
      99              : 
     100              : 
     101              : bool
     102        61850 : NBNodeCont::insert(NBNode* node) {
     103              :     std::string id = node->getID();
     104              :     NodeCont::iterator i = myNodes.find(id);
     105        61850 :     if (i != myNodes.end()) {
     106              :         return false;
     107              :     }
     108        61672 :     myNodes[id] = node;
     109        61672 :     const float pos[2] = {(float)node->getPosition().x(), (float)node->getPosition().y()};
     110        61672 :     myRTree.Insert(pos, pos, node);
     111        61672 :     return true;
     112              : }
     113              : 
     114              : 
     115              : NBNode*
     116       356502 : NBNodeCont::retrieve(const std::string& id) const {
     117              :     NodeCont::const_iterator i = myNodes.find(id);
     118       356502 :     if (i == myNodes.end()) {
     119              :         return nullptr;
     120              :     }
     121       314531 :     return (*i).second;
     122              : }
     123              : 
     124              : 
     125              : NBNode*
     126          236 : NBNodeCont::retrieve(const Position& position, const double offset) const {
     127          236 :     const double extOffset = offset + POSITION_EPS;
     128          236 :     const float cmin[2] = {(float)(position.x() - extOffset), (float)(position.y() - extOffset)};
     129          236 :     const float cmax[2] = {(float)(position.x() + extOffset), (float)(position.y() + extOffset)};
     130              :     std::set<const Named*> into;
     131              :     Named::StoringVisitor sv(into);
     132              :     myRTree.Search(cmin, cmax, sv);
     133          236 :     for (const Named* namedNode : into) {
     134           78 :         NBNode* node = const_cast<NBNode*>(dynamic_cast<const NBNode*>(namedNode));
     135           78 :         if (fabs(node->getPosition().x() - position.x()) <= offset
     136           78 :                 &&
     137           78 :                 fabs(node->getPosition().y() - position.y()) <= offset) {
     138              :             return node;
     139              :         }
     140              :     }
     141              :     return nullptr;
     142              : }
     143              : 
     144              : 
     145              : bool
     146         2834 : NBNodeCont::erase(NBNode* node) {
     147         2834 :     if (extract(node)) {
     148         2834 :         delete node;
     149         2834 :         return true;
     150              :     } else {
     151              :         return false;
     152              :     }
     153              : }
     154              : 
     155              : 
     156              : bool
     157        11367 : NBNodeCont::extract(NBNode* node, bool remember) {
     158              :     NodeCont::iterator i = myNodes.find(node->getID());
     159        11367 :     if (i == myNodes.end()) {
     160              :         return false;
     161              :     }
     162              :     myNodes.erase(i);
     163        11367 :     const float pos[2] = {(float)node->getPosition().x(), (float)node->getPosition().y()};
     164        11367 :     myRTree.Remove(pos, pos, node);
     165        11367 :     node->removeTrafficLights();
     166        11367 :     if (remember) {
     167         8533 :         myExtractedNodes[node->getID()] = node;
     168              :     }
     169              :     return true;
     170              : }
     171              : 
     172              : 
     173              : // ----------- Adapting the input
     174              : int
     175         1704 : NBNodeCont::removeSelfLoops(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tc) {
     176         1704 :     int no = 0;
     177        62995 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
     178        61291 :         no += (*i).second->removeSelfLoops(dc, ec, tc);
     179              :     }
     180         1704 :     if (no != 0) {
     181            0 :         WRITE_WARNING(toString(no) + " self-looping edge(s) removed.");
     182              :     }
     183         1704 :     return no;
     184              : }
     185              : 
     186              : 
     187              : void
     188           19 : NBNodeCont::joinSimilarEdges(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, bool removeDuplicates) {
     189              :     // magic values
     190              :     const double distanceThreshold = 7.; // don't merge edges further apart
     191              :     const double lengthThreshold = 0.10; // don't merge edges with higher relative length-difference
     192              : 
     193         1408 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
     194              :         // count the edges to other nodes outgoing from the current node
     195              :         std::map<NBNode*, EdgeVector> connectionCount;
     196         1389 :         const EdgeVector& outgoing = (*i).second->getOutgoingEdges();
     197         3069 :         for (EdgeVector::const_iterator j = outgoing.begin(); j != outgoing.end(); j++) {
     198         1680 :             connectionCount[(*j)->getToNode()].push_back(*j);
     199              :         }
     200              :         // check whether more than a single edge connect another node and join them
     201              :         std::map<NBNode*, EdgeVector>::iterator k;
     202         2995 :         for (k = connectionCount.begin(); k != connectionCount.end(); k++) {
     203              :             // possibly we do not have anything to join...
     204         1606 :             if ((*k).second.size() < 2) {
     205              :                 continue;
     206              :             }
     207              :             // for the edges that seem to be a single street,
     208              :             //  check whether the geometry is similar
     209           65 :             const EdgeVector& ev = (*k).second;
     210           65 :             const NBEdge* const first = ev.front();
     211              :             EdgeVector::const_iterator jci; // join candidate iterator
     212           87 :             for (jci = ev.begin() + 1; jci != ev.end(); ++jci) {
     213           65 :                 const double relativeLengthDifference = fabs(first->getLoadedLength() - (*jci)->getLoadedLength()) / first->getLoadedLength();
     214           94 :                 if ((!first->isNearEnough2BeJoined2(*jci, distanceThreshold)) ||
     215           23 :                         (relativeLengthDifference > lengthThreshold) ||
     216          110 :                         (fabs(first->getSpeed() - (*jci)->getSpeed()) >= 0.01) || // output accuracy
     217           22 :                         (first->getPermissions() != (*jci)->getPermissions())
     218              :                    ) {
     219              :                     break;
     220              :                 }
     221              :             }
     222              :             // @bug If there are 3 edges of which 2 can be joined, no joining will
     223              :             //   take place with the current implementation
     224           65 :             if (jci == ev.end()) {
     225           22 :                 if (removeDuplicates) {
     226           20 :                     for (int ei = 1; ei < (int)ev.size(); ei++) {
     227           10 :                         ec.extract(dc, ev[ei], true);
     228              :                     }
     229              :                 } else {
     230           12 :                     ec.joinSameNodeConnectingEdges(dc, tlc, ev);
     231              :                 }
     232              :             }
     233              :         }
     234              :     }
     235           19 : }
     236              : 
     237              : 
     238              : int
     239            5 : NBNodeCont::removeIsolatedRoads(NBDistrictCont& dc, NBEdgeCont& ec) {
     240              :     int numRemovedEdges = 0;
     241              :     // Warn of isolated edges, i.e. a single edge with no connection to another edge
     242            5 :     const std::vector<std::string>& edgeNames = ec.getAllNames();
     243          379 :     for (std::vector<std::string>::const_iterator it = edgeNames.begin(); it != edgeNames.end(); ++it) {
     244              :         // Test whether this node starts at a dead end, i.e. it has only one adjacent node
     245              :         // to which an edge exists and from which an edge may come.
     246          374 :         NBEdge* e = ec.retrieve(*it);
     247          374 :         if (e == nullptr) {
     248          318 :             continue;
     249              :         }
     250              :         NBNode* from = e->getFromNode();
     251              :         const EdgeVector& outgoingEdges = from->getOutgoingEdges();
     252          364 :         if (outgoingEdges.size() != 1) {
     253              :             // At this node, several edges or no edge start; so, this node is no dead end.
     254          268 :             continue;
     255              :         }
     256              :         const EdgeVector& incomingEdges = from->getIncomingEdges();
     257           96 :         if (incomingEdges.size() > 1) {
     258              :             // At this node, several edges end; so, this node is no dead end.
     259           11 :             continue;
     260           85 :         } else if (incomingEdges.size() == 1) {
     261           64 :             NBNode* fromNodeOfIncomingEdge = incomingEdges[0]->getFromNode();
     262           64 :             NBNode* toNodeOfOutgoingEdge = outgoingEdges[0]->getToNode();
     263           64 :             if (fromNodeOfIncomingEdge != toNodeOfOutgoingEdge) {
     264              :                 // At this node, an edge ends which is not the inverse direction of
     265              :                 // the starting node.
     266           29 :                 continue;
     267              :             }
     268              :         }
     269              :         // Now we know that the edge e starts a dead end.
     270              :         // Next we test if the dead end is isolated, i.e. does not lead to a junction
     271              :         bool hasJunction = false;
     272              :         EdgeVector road;
     273              :         NBEdge* eOld = nullptr;
     274              :         NBNode* to;
     275              :         NodeSet adjacentNodes;
     276              :         do {
     277           91 :             road.push_back(e);
     278           91 :             eOld = e;
     279              :             from = e->getFromNode();
     280           91 :             to = e->getToNode();
     281              :             const EdgeVector& outgoingEdgesOfToNode = to->getOutgoingEdges();
     282              :             const EdgeVector& incomingEdgesOfToNode = to->getIncomingEdges();
     283              :             adjacentNodes.clear();
     284          254 :             for (EdgeVector::const_iterator itOfOutgoings = outgoingEdgesOfToNode.begin(); itOfOutgoings != outgoingEdgesOfToNode.end(); ++itOfOutgoings) {
     285          163 :                 if ((*itOfOutgoings)->getToNode() != from        // The back path
     286          163 :                         && (*itOfOutgoings)->getToNode() != to   // A loop / dummy edge
     287              :                    ) {
     288          109 :                     e = *itOfOutgoings; // Probably the next edge
     289              :                 }
     290          163 :                 adjacentNodes.insert((*itOfOutgoings)->getToNode());
     291              :             }
     292          279 :             for (EdgeVector::const_iterator itOfIncomings = incomingEdgesOfToNode.begin(); itOfIncomings != incomingEdgesOfToNode.end(); ++itOfIncomings) {
     293          188 :                 adjacentNodes.insert((*itOfIncomings)->getFromNode());
     294              :             }
     295              :             adjacentNodes.erase(to);  // Omit loops
     296           91 :             if (adjacentNodes.size() > 2) {
     297              :                 hasJunction = true;
     298              :             }
     299           49 :         } while (!hasJunction && eOld != e);
     300           56 :         if (!hasJunction) {
     301              :             std::string warningString;
     302           46 :             for (EdgeVector::iterator roadIt = road.begin(); roadIt != road.end(); ++roadIt) {
     303           32 :                 if (roadIt == road.begin()) {
     304           14 :                     warningString += (*roadIt)->getID();
     305              :                 } else {
     306           36 :                     warningString += "," + (*roadIt)->getID();
     307              :                 }
     308              : 
     309           32 :                 NBNode* fromNode = (*roadIt)->getFromNode();
     310              :                 NBNode* toNode = (*roadIt)->getToNode();
     311           32 :                 ec.erase(dc, *roadIt);
     312           32 :                 numRemovedEdges++;
     313           32 :                 if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
     314              :                     // Node is empty; can be removed
     315           16 :                     erase(fromNode);
     316              :                 }
     317           32 :                 if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
     318              :                     // Node is empty; can be removed
     319            7 :                     erase(toNode);
     320              :                 }
     321              :             }
     322           42 :             WRITE_WARNINGF(TL("Removed a road without junctions: %."), warningString);
     323              :         }
     324           56 :     }
     325            5 :     return numRemovedEdges;
     326            5 : }
     327              : 
     328              : 
     329              : int
     330           23 : NBNodeCont::removeComponents(NBDistrictCont& dc, NBEdgeCont& ec, const int numKeep, bool hasPTStops) {
     331              :     myRailComponents.clear();
     332              :     std::vector<std::set<NBEdge*> > components;
     333              :     // need to use ids here to have the same ordering on all platforms
     334              :     std::set<std::string> edgesLeft;
     335         2293 :     for (std::map<std::string, NBEdge*>::const_iterator edgeIt = ec.begin(); edgeIt != ec.end(); ++edgeIt) {
     336         2270 :         edgesLeft.insert(edgeIt->first);
     337              :     }
     338              :     EdgeVector queue;
     339              :     std::set<NBEdge*> toRemove;
     340           23 :     int foundComponents = 0;
     341           23 :     int numRemoved = 0;
     342          104 :     while (!edgesLeft.empty()) {
     343           81 :         queue.push_back(ec.getByID(*edgesLeft.begin()));
     344              :         std::set<NBEdge*> component;
     345         2432 :         while (!queue.empty()) {
     346         2351 :             NBEdge* const e = queue.back();
     347              :             queue.pop_back();
     348              :             component.insert(e);
     349              :             std::vector<EdgeVector> edgeLists;
     350         2351 :             edgeLists.push_back(e->getFromNode()->getOutgoingEdges());
     351         2351 :             edgeLists.push_back(e->getFromNode()->getIncomingEdges());
     352         2351 :             edgeLists.push_back(e->getToNode()->getOutgoingEdges());
     353         2351 :             edgeLists.push_back(e->getToNode()->getIncomingEdges());
     354        11755 :             for (std::vector<EdgeVector>::const_iterator listIt = edgeLists.begin(); listIt != edgeLists.end(); ++listIt) {
     355        26000 :                 for (EdgeVector::const_iterator edgeIt = listIt->begin(); edgeIt != listIt->end(); ++edgeIt) {
     356        16596 :                     std::set<std::string>::iterator leftIt = edgesLeft.find((*edgeIt)->getID());
     357        16596 :                     if (leftIt != edgesLeft.end()) {
     358         2270 :                         queue.push_back(*edgeIt);
     359              :                         edgesLeft.erase(leftIt);
     360              :                     }
     361              :                 }
     362              :             }
     363         2351 :         }
     364           81 :         foundComponents++;
     365              :         std::vector<std::set<NBEdge*> >::iterator cIt;
     366          154 :         for (cIt = components.begin(); cIt != components.end(); ++cIt) {
     367           81 :             if (cIt->size() < component.size()) {
     368              :                 break;
     369              :             }
     370              :         }
     371           81 :         components.insert(cIt, component);
     372           81 :         if ((int)components.size() > numKeep) {
     373              :             bool recheck = false;
     374           56 :             if (hasPTStops) {
     375          134 :                 for (NBEdge* e : components.back()) {
     376          118 :                     SVCPermissions permissions = e->getPermissions();
     377          118 :                     if (isRailway(permissions) || isWaterway(permissions)) {
     378              :                         // recheck for connection to other components via access definitions
     379              :                         recheck = true;
     380              :                         break;
     381              :                     }
     382              :                 }
     383              :             }
     384           18 :             if (!recheck) {
     385           54 :                 toRemove.insert(components.back().begin(), components.back().end());
     386           54 :                 numRemoved++;
     387              :             } else {
     388              :                 std::vector<std::string> edgeIDs;
     389            4 :                 for (NBEdge* e : components.back()) {
     390            2 :                     edgeIDs.push_back(e->getID());
     391              :                 }
     392            2 :                 myRailComponents.push_back(edgeIDs);
     393            2 :             }
     394              :             components.pop_back();
     395              :         }
     396              :     }
     397           23 :     ec.removeRoundaboutEdges(toRemove);
     398          344 :     for (NBEdge* e : toRemove) {
     399              :         NBNode* const fromNode = e->getFromNode();
     400              :         NBNode* const toNode = e->getToNode();
     401          321 :         ec.erase(dc, e);
     402          321 :         if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
     403          153 :             erase(fromNode);
     404              :         }
     405          321 :         if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
     406          115 :             erase(toNode);
     407              :         }
     408              :     }
     409           23 :     if (foundComponents > 1) {
     410           28 :         WRITE_MESSAGEF(TL("Found % components and removed % (% edges)."), toString(foundComponents), toString(numRemoved), toString(toRemove.size()));
     411              :     }
     412           46 :     return (int)toRemove.size();
     413           46 : }
     414              : 
     415              : 
     416              : int
     417           23 : NBNodeCont::removeRailComponents(NBDistrictCont& dc, NBEdgeCont& ec, NBPTStopCont& sc) {
     418              :     std::set<std::string> stopEdges;
     419          120 :     for (const auto& item : sc.getStops()) {
     420           97 :         stopEdges.insert(item.second->getEdgeId());
     421              :     }
     422           23 :     int numRemoved = 0;
     423           23 :     int numRemovedEdges = 0;
     424           25 :     for (auto& component : myRailComponents) {
     425              :         bool keep = false;
     426            3 :         for (std::string edgeID : component) {
     427              :             if (stopEdges.count(edgeID) != 0) {
     428              :                 keep = true;
     429              :                 break;
     430              :             }
     431              :         }
     432              :         if (!keep) {
     433            1 :             numRemoved++;
     434            1 :             numRemovedEdges += (int)component.size();
     435            2 :             for (std::string edgeID : component) {
     436            1 :                 NBEdge* e = ec.retrieve(edgeID);
     437            1 :                 if (e != nullptr) {
     438              :                     NBNode* const fromNode = e->getFromNode();
     439              :                     NBNode* const toNode = e->getToNode();
     440            1 :                     ec.erase(dc, e);
     441            1 :                     if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
     442            1 :                         erase(fromNode);
     443              :                     }
     444            1 :                     if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
     445            1 :                         erase(toNode);
     446              :                     }
     447              :                 }
     448              :             }
     449              :         }
     450              :     }
     451           23 :     if (numRemoved > 0) {
     452            2 :         WRITE_MESSAGEF(TL("Removed % railway components (% edges)."), toString(numRemoved), toString(numRemovedEdges));
     453              :     }
     454           23 :     return numRemoved;
     455              : }
     456              : 
     457              : 
     458              : int
     459         1705 : NBNodeCont::removeUnwishedNodes(NBDistrictCont& dc, NBEdgeCont& ec,
     460              :                                 NBTrafficLightLogicCont& /*tlc*/, NBPTStopCont& sc,
     461              :                                 NBPTLineCont& lc,
     462              :                                 NBParkingCont& pc,
     463              :                                 bool removeGeometryNodes) {
     464              :     // load edges that shall not be modified
     465              :     std::set<std::string> edges2keep;
     466         1705 :     if (removeGeometryNodes) {
     467          100 :         const OptionsCont& oc = OptionsCont::getOptions();
     468          200 :         if (oc.isSet("geometry.remove.keep-edges.input-file")) {
     469            2 :             NBHelpers::loadEdgesFromFile(oc.getString("geometry.remove.keep-edges.input-file"), edges2keep);
     470              :         }
     471          200 :         if (oc.isSet("geometry.remove.keep-edges.explicit")) {
     472            2 :             const std::vector<std::string> edges = oc.getStringVector("geometry.remove.keep-edges.explicit");
     473              :             edges2keep.insert(edges.begin(), edges.end());
     474            1 :         }
     475              :         // no need to keep pt stop edges, they are remapped later
     476              :         // no need to keep all pt route edges. They are validated again before writing
     477          100 :         pc.addEdges2Keep(oc, edges2keep);
     478          199 :         if (oc.exists("geometry.remove.keep-ptstops") && oc.getBool("geometry.remove.keep-ptstops")) {
     479            1 :             sc.addEdges2Keep(oc, edges2keep);
     480              :         }
     481              :     }
     482              : 
     483              :     std::map<NBEdge*, std::set<NBTrafficLightDefinition*> > tlsLookup;
     484        97881 :     for (auto it = ec.begin(); it != ec.end(); it++) {
     485        96176 :         NBEdge* e = it->second;
     486              :         NBNode* to = e->getToNode();
     487        96176 :         if (to->isTLControlled()) {
     488         5940 :             tlsLookup[e] = to->getControllingTLS();
     489              :         }
     490              :     }
     491              : 
     492              :     std::vector<NBNode*> toRemove;
     493        60920 :     for (const auto& i : myNodes) {
     494        59215 :         NBNode* const current = i.second;
     495              :         bool remove = false;
     496              :         // check for completely empty nodes and check for nodes which are only geometry nodes and ask the node whether to join
     497        76442 :         if (current->getEdges().empty() || (removeGeometryNodes && mySplit.count(current) == 0 && current->checkIsRemovable())) {
     498              :             remove = true;
     499              :             // check whether any of the edges must be kept
     500        19387 :             for (NBEdge* const it_edge : current->getEdges()) {
     501        10865 :                 if (edges2keep.find(it_edge->getID()) != edges2keep.end()) {
     502              :                     remove = false;
     503              :                     break;
     504              :                 }
     505              :             }
     506              :         }
     507              :         // remove the node and join the geometries when wished
     508         8554 :         if (!remove) {
     509        50693 :             continue;
     510              :         }
     511        13938 :         for (const std::pair<NBEdge*, NBEdge*>& j : current->getEdgesToJoin()) {
     512         5416 :             NBEdge* const begin = j.first;
     513         5416 :             NBEdge* const continuation = j.second;
     514         5416 :             begin->append(continuation);
     515         5416 :             continuation->getToNode()->replaceIncoming(continuation, begin, 0);
     516              :             auto itTL = tlsLookup.find(continuation);
     517         5416 :             if (itTL != tlsLookup.end()) {
     518         1002 :                 for (NBTrafficLightDefinition* tls : itTL->second) {
     519          501 :                     tls->replaceRemoved(continuation, -1, begin, -1, true);
     520              :                 }
     521          501 :                 tlsLookup[begin] = itTL->second;
     522              :             }
     523         5416 :             sc.replaceEdge(continuation->getID(), { begin });
     524         5416 :             lc.replaceEdge(continuation->getID(), { begin });
     525         5416 :             ec.extract(dc, continuation, true);
     526         8522 :         }
     527         8522 :         toRemove.push_back(current);
     528              :     }
     529              :     // erase all
     530        10227 :     for (NBNode* n : toRemove) {
     531         8522 :         extract(n, true);
     532              :     }
     533         1705 :     return (int)toRemove.size();
     534         1705 : }
     535              : 
     536              : 
     537              : void
     538         1601 : NBNodeCont::avoidOverlap() {
     539        39400 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
     540        37799 :         (*i).second->avoidOverlap();
     541              :     }
     542         1601 : }
     543              : 
     544              : 
     545              : // ----------- (Helper) methods for joining nodes
     546              : void
     547          144 : NBNodeCont::generateNodeClusters(double maxDist, NodeClusters& into) const {
     548              :     std::set<NBNode*> visited;
     549        33288 :     for (const auto& i : myNodes) {
     550         5046 :         if (visited.count(i.second) > 0) {
     551        31002 :             continue;
     552              :         }
     553              :         std::vector<NodeAndDist> toProc;
     554        28098 :         toProc.emplace_back(i.second, 0.);
     555              :         NodeSet c;
     556        63839 :         while (!toProc.empty()) {
     557        35741 :             NBNode* const n = toProc.back().first;
     558        35741 :             const double dist = toProc.back().second;
     559              :             toProc.pop_back();
     560         2597 :             if (visited.count(n) > 0) {
     561         7321 :                 continue;
     562              :             }
     563              :             visited.insert(n);
     564              :             bool pureRail = true;
     565              :             bool railAndPeds = true;
     566        71301 :             for (NBEdge* e : n->getEdges()) {
     567        54062 :                 if ((e->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_PEDESTRIAN)) != 0) {
     568              :                     railAndPeds = false;
     569              :                     pureRail = false;
     570              :                     break;
     571              :                 }
     572        38157 :                 if ((e->getPermissions() & ~(SVC_RAIL_CLASSES)) != 0) {
     573              :                     pureRail = false;
     574              :                 }
     575              :             }
     576        33144 :             if (pureRail) {
     577              :                 // do not join pure rail nodes
     578         4724 :                 continue;
     579              :             }
     580              :             c.insert(n);
     581       123434 :             for (NBEdge* e : n->getEdges()) {
     582       190028 :                 NBNode* s = n->hasIncoming(e) ? e->getFromNode() : e->getToNode();
     583              :                 const double length = e->getLoadedLength();
     584              : #ifdef DEBUG_JOINJUNCTIONS
     585              :                 if (DEBUGCOND(s)) {
     586              :                     std::cout << "generateNodeClusters: consider s=" << s->getID()
     587              :                               << " clusterNode=" << n->getID() << " edge=" << e->getID() << " dist=" << dist << " length=" << length << " with cluster " << joinNamedToString(c, ' ') << "\n";
     588              :                 }
     589              : #endif
     590        95014 :                 if (railAndPeds && n->getType() != SumoXMLNodeType::RAIL_CROSSING) {
     591              :                     bool railAndPeds2 = true;
     592       105640 :                     for (NBEdge* e2 : n->getEdges()) {
     593        77399 :                         if ((e2->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_PEDESTRIAN)) != 0) {
     594              :                             railAndPeds2 = false;
     595              :                             break;
     596              :                         }
     597              :                     }
     598        28241 :                     if (railAndPeds2 && s->getType() != SumoXMLNodeType::RAIL_CROSSING) {
     599              :                         // do not join rail/ped nodes unless at a rail crossing
     600              :                         // (neither nodes nor the traffic lights)
     601        74274 :                         continue;
     602              :                     }
     603              :                 }
     604        67051 :                 const bool bothCrossing = n->getType() == SumoXMLNodeType::RAIL_CROSSING && s->getType() == SumoXMLNodeType::RAIL_CROSSING;
     605          658 :                 const bool joinPedCrossings = bothCrossing && e->getPermissions() == SVC_PEDESTRIAN;
     606        24813 :                 if ( // never join pedestrian stuff (unless at a rail crossing
     607        66879 :                     !joinPedCrossings && (
     608        66879 :                         e->getPermissions() == SVC_PEDESTRIAN
     609              :                         // only join edges for regular passenger traffic or edges that are extremely short
     610        60122 :                         || (length > 3 * POSITION_EPS
     611        54276 :                             && (e->getPermissions() & (SVC_PASSENGER | SVC_TRAM)) == 0
     612        18082 :                             && n->getPosition().distanceTo2D(s->getPosition()) > SUMO_const_laneWidth))) {
     613              : #ifdef DEBUG_JOINJUNCTIONS
     614              :                     if (DEBUGCOND(s)) {
     615              :                         std::cout << " ignored s=" << s->getID() << " pedestrian edge=" << e->getID() << " cd=" << n->getPosition().distanceTo2D(s->getPosition()) << "\n";
     616              :                     }
     617              : #endif
     618        24813 :                     continue;
     619              :                 }
     620              :                 // never join rail_crossings with other node types unless the crossing is only for tram
     621         1217 :                 if ((n->getType() == SumoXMLNodeType::RAIL_CROSSING && s->getType() != SumoXMLNodeType::RAIL_CROSSING)
     622        42884 :                         || (n->getType() != SumoXMLNodeType::RAIL_CROSSING && s->getType() == SumoXMLNodeType::RAIL_CROSSING)) {
     623              :                     const SVCPermissions railNoTram = (SVC_RAIL_CLASSES & ~SVC_TRAM);
     624              :                     bool foundRail = false;
     625          776 :                     NBNode* crossingNode = n->getType() == SumoXMLNodeType::RAIL_CROSSING ? n : s;
     626         2848 :                     for (NBEdge* e2 : crossingNode->getIncomingEdges()) {
     627         2088 :                         if ((e2->getPermissions() & railNoTram) != 0) {
     628              :                             foundRail = true;
     629              :                             break;
     630              :                         }
     631              :                     }
     632          776 :                     if (foundRail) {
     633           16 :                         continue;
     634              :                     }
     635              :                 }
     636              :                 // never join rail_crossings via a rail edge
     637        42222 :                 if (bothCrossing && (e->getPermissions() & ~SVC_RAIL_CLASSES) == 0) {
     638          418 :                     continue;
     639              :                 }
     640        41804 :                 if (visited.find(s) != visited.end()) {
     641        21064 :                     continue;
     642              :                 }
     643        20740 :                 if (length + dist < maxDist) {
     644              :                     // don't add long "boring" appendages but always join the whole rail crossing or tls
     645         7643 :                     const bool trueGeomLike = s->geometryLike();
     646         7643 :                     if (trueGeomLike || geometryLikeForClass(s, SVC_VULNERABLE | SVC_DELIVERY)) {
     647         9277 :                         const bool hasTLS = n->isTrafficLight() || s->isTrafficLight();
     648         4938 :                         const double fullLength = e->getGeometry().length2D();
     649         4938 :                         const double length2 = bothCrossing || hasTLS || trueGeomLike ? length : fullLength;
     650         4938 :                         toProc.emplace_back(s, dist + length2);
     651              :                     } else {
     652         2705 :                         toProc.emplace_back(s, 0.);
     653              :                     }
     654              :                 }
     655              :             }
     656              :         }
     657        28098 :         if (c.size() < 2) {
     658              :             continue;
     659              :         }
     660              : #ifdef DEBUG_JOINJUNCTIONS
     661              :         std::cout << " DEBUG: consider cluster " << joinNamedToString(c, ' ') << "\n";
     662              : #endif
     663         2142 :         into.push_back(c);
     664        28098 :     }
     665          144 : }
     666              : 
     667              : 
     668              : bool
     669         5513 : NBNodeCont::geometryLikeForClass(const NBNode* n, SVCPermissions ignored) {
     670              :     EdgeVector allowedIn;
     671              :     EdgeVector allowedOut;
     672        20245 :     for (NBEdge* e : n->getIncomingEdges()) {
     673        14732 :         if ((e->getPermissions() & ~ignored) != 0) {
     674        11100 :             allowedIn.push_back(e);
     675              :         }
     676              :     }
     677        20204 :     for (NBEdge* e : n->getOutgoingEdges()) {
     678        14691 :         if ((e->getPermissions() & ~ignored) != 0) {
     679        11073 :             allowedOut.push_back(e);
     680              :         }
     681              :     }
     682         5513 :     if (allowedIn.size() > 0 && allowedOut.size() > 0) {
     683              :         //std::cout << n->getID() << " geometryLikeForClass=" << n->geometryLike(allowedIn, allowedOut) << " in=" << toString(allowedIn) << " out=" << toString(allowedOut) << "\n";
     684         5097 :         return n->geometryLike(allowedIn, allowedOut);
     685              :     }
     686              :     return true;
     687         5513 : }
     688              : 
     689              : 
     690              : void
     691           16 : NBNodeCont::addJoinExclusion(const std::vector<std::string>& ids) {
     692          116 :     for (const std::string& nodeID : ids) {
     693              :         // error handling has to take place here since joinExclusions could be
     694              :         // loaded from multiple files / command line
     695              :         if (myJoined.count(nodeID) > 0) {
     696            0 :             WRITE_WARNINGF(TL("Ignoring join exclusion for junction '%' since it already occurred in a list of nodes to be joined."), nodeID);
     697              :         } else {
     698              :             myJoinExclusions.insert(nodeID);
     699              :         }
     700              :     }
     701           16 : }
     702              : 
     703              : 
     704              : std::string
     705          817 : NBNodeCont::createClusterId(const std::set<std::string>& cluster, const std::string& prefix) {
     706          817 :     int maxIds = OptionsCont::getOptions().getInt("max-join-ids");
     707          817 :     if (maxIds <= 0) {
     708            0 :         maxIds = (int)cluster.size();
     709              :     }
     710          817 :     if ((int)cluster.size() > maxIds) {
     711              :         auto clusterIt = cluster.begin();
     712              :         std::string result = prefix + *clusterIt;
     713          500 :         for (int i = 1; i < maxIds; i++) {
     714              :             ++clusterIt;
     715          750 :             result += "_" + *clusterIt;
     716              :         }
     717          250 :         return result + "_#" + toString((int)cluster.size() - maxIds) + "more";
     718              :     }
     719         1384 :     return prefix + joinToString(cluster, "_");
     720              : }
     721              : 
     722              : 
     723              : void
     724            9 : NBNodeCont::addCluster2Join(const std::set<std::string>& cluster, NBNode* node) {
     725              :     // error handling has to take place here since joins could be loaded from multiple files
     726              :     std::set<std::string> validCluster;
     727           89 :     for (std::string nodeID : cluster) {
     728              :         if (myJoinExclusions.count(nodeID) > 0) {
     729            0 :             WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' was already excluded from joining."), nodeID);
     730            0 :             return;
     731              :         } else if (myJoined.count(nodeID) > 0) {
     732            0 :             WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' already occurred in another join-cluster."), nodeID);
     733            0 :             return;
     734              :         } else {
     735           80 :             if (retrieve(nodeID) != nullptr) {
     736              :                 validCluster.insert(nodeID);
     737              :             } else {
     738            0 :                 WRITE_ERRORF(TL("Unknown junction '%' in join-cluster."), nodeID);
     739              :             }
     740              :         }
     741              :     }
     742            9 :     if (validCluster.size() > 1) {
     743            8 :         myJoined.insert(validCluster.begin(), validCluster.end());
     744           16 :         myClusters2Join.push_back(std::make_pair(validCluster, node));
     745              :     } else {
     746            3 :         WRITE_WARNINGF(TL("Ignoring join-cluster '%' because it has size '%'."), node->getID(), validCluster.size());
     747              :     }
     748              : }
     749              : 
     750              : 
     751              : int
     752         1704 : NBNodeCont::joinLoadedClusters(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc) {
     753              :     int numJoined = 0;
     754         1712 :     for (auto& item : myClusters2Join) {
     755              :         // verify loaded cluster
     756              :         NodeSet cluster;
     757           87 :         for (std::string nodeID : item.first) {
     758           79 :             NBNode* node = retrieve(nodeID);
     759           79 :             if (node == nullptr) {
     760            0 :                 WRITE_ERRORF(TL("unknown junction '%' while joining."), nodeID);
     761              :             } else {
     762              :                 cluster.insert(node);
     763              :             }
     764              :         }
     765            8 :         if (cluster.size() > 1) {
     766            8 :             joinNodeCluster(cluster, dc, ec, tlc, item.second);
     767            8 :             numJoined++;
     768            8 :             myJoinExclusions.insert(item.second->getID());
     769              :         }
     770              :     }
     771              :     myClusters2Join.clear(); // make save for recompute
     772         1704 :     return numJoined;
     773              : }
     774              : 
     775              : 
     776              : int
     777          104 : NBNodeCont::joinJunctions(double maxDist, NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, NBPTStopCont& sc) {
     778              : #ifdef DEBUG_JOINJUNCTIONS
     779              :     std::cout << "joinJunctions...\n";
     780              : #endif
     781              :     NodeClusters cands;
     782              :     NodeClusters clusters;
     783              :     std::map<const NBNode*, std::vector<NBNode*> > ptStopEnds;
     784              :     // check for stop edges within the cluster
     785         1046 :     for (const auto& stopIt : sc.getStops()) {
     786          942 :         NBEdge* edge = ec.retrieve(stopIt.second->getEdgeId());
     787          942 :         if (edge != nullptr) {
     788          942 :             ptStopEnds[edge->getFromNode()].push_back(edge->getToNode());
     789              :         }
     790              :     }
     791          104 :     generateNodeClusters(maxDist, cands);
     792         1587 :     for (NodeSet& cluster : cands) {
     793              : #ifdef DEBUG_JOINJUNCTIONS
     794              :         gDebugFlag1 = false;
     795              :         for (NBNode* n : cluster) {
     796              :             if (DEBUGCOND(n)) {
     797              :                 gDebugFlag1 = true;
     798              :             }
     799              :         }
     800              : #endif
     801              :         // remove join exclusions
     802         6565 :         for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
     803              :             NodeSet::iterator check = j;
     804              :             ++j;
     805         5082 :             if (myJoinExclusions.count((*check)->getID()) > 0) {
     806              :                 cluster.erase(check);
     807              :             }
     808              :         }
     809         1483 :         std::string origCluster = joinNamedToString(cluster, ',');
     810              :         // remove nodes that can be eliminated by geometry.remove
     811         1483 :         pruneClusterFringe(cluster, maxDist);
     812         1483 :         if (cluster.size() < 2) {
     813          713 :             continue;
     814              :         }
     815              :         // remove nodes that are part of a bypass lane (typically for turning right without waiting at a traffic light)
     816          770 :         pruneSlipLaneNodes(cluster, maxDist);
     817          770 :         if (cluster.size() < 2) {
     818            3 :             WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
     819            1 :             continue;
     820              :         }
     821              :         std::string reason;
     822              :         std::string origReason;
     823              :         // pruneLongEdges might remove too much, so we check first to have a fallback with the circles
     824          769 :         bool feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason);
     825          769 :         if (feasible && ((int)cluster.size() - pruneLongEdges(cluster, maxDist, true) < 2)) {
     826              :             origReason = "long edge";
     827              :             feasible = false;
     828              :         }
     829          764 :         if (!feasible) {
     830              : #ifdef DEBUG_JOINJUNCTIONS
     831              :             if (gDebugFlag1) {
     832              :                 std::cout << "   try to reduce to 4-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
     833              :             }
     834              : #endif
     835          126 :             if (reduceToCircle(cluster, 4, cluster, maxDist)) {
     836            1 :                 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason);
     837            1 :                 if (feasible) {
     838            3 :                     WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
     839              :                 }
     840              :             }
     841              :         }
     842           63 :         if (!feasible) {
     843              : #ifdef DEBUG_JOINJUNCTIONS
     844              :             if (gDebugFlag1) {
     845              :                 std::cout << "   try to reduce to 2-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
     846              :             }
     847              : #endif
     848          124 :             origCluster = joinNamedToString(cluster, ',');
     849          124 :             if (reduceToCircle(cluster, 2, cluster, maxDist)) {
     850           20 :                 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason);
     851           20 :                 if (feasible) {
     852           54 :                     WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
     853              :                 }
     854              :             }
     855              :         }
     856              :         // avoid removal of long edges (must have been added via an alternative path).
     857          769 :         const int numPruned = pruneLongEdges(cluster, maxDist);
     858          769 :         if (cluster.size() < 2) {
     859            0 :             WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
     860            0 :             continue;
     861              :         }
     862              :         // after pruning long edges we have to recheck
     863          769 :         if (numPruned > 0) {
     864            5 :             pruneClusterFringe(cluster, maxDist);
     865            5 :             if (cluster.size() < 2) {
     866            0 :                 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
     867            0 :                 continue;
     868              :             }
     869            5 :             pruneSlipLaneNodes(cluster, maxDist);
     870            5 :             if (cluster.size() < 2) {
     871            0 :                 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
     872            0 :                 continue;
     873              :             }
     874              :         }
     875          769 :         origCluster = joinNamedToString(cluster, ',');
     876          769 :         feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason);
     877          769 :         if (!feasible) {
     878          135 :             WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, origReason);
     879           45 :             continue;
     880              :         }
     881              :         // compute all connected components of this cluster
     882              :         // (may be more than 1 if intermediate nodes were removed)
     883              :         NodeClusters components;
     884         3148 :         for (NBNode* current : cluster) {
     885              :             // merge all connected components into newComp
     886              :             NodeSet newComp;
     887              :             //std::cout << "checking connectivity for " << current->getID() << "\n";
     888              :             newComp.insert(current);
     889         4890 :             for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end();) {
     890              :                 NodeClusters::iterator check = it_comp;
     891              :                 //std::cout << "   connected with " << toString(*check) << "?\n";
     892              :                 bool connected = false;
     893         4703 :                 for (NBNode* k : *check) {
     894         3937 :                     if (current->getConnectionTo(k) != nullptr || k->getConnectionTo(current) != nullptr) {
     895              :                         //std::cout << "joining with connected component " << toString(*check) << "\n";
     896         1700 :                         newComp.insert((*check).begin(), (*check).end());
     897              :                         it_comp = components.erase(check);
     898              :                         connected = true;
     899              :                         break;
     900              :                     }
     901              :                 }
     902              :                 if (!connected) {
     903              :                     it_comp++;
     904              :                 }
     905              :             }
     906              :             //std::cout << "adding new component " << toString(newComp) << "\n";
     907         2424 :             components.push_back(newComp);
     908              :         }
     909         1448 :         for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end(); ++it_comp) {
     910          724 :             if ((*it_comp).size() > 1) {
     911              :                 //std::cout << "adding cluster " << toString(*it_comp) << "\n";
     912          724 :                 clusters.push_back(*it_comp);
     913              :             }
     914              :         }
     915              : #ifdef DEBUG_JOINJUNCTIONS
     916              :         gDebugFlag1 = false;
     917              : #endif
     918          724 :     }
     919          104 :     joinNodeClusters(clusters, dc, ec, tlc);
     920          208 :     return (int)clusters.size();
     921          104 : }
     922              : 
     923              : 
     924              : int
     925           11 : NBNodeCont::joinSameJunctions(NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc) {
     926              : #ifdef DEBUG_JOINJUNCTIONS
     927              :     std::cout << "joinSameJunctions...\n";
     928              : #endif
     929              :     std::map<std::string, NodeSet> positions;
     930          950 :     for (auto& item : myNodes) {
     931          939 :         Position pos = item.second->getPosition();
     932          939 :         std::string rounded = (OutputDevice::realString(pos.x(), gPrecision)
     933         2817 :                                + "_" + OutputDevice::realString(pos.y(), gPrecision)
     934         2817 :                                + "_" + OutputDevice::realString(pos.z(), gPrecision));
     935          939 :         positions[rounded].insert(item.second);
     936              :     }
     937              :     NodeClusters clusters;
     938          931 :     for (auto& item : positions) {
     939          920 :         if (item.second.size() > 1) {
     940           57 :             for (NBNode* n : item.second) {
     941              :                 if (myJoinExclusions.count(n->getID()) > 0) {
     942              :                     item.second.erase(n);
     943              :                 }
     944              :             }
     945           19 :             if (item.second.size() > 1) {
     946           19 :                 clusters.push_back(item.second);
     947              :             }
     948              :         }
     949              :     }
     950           11 :     joinNodeClusters(clusters, dc, ec, tlc, true);
     951           11 :     return (int)clusters.size();
     952           11 : }
     953              : 
     954              : void
     955         1531 : NBNodeCont::pruneClusterFringe(NodeSet& cluster, double maxDist) const {
     956              : #ifdef DEBUG_JOINJUNCTIONS
     957              :     if (gDebugFlag1) {
     958              :         std::cout << "pruning cluster=" << joinNamedToString(cluster, ' ') << "\n";
     959              :     }
     960              : #endif
     961              :     // iteratively remove the fringe
     962              :     bool pruneFringe = true;
     963              :     bool pruneNoisyFringe = false;
     964              :     // collect nodes that shall be joined due to distance but are not connected
     965              :     // to the cluster for passenger traffic
     966         5959 :     while (pruneFringe) {
     967              :         pruneFringe = false;
     968        14641 :         for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
     969              :             NodeSet::iterator check = j;
     970        10213 :             NBNode* n = *check;
     971              :             ++j;
     972              : 
     973              :             // compute clusterDist for node (length of shortest edge which connects this node to the cluster)
     974              :             double clusterDist = std::numeric_limits<double>::max();
     975              :             bool touchingCluster = false;
     976        32441 :             for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
     977        22228 :                 NBNode* neighbor = (*it_edge)->getToNode();
     978              :                 if (cluster.count(neighbor) != 0) {
     979              :                     clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
     980        10671 :                     touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
     981              :                 }
     982              :             }
     983        32555 :             for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
     984        22342 :                 NBNode* neighbor = (*it_edge)->getFromNode();
     985              :                 if (cluster.count(neighbor) != 0) {
     986              :                     clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
     987        10576 :                     touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
     988              :                 }
     989              :             }
     990              :             // remove geometry-like nodes at fringe of the cluster
     991              :             // (they have 1 neighbor in the cluster and at most 1 neighbor outside the cluster)
     992              :             std::set<NBNode*> outsideNeighbors;
     993              :             std::set<NBNode*> clusterNeighbors;
     994              :             const double pedestrianFringeThreshold = 0.3;
     995        54783 :             for (NBEdge* e : n->getEdges()) {
     996        66798 :                 NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
     997              :                 if (cluster.count(neighbor) == 0) {
     998        23323 :                     if ((e->getPermissions() & SVC_PASSENGER) != 0
     999         9491 :                             || isRailway(e->getPermissions()) // join railway crossings
    1000         7240 :                             || (clusterDist <= pedestrianFringeThreshold
    1001         4975 :                                 && (!pruneNoisyFringe
    1002         1914 :                                     || isForVulnerableModes(e->getPermissions())
    1003              :                                     // permit joining small opposite merges
    1004          772 :                                     || getDiameter(cluster) < maxDist
    1005          160 :                                     || cluster.size() == 2))
    1006        25682 :                             || touchingCluster) {
    1007              :                         outsideNeighbors.insert(neighbor);
    1008              :                     }
    1009              :                 } else {
    1010              :                     clusterNeighbors.insert(neighbor);
    1011              :                 }
    1012              :             }
    1013              : #ifdef DEBUG_JOINJUNCTIONS
    1014              :             if (gDebugFlag1) std::cout << "  check n=" << n->getID()
    1015              :                                            << " clusterDist=" << clusterDist
    1016              :                                            << " cd<th=" << (clusterDist <= pedestrianFringeThreshold)
    1017              :                                            << " touching=" << touchingCluster
    1018              :                                            << " out=" << joinNamedToString(outsideNeighbors, ',')
    1019              :                                            << " in=" << joinNamedToString(clusterNeighbors, ',')
    1020              :                                            << " dia=" << getDiameter(cluster)
    1021              :                                            << "\n";
    1022              : #endif
    1023              :             if (clusterNeighbors.size() == 0
    1024        10213 :                     || (outsideNeighbors.size() <= 1
    1025         5258 :                         && clusterNeighbors.size() == 1
    1026         2133 :                         && !n->isTLControlled())) {
    1027              :                 cluster.erase(check);
    1028              :                 pruneFringe = true; // other nodes could belong to the fringe now
    1029              : #ifdef DEBUG_JOINJUNCTIONS
    1030              :                 if (gDebugFlag1) {
    1031              :                     std::cout << "  pruned n=" << n->getID() << "\n";
    1032              :                 }
    1033              : #endif
    1034              :             }
    1035              :         }
    1036         4428 :         if (!pruneFringe && !pruneNoisyFringe) {
    1037              :             // run once more and prune more things (with a look at cluster size)
    1038              :             pruneFringe = true;
    1039              :             pruneNoisyFringe = true;
    1040              : 
    1041              :         }
    1042              :     }
    1043         1531 : }
    1044              : 
    1045              : double
    1046          772 : NBNodeCont::getDiameter(const NodeSet& cluster) {
    1047              :     double result = 0;
    1048         2835 :     for (const NBNode* n1 : cluster) {
    1049        10532 :         for (const NBNode* n2 : cluster) {
    1050              :             result = MAX2(result, n1->getPosition().distanceTo2D(n2->getPosition()));
    1051              :         }
    1052              :     }
    1053          772 :     return result;
    1054              : }
    1055              : 
    1056              : int
    1057         1480 : NBNodeCont::pruneLongEdges(NodeSet& cluster, double maxDist, const bool dryRun) {
    1058              :     std::set<NBNode*> toRemove;
    1059              :     int maxPassengerLanes = 0;
    1060         6477 :     for (NBNode* n : cluster) {
    1061        28219 :         for (NBEdge* edge : n->getEdges()) {
    1062        23222 :             maxPassengerLanes = MAX2(maxPassengerLanes, edge->getNumLanesThatAllow(SVC_PASSENGER));
    1063              :         }
    1064              :     }
    1065         6477 :     for (NBNode* n : cluster) {
    1066        16583 :         for (NBEdge* edge : n->getOutgoingEdges()) {
    1067              :             // we must track the edge length across geometry like nodes
    1068              :             // Also, intersections that are geometry-like
    1069              :             // from the perspective of passenger traffic should be tracked across
    1070              :             std::vector<NBNode*> passed;
    1071              :             double length = 0;
    1072              :             NBEdge* cur = edge;
    1073        11586 :             NBNode* to = edge->getToNode();
    1074        11586 :             while (cluster.count(to) != 0) {
    1075         6688 :                 length += cur->getLoadedLength();
    1076         6688 :                 bool goStraight = (std::find(passed.begin(), passed.end(), to) == passed.end()
    1077         6688 :                                    && (edge->getPermissions() & SVC_PASSENGER) != 0
    1078        17052 :                                    && to->geometryLike(
    1079        11870 :                                        NBEdge::filterByPermissions(to->getIncomingEdges(), SVC_PASSENGER),
    1080        11870 :                                        NBEdge::filterByPermissions(to->getOutgoingEdges(), SVC_PASSENGER)));
    1081         6688 :                 passed.push_back(to);
    1082         6688 :                 if (goStraight) {
    1083         2660 :                     cur = cur->getStraightContinuation(SVC_PASSENGER);
    1084         2660 :                     if (cur != nullptr) {
    1085         2638 :                         to = cur->getToNode();
    1086              :                     } else {
    1087              :                         break;
    1088              :                     }
    1089              :                 } else {
    1090              :                     break;
    1091              :                 }
    1092              :             }
    1093              :             // allow higher threshold at larger junctions
    1094        11586 :             double longThreshold = maxDist + SUMO_const_laneWidth * MAX2(0, maxPassengerLanes - 1);
    1095              : #ifdef DEBUG_JOINJUNCTIONS
    1096              :             if (gDebugFlag1) {
    1097              :                 std::cout << "check edge length " << edge->getID() << " (" << length << ", passed=" << passed.size() << ", max=" << longThreshold << ")\n";
    1098              :             }
    1099              : #endif
    1100        11586 :             if (length > longThreshold) {
    1101              :                 // we found an edge that should not be removed. Maybe we can
    1102              :                 // still keep the start or end in the cluster
    1103              :                 // (keep the start if the end can be removed and vice versa)
    1104           35 :                 const bool keepStart = getClusterNeighbors(passed.back(), longThreshold, cluster).size() == 1;
    1105           35 :                 const bool keepEnd = !keepStart && getClusterNeighbors(n, longThreshold, cluster).size() == 1;
    1106              : #ifdef DEBUG_JOINJUNCTIONS
    1107              :                 if (gDebugFlag1) {
    1108              :                     std::cout << "node=" << n->getID() << " long edge " << edge->getID() << " (" << length << ", passed=" << toString(passed) << ", max=" << longThreshold << ") keepStart=" << keepStart << " keepEnd=" << keepEnd << "\n";
    1109              :                 }
    1110              : #endif
    1111           35 :                 if (!keepStart) {
    1112              :                     toRemove.insert(n);
    1113              :                 }
    1114              :                 toRemove.insert(passed.begin(), passed.end() - 1);
    1115           35 :                 if (!keepEnd) {
    1116              :                     toRemove.insert(passed.back());
    1117              :                 }
    1118              : 
    1119              :             }
    1120        11586 :         }
    1121              :     }
    1122         1480 :     if (!dryRun) {
    1123          780 :         for (std::set<NBNode*>::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
    1124              :             cluster.erase(*j);
    1125              :         }
    1126              :     }
    1127         1480 :     return (int)toRemove.size();
    1128              : }
    1129              : 
    1130              : 
    1131              : NodeSet
    1132           49 : NBNodeCont::getClusterNeighbors(const NBNode* n, double longThreshold, NodeSet& cluster) {
    1133              :     NodeSet result;
    1134          323 :     for (NBEdge* e : n->getEdges()) {
    1135          274 :         if (e->getLength() > longThreshold) {
    1136           94 :             continue;
    1137              :         }
    1138          273 :         NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
    1139              :         if (cluster.count(neighbor) != 0) {
    1140              :             result.insert(neighbor);
    1141              :         }
    1142              :     }
    1143           49 :     return result;
    1144              : }
    1145              : 
    1146              : 
    1147              : void
    1148          775 : NBNodeCont::pruneSlipLaneNodes(NodeSet& cluster, double maxDist) const {
    1149              : #ifdef DEBUG_JOINJUNCTIONS
    1150              :     if (gDebugFlag1) {
    1151              :         std::cout << "pruning slip-lanes at cluster=" << joinNamedToString(cluster, ' ') << "\n";
    1152              :     }
    1153              : #endif
    1154              :     // fringe has already been removed
    1155          775 :     if (cluster.size() <= 2) {
    1156          460 :         return;
    1157              :     }
    1158              :     NodeSet toRemove;
    1159         2130 :     for (NBNode* n : cluster) {
    1160              :         EdgeVector outgoing;
    1161              :         double inAngle;
    1162              :         // find slip lanes where the start is part of the cluster
    1163         1815 :         if (maybeSlipLaneStart(n, outgoing, inAngle)) {
    1164              :             // potential slip lane start but we don't know which of the outgoing edges it is
    1165              : #ifdef DEBUG_JOINJUNCTIONS
    1166              :             if (gDebugFlag1) {
    1167              :                 std::cout << "   candidate slip-lane start=" << n->getID() << " outgoing=" << toString(outgoing) << "\n";
    1168              :             }
    1169              : #endif
    1170          207 :             for (NBEdge* contEdge : outgoing) {
    1171          138 :                 if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
    1172            0 :                     continue;
    1173              :                 }
    1174          138 :                 double slipLength = contEdge->getLength();
    1175          138 :                 NBNode* cont = contEdge->getToNode();
    1176              :                 NodeSet cands;
    1177              :                 cands.insert(n);
    1178          274 :                 while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
    1179              :                     if (cands.count(cont) != 0) {
    1180              :                         break; // circle, should not happen
    1181              :                     }
    1182              :                     cands.insert(cont);
    1183              : #ifdef DEBUG_JOINJUNCTIONS
    1184              :                     if (gDebugFlag1) {
    1185              :                         std::cout << "   candidate slip-lane cont=" << cont->getID() << "\n";
    1186              :                     }
    1187              : #endif
    1188          136 :                     NBEdge* next = cont->getOutgoingEdges().front();
    1189          136 :                     slipLength += next->getLength();
    1190          136 :                     cont = next->getToNode();
    1191              :                 }
    1192              : #ifdef DEBUG_JOINJUNCTIONS
    1193              :                 if (gDebugFlag1) {
    1194              :                     std::cout << "   candidate slip-lane end=" << cont->getID() << " slipLength=" << slipLength << "\n";
    1195              :                 }
    1196              : #endif
    1197          175 :                 if (cont->getIncomingEdges().size() >= 2 && cont->getOutgoingEdges().size() == 1 &&
    1198              :                         // slip lanes are for turning so there needs to be a sufficient angle
    1199           37 :                         abs(NBHelpers::relAngle(inAngle, cont->getOutgoingEdges().front()->getAngleAtNode(cont))) > 45) {
    1200              :                     // check whether the other continuation at n is also connected to the sliplane end
    1201           30 :                     const NBEdge* const otherEdge = (contEdge == outgoing.front() ? outgoing.back() : outgoing.front());
    1202              :                     NodeSet visited;
    1203              :                     visited.insert(n);
    1204              :                     std::vector<NodeAndDist> toProc;
    1205           30 :                     toProc.push_back(std::make_pair(otherEdge->getToNode(), otherEdge->getLength()));
    1206              :                     bool found = false;
    1207          135 :                     while (!toProc.empty()) {
    1208          128 :                         NodeAndDist nodeAndDist = toProc.back();
    1209          128 :                         NBNode* cont2 = nodeAndDist.first;
    1210              :                         double dist = nodeAndDist.second;
    1211              : #ifdef DEBUG_JOINJUNCTIONS
    1212              :                         if (gDebugFlag1) {
    1213              :                             std::cout << "   search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
    1214              :                         }
    1215              : #endif
    1216              :                         toProc.pop_back();
    1217          128 :                         if (visited.find(cont2) != visited.end()) {
    1218           10 :                             continue;
    1219              :                         }
    1220              :                         visited.insert(cont2);
    1221          118 :                         if (cont2 == cont) {
    1222              :                             found = true;
    1223           23 :                             break;
    1224              :                         }
    1225          228 :                         for (NBEdge* e : cont2->getOutgoingEdges()) {
    1226          133 :                             const double dist2 = dist + e->getLength();
    1227          133 :                             if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
    1228          117 :                                 toProc.push_back(std::make_pair(e->getToNode(), dist2));
    1229              :                             }
    1230              :                         }
    1231              :                     }
    1232              :                     if (found) {
    1233              :                         // found slip lane
    1234              :                         cands.insert(cont);
    1235           23 :                         toRemove.insert(cands.begin(), cands.end());
    1236              : #ifdef DEBUG_JOINJUNCTIONS
    1237              :                         if (gDebugFlag1) {
    1238              :                             std::cout << "   found slip-lane with nodes=" << joinNamedToString(cands, ' ') << "\n";
    1239              :                         }
    1240              : #endif
    1241              :                     }
    1242           30 :                 }
    1243              :             }
    1244              :         }
    1245              : 
    1246              :         EdgeVector incoming;
    1247              :         double outAngle;
    1248              :         // find slip lanes where the end is part of the cluster
    1249         1815 :         if (maybeSlipLaneEnd(n, incoming, outAngle)) {
    1250              :             // potential slip lane end but we don't know which of the incoming edges it is
    1251              : #ifdef DEBUG_JOINJUNCTIONS
    1252              :             if (gDebugFlag1) {
    1253              :                 std::cout << "   candidate slip-lane end=" << n->getID() << " incoming=" << toString(incoming) << "\n";
    1254              :             }
    1255              : #endif
    1256          264 :             for (NBEdge* contEdge : incoming) {
    1257          176 :                 if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
    1258            0 :                     continue;
    1259              :                 }
    1260          176 :                 double slipLength = contEdge->getLength();
    1261          176 :                 NBNode* cont = contEdge->getFromNode();
    1262              :                 NodeSet cands;
    1263              :                 cands.insert(n);
    1264          347 :                 while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
    1265              :                     if (cands.count(cont) != 0) {
    1266              :                         break; // circle, should not happen
    1267              :                     }
    1268              :                     cands.insert(cont);
    1269              : #ifdef DEBUG_JOINJUNCTIONS
    1270              :                     if (gDebugFlag1) {
    1271              :                         std::cout << "   candidate slip-lane cont=" << cont->getID() << "\n";
    1272              :                     }
    1273              : #endif
    1274          171 :                     NBEdge* next = cont->getIncomingEdges().front();
    1275          171 :                     slipLength += next->getLength();
    1276          171 :                     cont = next->getFromNode();
    1277              :                 }
    1278              : #ifdef DEBUG_JOINJUNCTIONS
    1279              :                 if (gDebugFlag1) {
    1280              :                     std::cout << "   candidate slip-lane start=" << cont->getID() << " slipLength=" << slipLength << "\n";
    1281              :                 }
    1282              : #endif
    1283          227 :                 if (cont->getOutgoingEdges().size() >= 2 && cont->getIncomingEdges().size() == 1 &&
    1284              :                         // slip lanes are for turning so there needs to be a sufficient angle
    1285           51 :                         abs(NBHelpers::relAngle(outAngle, cont->getIncomingEdges().front()->getAngleAtNode(cont))) > 45) {
    1286              :                     // check whether the other continuation at n is also connected to the sliplane end
    1287           38 :                     const NBEdge* const otherEdge = (contEdge == incoming.front() ? incoming.back() : incoming.front());
    1288              :                     NodeSet visited;
    1289              :                     visited.insert(n);
    1290              :                     std::vector<NodeAndDist> toProc;
    1291           38 :                     toProc.push_back(std::make_pair(otherEdge->getFromNode(), otherEdge->getLength()));
    1292              :                     bool found = false;
    1293          377 :                     while (!toProc.empty()) {
    1294          362 :                         NodeAndDist nodeAndDist = toProc.back();
    1295          362 :                         NBNode* cont2 = nodeAndDist.first;
    1296              :                         double dist = nodeAndDist.second;
    1297              : #ifdef DEBUG_JOINJUNCTIONS
    1298              :                         if (gDebugFlag1) {
    1299              :                             std::cout << "   search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
    1300              :                         }
    1301              : #endif
    1302              :                         toProc.pop_back();
    1303          362 :                         if (visited.find(cont2) != visited.end()) {
    1304           61 :                             continue;
    1305              :                         }
    1306              :                         visited.insert(cont2);
    1307          301 :                         if (cont2 == cont) {
    1308              :                             found = true;
    1309           23 :                             break;
    1310              :                         }
    1311          708 :                         for (NBEdge* e : cont2->getIncomingEdges()) {
    1312          430 :                             const double dist2 = dist + e->getLength();
    1313          430 :                             if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
    1314          343 :                                 toProc.push_back(std::make_pair(e->getFromNode(), dist2));
    1315              :                             }
    1316              :                         }
    1317              :                     }
    1318              :                     if (found) {
    1319              :                         // found slip lane
    1320              :                         cands.insert(cont);
    1321           23 :                         toRemove.insert(cands.begin(), cands.end());
    1322              : #ifdef DEBUG_JOINJUNCTIONS
    1323              :                         if (gDebugFlag1) {
    1324              :                             std::cout << "   found slip-lane start with nodes=" << joinNamedToString(cands, ' ') << "\n";
    1325              :                         }
    1326              : #endif
    1327              :                     }
    1328           38 :                 }
    1329              :             }
    1330              :         }
    1331              : 
    1332              : 
    1333              : 
    1334         1815 :     }
    1335              :     int numRemoved = 0;
    1336          409 :     for (NBNode* n : toRemove) {
    1337           94 :         numRemoved += (int)cluster.erase(n);
    1338              :     }
    1339          315 :     if (numRemoved > 0) {
    1340              : #ifdef DEBUG_JOINJUNCTIONS
    1341              :         if (gDebugFlag1) {
    1342              :             std::cout << "   removed " << numRemoved << " nodes from cluster: " << joinNamedToString(toRemove, ' ') << "\n";
    1343              :         }
    1344              : #endif
    1345           19 :         pruneClusterFringe(cluster, maxDist);
    1346              :     }
    1347              : }
    1348              : 
    1349              : 
    1350              : bool
    1351          621 : NBNodeCont::isSlipLaneContinuation(const NBNode* cont) {
    1352          928 :     return cont->getPassengerEdges(true).size() == 1 && cont->getPassengerEdges(false).size() == 1;
    1353              : }
    1354              : 
    1355              : 
    1356              : bool
    1357         1815 : NBNodeCont::maybeSlipLaneStart(const NBNode* n, EdgeVector& outgoing, double& inAngle) const {
    1358         1815 :     EdgeVector inPE = n->getPassengerEdges(true);
    1359         1815 :     EdgeVector outPE = n->getPassengerEdges(false);
    1360         1815 :     if (inPE.size() == 1 && outPE.size() == 2) {
    1361           63 :         outgoing.insert(outgoing.begin(), outPE.begin(), outPE.end());
    1362           63 :         inAngle = inPE.front()->getAngleAtNode(n);
    1363           63 :         return true;
    1364         1752 :     } else if (inPE.size() >= 2 && outPE.size() == 3) {
    1365              :         // check if the incoming edges are going in opposite directions and then
    1366              :         // use the incoming edge that has 2 almost-straight outgoing edges
    1367          173 :         const double inRelAngle = fabs(NBHelpers::relAngle(inPE.front()->getAngleAtNode(n), inPE.back()->getAngleAtNode(n)));
    1368              :         //std::cout << "n=" << n->getID() << " inRelAngle=" << inRelAngle << "\n";
    1369          173 :         if (inRelAngle < 135) {
    1370              :             return false; // not opposite incoming
    1371              :         }
    1372          323 :         for (NBEdge* in : inPE) {
    1373              :             EdgeVector straight;
    1374              :             int numReverse = 0;
    1375          972 :             for (NBEdge* out : outPE) {
    1376          729 :                 const double outRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
    1377          729 :                 if (outRelAngle <= 45) {
    1378          227 :                     straight.push_back(out);
    1379          502 :                 } else if (outRelAngle >= 135) {
    1380          199 :                     numReverse++;
    1381              :                 }
    1382              :             }
    1383          243 :             if (straight.size() == 2 && numReverse == 1) {
    1384            6 :                 outgoing.insert(outgoing.begin(), straight.begin(), straight.end());
    1385            6 :                 inAngle = in->getAngleAtNode(n);
    1386              :                 return true;
    1387              :             }
    1388          243 :         }
    1389              :     }
    1390              :     return false;
    1391         1815 : }
    1392              : 
    1393              : 
    1394              : bool
    1395         1815 : NBNodeCont::maybeSlipLaneEnd(const NBNode* n, EdgeVector& incoming, double& outAngle) const {
    1396         1815 :     EdgeVector inPE = n->getPassengerEdges(true);
    1397         1815 :     EdgeVector outPE = n->getPassengerEdges(false);
    1398         1815 :     if (inPE.size() == 2 && outPE.size() == 1) {
    1399           83 :         incoming.insert(incoming.begin(), inPE.begin(), inPE.end());
    1400           83 :         outAngle = outPE.front()->getAngleAtNode(n);
    1401           83 :         return true;
    1402         1732 :     } else if (inPE.size() == 3 && outPE.size() >= 2) {
    1403              :         // check if the outgoing edges are going in opposite directions and then
    1404              :         // use the outgoing edge that has 2 almost-straight incoming edges
    1405          180 :         const double outRelAngle = fabs(NBHelpers::relAngle(outPE.front()->getAngleAtNode(n), outPE.back()->getAngleAtNode(n)));
    1406              :         //std::cout << "n=" << n->getID() << " outRelAngle=" << outRelAngle << "\n";
    1407          180 :         if (outRelAngle < 135) {
    1408              :             return false; // not opposite outgoing
    1409              :         }
    1410          402 :         for (NBEdge* out : outPE) {
    1411              :             EdgeVector straight;
    1412              :             int numReverse = 0;
    1413         1204 :             for (NBEdge* in : inPE) {
    1414          903 :                 const double inRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
    1415          903 :                 if (inRelAngle <= 45) {
    1416          283 :                     straight.push_back(in);
    1417          620 :                 } else if (inRelAngle >= 135) {
    1418          241 :                     numReverse++;
    1419              :                 }
    1420              :             }
    1421          301 :             if (straight.size() == 2 && numReverse == 1) {
    1422            5 :                 incoming.insert(incoming.begin(), straight.begin(), straight.end());
    1423            5 :                 outAngle = out->getAngleAtNode(n);
    1424              :                 return true;
    1425              :             }
    1426          301 :         }
    1427              :     }
    1428              :     return false;
    1429         1815 : }
    1430              : 
    1431              : bool
    1432         1559 : NBNodeCont::feasibleCluster(const NodeSet& cluster, const std::map<const NBNode*, std::vector<NBNode*> >& ptStopEnds,
    1433              :                             double maxDist, std::string& reason) const {
    1434              :     // check for clusters which are to complex and probably won't work very well
    1435              :     // we count the incoming edges of the final junction
    1436              :     std::map<NBEdge*, double, ComparatorIdLess> finalIncomingAngles;
    1437              :     std::map<NBEdge*, double, ComparatorIdLess> finalOutgoingAngles;
    1438         6777 :     for (NBNode* n : cluster) {
    1439        17357 :         for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
    1440        12139 :             NBEdge* edge = *it_edge;
    1441        17924 :             if (cluster.count(edge->getFromNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
    1442              :                 // incoming edge, does not originate in the cluster
    1443         3363 :                 finalIncomingAngles[edge] = edge->getAngleAtNode(edge->getToNode());
    1444              :             }
    1445              :         }
    1446        17286 :         for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
    1447        12068 :             NBEdge* edge = *it_edge;
    1448        17853 :             if (cluster.count(edge->getToNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
    1449              :                 // outgoing edge, does not end in the cluster
    1450         3372 :                 finalOutgoingAngles[edge] = edge->getAngleAtNode(edge->getFromNode());
    1451              :             }
    1452              :         }
    1453              : 
    1454              :     }
    1455              : #ifdef DEBUG_JOINJUNCTIONS
    1456              :     for (NBNode* n : cluster) {
    1457              :         if (DEBUGCOND(n)) {
    1458              :             std::cout << "feasibleCluster c=" << joinNamedToString(cluster, ',')
    1459              :                       << "\n inAngles=" << joinNamedToString(finalIncomingAngles, ' ', ':')
    1460              :                       << "\n outAngles=" << joinNamedToString(finalOutgoingAngles, ' ', ':')
    1461              :                       << "\n";
    1462              :         }
    1463              :     }
    1464              : #endif
    1465         1559 :     if (finalIncomingAngles.size() > 5) {
    1466            6 :         reason = toString(finalIncomingAngles.size()) + " incoming edges";
    1467            3 :         return false;
    1468              :     }
    1469              :     // check for incoming parallel edges
    1470              :     const double PARALLEL_THRESHOLD_SAME_NODE = 10;
    1471              :     const double PARALLEL_THRESHOLD_DIFF_NODE = 30;
    1472              :     bool foundParallel = false;
    1473         4800 :     for (auto j = finalIncomingAngles.begin(); j != finalIncomingAngles.end() && !foundParallel; ++j) {
    1474              :         auto k = j;
    1475         6527 :         for (++k; k != finalIncomingAngles.end() && !foundParallel; ++k) {
    1476         3283 :             const double angleDiff = fabs(j->second - k->second);
    1477         3283 :             if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
    1478              :                 // for edge targeting the same node, permit a narrower angle
    1479           35 :                 const double edgeDist = j->first->getLaneShape(0).back().distanceTo2D(k->first->getLaneShape(0).back());
    1480              : #ifdef DEBUG_JOINJUNCTIONS
    1481              :                 if (DEBUGCOND(j->first->getToNode())) {
    1482              :                     std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
    1483              :                 }
    1484              : #endif
    1485           35 :                 if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
    1486           18 :                             (j->first->getToNode() == k->first->getToNode()
    1487           12 :                              || (edgeDist  < maxDist)))) {
    1488            8 :                     continue;
    1489              :                 }
    1490           81 :                 reason = "parallel incoming " + j->first->getID() + "," + k->first->getID();
    1491              :                 return false;
    1492              :             }
    1493              :         }
    1494              :     }
    1495              :     // check for outgoing parallel edges
    1496         4729 :     for (auto j = finalOutgoingAngles.begin(); j != finalOutgoingAngles.end() && !foundParallel; ++j) {
    1497              :         auto k = j;
    1498         6426 :         for (++k; k != finalOutgoingAngles.end() && !foundParallel; ++k) {
    1499         3226 :             const double angleDiff = fabs(j->second - k->second);
    1500         3226 :             if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
    1501              :                 // for edge leaving the same node, permit a narrower angle
    1502           21 :                 const double edgeDist = j->first->getLaneShape(0).front().distanceTo2D(k->first->getLaneShape(0).front());
    1503              : #ifdef DEBUG_JOINJUNCTIONS
    1504              :                 if (DEBUGCOND(j->first->getFromNode())) {
    1505              :                     std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
    1506              :                 }
    1507              : #endif
    1508           21 :                 if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
    1509           16 :                             (j->first->getFromNode() == k->first->getFromNode()
    1510           12 :                              || (edgeDist < maxDist)))) {
    1511            4 :                     continue;
    1512              :                 }
    1513           51 :                 reason = "parallel outgoing " + j->first->getID() + "," + k->first->getID();
    1514              :                 return false;
    1515              :             }
    1516              :         }
    1517              :     }
    1518              :     // check for stop edges and tls within the cluster
    1519              :     bool hasTLS = false;
    1520         6578 :     for (NBNode* n : cluster) {
    1521         5072 :         if (n->isTLControlled()) {
    1522              :             hasTLS = true;
    1523              :         }
    1524              :         const auto& stopEnds = ptStopEnds.find(n);
    1525         5072 :         if (stopEnds != ptStopEnds.end()) {
    1526          594 :             for (NBNode* const to : stopEnds->second) {
    1527              :                 if (cluster.count(to) != 0) {
    1528              :                     reason = "it contains a pt stop edge";
    1529            6 :                     return false;
    1530              :                 }
    1531              :             }
    1532              :         }
    1533              :     }
    1534              :     // prevent removal of long edges unless there is weak circle or a traffic light
    1535         1506 :     if (cluster.size() > 2) {
    1536              :         // find the nodes with the biggests physical distance between them
    1537          571 :         double maxLength = -1;
    1538              :         NBEdge* maxEdge = nullptr;
    1539         3743 :         for (NBNode* n1 : cluster) {
    1540        27596 :             for (NBNode* n2 : cluster) {
    1541        24424 :                 NBEdge* e1 = n1->getConnectionTo(n2);
    1542        24424 :                 NBEdge* e2 = n2->getConnectionTo(n1);
    1543        28406 :                 if (e1 != nullptr && e1->getLoadedLength() > maxLength) {
    1544          717 :                     maxLength = e1->getLoadedLength();
    1545              :                     maxEdge = e1;
    1546              :                 }
    1547        28406 :                 if (e2 != nullptr && e2->getLoadedLength() > maxLength) {
    1548          582 :                     maxLength = e2->getLoadedLength();
    1549              :                     maxEdge = e2;
    1550              :                 }
    1551              :             }
    1552              :         }
    1553              : #ifdef DEBUG_JOINJUNCTIONS
    1554              :         for (NBNode* n : cluster) {
    1555              :             if (DEBUGCOND(n)) {
    1556              :                 std::cout << "feasible hasTLS=" << hasTLS << " maxLength=" << maxLength << " maxEdge=" << maxEdge->getID() << "\n";
    1557              :             }
    1558              :         }
    1559              : #endif
    1560          571 :         if (!hasTLS && maxLength > 5) {
    1561              :             // find a weak circle within cluster that does not use maxEdge
    1562              :             std::vector<NBNode*> toCheck;
    1563              :             std::set<NBNode*> visited;
    1564           43 :             toCheck.push_back(maxEdge->getToNode());
    1565              :             bool foundCircle = false;
    1566          130 :             while (!toCheck.empty()) {
    1567          116 :                 NBNode* n = toCheck.back();
    1568          116 :                 if (n == maxEdge->getFromNode()) {
    1569              :                     foundCircle = true;
    1570           29 :                     break;
    1571              :                 }
    1572              :                 toCheck.pop_back();
    1573              :                 visited.insert(n);
    1574          447 :                 for (NBEdge* e : n->getEdges()) {
    1575          360 :                     if (e != maxEdge) {
    1576          506 :                         NBNode* cand = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
    1577              :                         if (visited.count(cand) == 0 && cluster.count(cand) != 0) {
    1578          127 :                             toCheck.push_back(cand);
    1579              :                         }
    1580              :                     }
    1581              :                 }
    1582              :             }
    1583              :             if (!foundCircle) {
    1584           56 :                 reason = "not compact (maxEdge=" + maxEdge->getID() + " length=" + toString(maxLength) + ")";
    1585              :                 return false;
    1586              :             }
    1587           43 :         }
    1588              :     }
    1589              :     // prevent joining of simple merging/spreading structures
    1590         1194 :     if (!hasTLS && cluster.size() >= 2) {
    1591              :         int entryNodes = 0;
    1592              :         int exitNodes = 0;
    1593              :         int outsideIncoming = 0;
    1594              :         int outsideOutgoing = 0;
    1595              :         int edgesWithin = 0;
    1596         3726 :         for (NBNode* n : cluster) {
    1597              :             bool foundOutsideIncoming = false;
    1598         9235 :             for (NBEdge* e : n->getIncomingEdges()) {
    1599         6585 :                 if (cluster.count(e->getFromNode()) == 0) {
    1600              :                     // edge entering from outside the cluster
    1601         3931 :                     outsideIncoming++;
    1602              :                     foundOutsideIncoming = true;
    1603              :                 } else {
    1604         2654 :                     edgesWithin++;
    1605              :                 }
    1606              :             }
    1607         2650 :             if (foundOutsideIncoming) {
    1608         2437 :                 entryNodes++;
    1609              :             }
    1610              :             bool foundOutsideOutgoing = false;
    1611         9267 :             for (NBEdge* e : n->getOutgoingEdges()) {
    1612         6617 :                 if (cluster.count(e->getToNode()) == 0) {
    1613              :                     // edge leaving cluster
    1614         3963 :                     outsideOutgoing++;
    1615              :                     foundOutsideOutgoing = true;
    1616              :                 }
    1617              :             }
    1618         2650 :             if (foundOutsideOutgoing) {
    1619         2423 :                 exitNodes++;
    1620              :             }
    1621              :         }
    1622         1076 :         if (entryNodes < 2) {
    1623              :             reason = "only 1 entry node";
    1624              :             return false;
    1625              :         }
    1626         1072 :         if (exitNodes < 2) {
    1627              :             reason = "only 1 exit node";
    1628              :             return false;
    1629              :         }
    1630         1050 :         if (cluster.size() == 2) {
    1631          805 :             if (edgesWithin == 1 && outsideIncoming < 3 && outsideOutgoing < 3) {
    1632              :                 reason = "only 1 edge within and no cross-traffic";
    1633              :                 return false;
    1634              :             }
    1635              :         }
    1636              :     }
    1637              :     return true;
    1638              : }
    1639              : 
    1640              : 
    1641              : bool
    1642          721 : NBNodeCont::reduceToCircle(NodeSet& cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector<NBNode*> cands) const {
    1643              :     //std::cout << "reduceToCircle  cs=" << circleSize << " cands=" << toString(cands, ',') << " startNodes=" << joinNamedToString(startNodes, ',') << "\n";
    1644              :     assert(circleSize >= 2);
    1645          721 :     if ((int)cands.size() == circleSize) {
    1646          131 :         if (cands.back()->getConnectionTo(cands.front()) != nullptr) {
    1647              :             // cluster found
    1648              :             NodeSet candCluster;
    1649              :             candCluster.insert(cands.begin(), cands.end());
    1650           24 :             pruneClusterFringe(candCluster, maxDist);
    1651           24 :             const bool feasible = (int)candCluster.size() == circleSize;
    1652           24 :             if (feasible) {
    1653              :                 cluster.clear();
    1654              :                 cluster.insert(cands.begin(), cands.end());
    1655              :             }
    1656              :             return feasible;
    1657              :         } else {
    1658              :             return false;
    1659              :         }
    1660              :     }
    1661          590 :     if ((int)cluster.size() <= circleSize || startNodes.size() == 0) {
    1662              :         // no reduction possible
    1663              :         //std::cout << "    abort\n";
    1664              :         return false;
    1665              :     }
    1666          499 :     if (cands.size() == 0) {
    1667              :         // try to find a circle starting from another start node
    1668          199 :         NBEdge* e = shortestEdge(cluster, startNodes, cands);
    1669          199 :         if (e != nullptr) {
    1670          176 :             cands.push_back(e->getFromNode());
    1671          176 :             startNodes.erase(e->getFromNode());
    1672          528 :             if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands)) {
    1673              :                 return true;
    1674              :             } else {
    1675              :                 // try another start node
    1676          310 :                 return reduceToCircle(cluster, circleSize, startNodes, maxDist);
    1677              :             }
    1678              :         }
    1679              :     } else {
    1680              :         NodeSet singleStart;
    1681              :         singleStart.insert(cands.back());
    1682          300 :         NBEdge* e = shortestEdge(cluster, singleStart, cands);
    1683          300 :         if (e != nullptr) {
    1684          265 :             std::vector<NBNode*> cands2(cands);
    1685          265 :             cands2.push_back(e->getToNode());
    1686          795 :             if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands2)) {
    1687              :                 return true;
    1688              :             }
    1689          265 :         }
    1690              :     }
    1691              :     //std::cout << "    abort2\n";
    1692              :     return false;
    1693              : }
    1694              : 
    1695              : 
    1696              : NBEdge*
    1697          499 : NBNodeCont::shortestEdge(const NodeSet& cluster, const NodeSet& startNodes, const std::vector<NBNode*>& exclude) const {
    1698              :     double minDist = std::numeric_limits<double>::max();
    1699              :     NBEdge* result = nullptr;
    1700         1545 :     for (NBNode* n : startNodes) {
    1701         3244 :         for (NBEdge* e : n->getOutgoingEdges()) {
    1702         2198 :             NBNode* neigh = e->getToNode();
    1703         2198 :             if (cluster.count(neigh) != 0 && std::find(exclude.begin(), exclude.end(), neigh) == exclude.end()) {
    1704              :                 const double dist = n->getPosition().distanceTo2D(neigh->getPosition());
    1705              :                 //std::cout << "    e=" << e->getID() << " dist=" << dist << " minD=" << minDist << "\n";
    1706         1296 :                 if (dist < minDist) {
    1707              :                     minDist = dist;
    1708              :                     result = e;
    1709              :                 }
    1710              :             }
    1711              :         }
    1712              :     }
    1713              :     //std::cout << "closestNeighbor startNodes=" << toString(startNodes) << " result=" << Named::getIDSecure(result) << "\n";
    1714          499 :     return result;
    1715              : }
    1716              : 
    1717              : void
    1718          115 : NBNodeCont::joinNodeClusters(NodeClusters clusters,
    1719              :                              NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, bool resetConnections) {
    1720          858 :     for (NodeSet cluster : clusters) {
    1721         1486 :         joinNodeCluster(cluster, dc, ec, tlc, nullptr, resetConnections);
    1722              :     }
    1723          115 : }
    1724              : 
    1725              : 
    1726              : void
    1727          751 : NBNodeCont::joinNodeCluster(NodeSet cluster, NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, NBNode* predefined, bool resetConnections) {
    1728          751 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    1729              :     assert(cluster.size() > 1);
    1730          751 :     std::string id = "cluster_";
    1731              :     Position pos;
    1732          751 :     bool setTL = false;
    1733          751 :     SumoXMLNodeType nodeType = SumoXMLNodeType::UNKNOWN;
    1734         1502 :     TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
    1735              :     // collect edges
    1736              :     std::set<NBEdge*, ComparatorIdLess> allEdges;
    1737         3292 :     for (NBNode* n : cluster) {
    1738              :         const EdgeVector& edges = n->getEdges();
    1739              :         allEdges.insert(edges.begin(), edges.end());
    1740              :     }
    1741              :     // determine edges with are incoming or fully inside
    1742              :     std::set<NBEdge*, ComparatorIdLess> clusterIncoming;
    1743              :     std::set<NBEdge*, ComparatorIdLess> inside;
    1744         9660 :     for (NBEdge* e : allEdges) {
    1745         8909 :         if (cluster.count(e->getToNode()) > 0) {
    1746         5861 :             if (cluster.count(e->getFromNode()) > 0) {
    1747              :                 inside.insert(e);
    1748              :                 if (e->getSignalPosition() != Position::INVALID) {
    1749           39 :                     setTL = true;
    1750           39 :                     nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
    1751              :                 }
    1752              :             } else {
    1753              :                 clusterIncoming.insert(e);
    1754              :             }
    1755              :         }
    1756              :     }
    1757              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1758              :     std::cout << "joining cluster " << joinNamedToString(cluster, ' ')
    1759              :               << "  resetConnections=" << resetConnections << "\n"
    1760              :               << "  incoming=" << joinNamedToString(clusterIncoming, ' ') << "\n"
    1761              :               << "  inside=" << joinNamedToString(inside, ' ') << "\n";
    1762              : #endif
    1763          751 :     analyzeCluster(cluster, id, pos, setTL, type, nodeType);
    1764              :     NBNode* newNode = nullptr;
    1765          751 :     if (predefined != nullptr) {
    1766              :         newNode = predefined;
    1767              :     } else {
    1768          743 :         if (!insert(id, pos)) {
    1769              :             // should not fail
    1770            0 :             WRITE_WARNINGF(TL("Could not join junctions %."), id);
    1771              :             return;
    1772              :         }
    1773          743 :         newNode = retrieve(id);
    1774              :     }
    1775              :     std::string tlID = id;
    1776          751 :     if (predefined != nullptr) {
    1777            8 :         if (predefined->getType() != SumoXMLNodeType::UNKNOWN) {
    1778            1 :             nodeType = predefined->getType();
    1779              :         }
    1780            8 :         Position ppos = predefined->getPosition();
    1781            8 :         if (ppos.x() != Position::INVALID.x()) {
    1782              :             pos.setx(ppos.x());
    1783              :         }
    1784            8 :         if (ppos.y() != Position::INVALID.y()) {
    1785              :             pos.sety(ppos.y());
    1786              :         }
    1787            8 :         if (ppos.z() != Position::INVALID.z()) {
    1788              :             pos.setz(ppos.z());
    1789              :         }
    1790              :     }
    1791          751 :     newNode->reinit(pos, nodeType);
    1792          751 :     if (origNames) {
    1793          950 :         newNode->setParameter(SUMO_PARAM_ORIGID, joinNamedToString(cluster, ' '));
    1794              :     }
    1795          751 :     if (setTL && !newNode->isTLControlled()) {
    1796          212 :         NBTrafficLightDefinition* tlDef = new NBOwnTLDef(tlID, newNode, 0, type);
    1797          212 :         if (!tlc.insert(tlDef)) {
    1798              :             // actually, nothing should fail here
    1799            0 :             delete tlDef;
    1800            0 :             throw ProcessError(TLF("Could not allocate tls '%'.", id));
    1801              :         }
    1802              :     }
    1803              : 
    1804              :     // determine possible connectivity from outside edges
    1805              :     std::map<NBEdge*, EdgeSet> reachable;
    1806              :     std::map<std::pair<NBEdge*, NBEdge*>, SVCPermissions> conPermissions;
    1807              :     EdgeSet specialPermissions;
    1808         3813 :     for (NBEdge* const e : clusterIncoming) {
    1809              :         EdgeVector open;
    1810              :         EdgeSet seen;
    1811         3062 :         open.push_back(e);
    1812        24618 :         while (open.size() > 0) {
    1813        21556 :             NBEdge* const cur = open.back();
    1814        21556 :             const SVCPermissions pCur = conPermissions.count({e, cur}) == 0 ? cur->getPermissions() : conPermissions[ {e, cur}];
    1815              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1816              :             std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
    1817              :             std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
    1818              : #endif
    1819              :             seen.insert(cur);
    1820              :             open.pop_back();
    1821        21556 :             if (cluster.count(cur->getToNode()) == 0) {
    1822              :                 //std::cout << "      continue\n";
    1823         9486 :                 continue;
    1824              :             }
    1825              :             const auto& cons = cur->getConnections();
    1826        13177 :             if (cons.size() == 0 || ec.hasPostProcessConnection(cur->getID()) || cur->getStep() == NBEdge::EdgeBuildingStep::INIT) {
    1827              :                 // check permissions to determine reachability
    1828        39658 :                 for (NBEdge* out : cur->getToNode()->getOutgoingEdges()) {
    1829              :                     if (allEdges.count(out) != 0) {
    1830        28652 :                         const SVCPermissions p = pCur & out->getPermissions();
    1831         6920 :                         if (seen.count(out) == 0 || (~conPermissions[ {e, out}] & p) != 0) {
    1832        21799 :                             if ((p & ~SVC_PEDESTRIAN) != 0) {
    1833        16543 :                                 open.push_back(out);
    1834        16543 :                                 conPermissions[ {e, out}] |= p;
    1835              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1836              :                                 std::cout << "  e=" << e->getID() << " out=" << out->getID() << " pOut=" << getVehicleClassNames(out->getPermissions()) << "\n    p=" << getVehicleClassNames(p) << "\n    q=" << getVehicleClassNames(conPermissions[ {e, out}]) << "\n";
    1837              : #endif
    1838              :                             }
    1839              :                         }
    1840              :                     }
    1841              :                 }
    1842              :             } else {
    1843              :                 // check existing connections
    1844         4389 :                 for (const auto& con : cons) {
    1845         3325 :                     if (con.toEdge != nullptr && allEdges.count(con.toEdge) != 0) {
    1846         3325 :                         SVCPermissions p = pCur & con.toEdge->getPermissions();
    1847         3325 :                         if (con.permissions != SVC_UNSPECIFIED) {
    1848            7 :                             p &= con.permissions;
    1849              :                         }
    1850         1374 :                         if (seen.count(con.toEdge) == 0 || (~conPermissions[ {e, con.toEdge}] & p) != 0) {
    1851         1951 :                             open.push_back(con.toEdge);
    1852         1951 :                             conPermissions[ {e, con.toEdge}] |= p;
    1853              :                             //std::cout << "  e=" << e->getID() << " con.toEdge=" << con.toEdge->getID() << " pSpecial=" << toString(con.permissions) << " pOut=" << getVehicleClassNames(con.toEdge->getPermissions()) << "\n    p=" << getVehicleClassNames(p) << "\n    q=" << getVehicleClassNames(conPermissions[{e, con.toEdge}]) << "\n";
    1854              :                         }
    1855              :                     }
    1856              :                 }
    1857              :             }
    1858              :         }
    1859              :         seen.erase(e);
    1860        18541 :         for (NBEdge* reached : seen) {
    1861              :             // filter out inside edges from reached
    1862              :             if (inside.count(reached) == 0) {
    1863         7642 :                 if (e->getStep() > NBEdge::EdgeBuildingStep::INIT && reached->getFromNode() == e->getToNode() && !e->isConnectedTo(reached)) {
    1864              :                     // also filter out edges that are outgoing of the to-node of edge but aren't currently connected
    1865           14 :                     continue;
    1866              :                 }
    1867         7628 :                 reachable[e].insert(reached);
    1868         7628 :                 const SVCPermissions pDefault = e->getPermissions() & reached->getPermissions();
    1869         7628 :                 if (conPermissions[ {e, reached}] != pDefault) {
    1870              :                     specialPermissions.insert(e);
    1871              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1872              :                     std::cout << "e=" << e->getID() << " out=" << reached->getID() << " special=" << getVehicleClassNames(conPermissions[ {e, reached}]) << "\n";
    1873              : #endif
    1874              :                 }
    1875              :             }
    1876              :         }
    1877              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1878              :         std::cout << " reachable e=" << e->getID() << " seen=" << toString(seen) << " reachable=" << toString(reachable[e]) << "\n";
    1879              : #endif
    1880         3062 :     }
    1881              : 
    1882              :     // remap and remove edges which are completely within the new intersection
    1883          751 :     if (origNames) {
    1884          950 :         newNode->setParameter("origEdgeIds", joinNamedToString(inside, ' '));
    1885              :     }
    1886         3550 :     for (NBEdge* e : inside) {
    1887        51233 :         for (NBEdge* e2 : allEdges) {
    1888        48434 :             if (e != e2) {
    1889        45635 :                 e2->replaceInConnections(e, e->getConnections());
    1890              :             }
    1891              :         }
    1892         2799 :         ec.extract(dc, e, true);
    1893              :         allEdges.erase(e);
    1894              :     }
    1895              : 
    1896              :     // remap edges which are incoming / outgoing
    1897         6861 :     for (NBEdge* e : allEdges) {
    1898         6110 :         const bool outgoing = cluster.count(e->getFromNode()) > 0;
    1899              :         NBNode* from = outgoing ? newNode : e->getFromNode();
    1900              :         NBNode* to   = outgoing ? e->getToNode() : newNode;
    1901         6110 :         if (origNames) {
    1902         4009 :             if (outgoing) {
    1903         4004 :                 e->setParameter("origFrom", e->getFromNode()->getID());
    1904              :             } else {
    1905         4014 :                 e->setParameter("origTo", e->getToNode()->getID());
    1906              :             }
    1907              :         }
    1908         6110 :         if (e->getTurnSignTarget() != "") {
    1909        24444 :             for (NBNode* n : cluster) {
    1910        20556 :                 if (e->getTurnSignTarget() == n->getID()) {
    1911              :                     e->setTurnSignTarget(to->getID());
    1912              :                     break;
    1913              :                 }
    1914              :             }
    1915              :         }
    1916         6110 :         e->reinitNodes(from, to);
    1917              :         // re-add connections which previously existed and may still valid.
    1918              :         // connections to removed edges will be ignored
    1919         6110 :         std::vector<NBEdge::Connection> conns = e->getConnections();
    1920         7716 :         for (std::vector<NBEdge::Connection>::iterator k = conns.begin(); k != conns.end(); ++k) {
    1921         1606 :             if ((*k).toEdge == nullptr) {
    1922              :                 // edge explicitly set to have no connections
    1923            1 :                 continue;
    1924              :             }
    1925         1605 :             e->addLane2LaneConnection((*k).fromLane, (*k).toEdge, (*k).toLane, NBEdge::Lane2LaneInfoType::USER, false, (*k).mayDefinitelyPass);
    1926         1605 :             if ((*k).fromLane >= 0 && (*k).fromLane < e->getNumLanes() && e->getLaneStruct((*k).fromLane).connectionsDone) {
    1927              :                 // @note (see NIImporter_DlrNavteq::ConnectedLanesHandler)
    1928              :                 e->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::INIT);
    1929              : #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
    1930              :                 std::cout << "   e=" << e->getID() << " declareConnectionsAsLoaded\n";
    1931              : #endif
    1932              :             }
    1933              :         }
    1934         6110 :     }
    1935          751 :     if (!resetConnections) {
    1936              :         // disable connections that were impossible with the old topology
    1937              :         // if connectivity has special permissions, set edge to edge connections explicitly
    1938         3746 :         for (NBEdge* in : newNode->getIncomingEdges()) {
    1939        17407 :             for (NBEdge* out : newNode->getOutgoingEdges()) {
    1940        14393 :                 if (reachable[in].count(out) == 0) {
    1941         6819 :                     if (!ec.hasPostProcessConnection(in->getID(), out->getID())) {
    1942              :                         //std::cout << " removeUnreachable in=" << in->getID() << " out=" << out->getID() << "\n";
    1943         6819 :                         in->removeFromConnections(out, -1, -1, true, false, true);
    1944              :                     } else {
    1945              :                         //std::cout << " hasPostProcessConnection in=" << in->getID() << " out=" << out->getID() << "\n";
    1946              :                     }
    1947              :                 } else if (specialPermissions.count(in) != 0) {
    1948          413 :                     SVCPermissions pDefault = in->getPermissions() & out->getPermissions();
    1949          413 :                     SVCPermissions p = conPermissions[ {in, out}] == 0 ? pDefault : conPermissions[ {in, out}];
    1950          413 :                     in->addEdge2EdgeConnection(out, true, p == pDefault ? SVC_UNSPECIFIED : p);
    1951              :                     //std::cout << " addEdge2Edge in=" << in->getID() << " out=" << out->getID() << "\n";
    1952              :                 }
    1953              :             }
    1954              :         }
    1955              :     } else {
    1956           67 :         for (NBEdge* in : newNode->getIncomingEdges()) {
    1957           48 :             in->invalidateConnections(true);
    1958              :         }
    1959              :     }
    1960              : 
    1961              :     // remove original nodes
    1962          751 :     registerJoinedCluster(cluster);
    1963         3292 :     for (NBNode* n : cluster) {
    1964         2541 :         erase(n);
    1965              :     }
    1966              : }
    1967              : 
    1968              : 
    1969              : void
    1970          751 : NBNodeCont::registerJoinedCluster(const NodeSet& cluster) {
    1971              :     std::set<std::string> ids;
    1972         3292 :     for (NBNode* n : cluster) {
    1973              :         ids.insert(n->getID());
    1974              :     }
    1975          751 :     myJoinedClusters.push_back(ids);
    1976          751 : }
    1977              : 
    1978              : void
    1979            0 : NBNodeCont::registerJoinedCluster(const std::set<std::string>& cluster) {
    1980            0 :     myJoinedClusters.push_back(cluster);
    1981            0 : }
    1982              : 
    1983              : void
    1984            0 : NBNodeCont::unregisterJoinedCluster(const std::set<std::string>& cluster) {
    1985            0 :     auto it = std::find(myJoinedClusters.begin(), myJoinedClusters.end(), cluster);
    1986            0 :     if (it != myJoinedClusters.end()) {
    1987            0 :         myJoinedClusters.erase(it);
    1988              :     }
    1989            0 : }
    1990              : 
    1991              : 
    1992              : void
    1993          798 : NBNodeCont::analyzeCluster(NodeSet cluster, std::string& id, Position& pos,
    1994              :                            bool& hasTLS, TrafficLightType& type, SumoXMLNodeType& nodeType) {
    1995         1596 :     id = createClusterId(cluster, id);
    1996              :     bool ambiguousType = false;
    1997         3464 :     for (NBNode* j : cluster) {
    1998              :         pos.add(j->getPosition());
    1999              :         // add a traffic light if any of the cluster members was controlled
    2000         2666 :         if (j->isTLControlled()) {
    2001          729 :             if (!hasTLS) {
    2002              :                 // init type
    2003          225 :                 type = (*j->getControllingTLS().begin())->getType();
    2004          504 :             } else if (type != (*j->getControllingTLS().begin())->getType()) {
    2005              :                 ambiguousType = true;
    2006              :             }
    2007          729 :             hasTLS = true;
    2008              :         }
    2009         2666 :         SumoXMLNodeType otherType = j->getType();
    2010         2666 :         if (nodeType == SumoXMLNodeType::UNKNOWN) {
    2011         1455 :             nodeType = otherType;
    2012         1211 :         } else if (nodeType != otherType) {
    2013          508 :             if (hasTLS) {
    2014          470 :                 nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
    2015           38 :             } else if (otherType != SumoXMLNodeType::UNKNOWN) {
    2016           14 :                 if ((nodeType != SumoXMLNodeType::PRIORITY && (nodeType != SumoXMLNodeType::NOJUNCTION || otherType != SumoXMLNodeType::PRIORITY))
    2017           12 :                         || (otherType != SumoXMLNodeType::NOJUNCTION && otherType != SumoXMLNodeType::UNKNOWN && otherType != SumoXMLNodeType::PRIORITY)) {
    2018           24 :                     WRITE_WARNINGF("Ambiguous node type for node cluster '%' (%,%), setting to '" + toString(SumoXMLNodeType::PRIORITY) + "'.", id, toString(nodeType), toString(otherType));
    2019              :                 }
    2020           14 :                 nodeType = SumoXMLNodeType::PRIORITY;
    2021              :             }
    2022              :         }
    2023              :     }
    2024          798 :     pos.mul(1. / (double)cluster.size());
    2025          798 :     if (ambiguousType) {
    2026            0 :         type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
    2027            0 :         WRITE_WARNINGF(TL("Ambiguous traffic light type for node cluster '%', setting to '%'."), id, toString(type));
    2028              :     }
    2029          798 : }
    2030              : 
    2031              : 
    2032              : // ----------- (Helper) methods for guessing/computing traffic lights
    2033              : bool
    2034          996 : NBNodeCont::shouldBeTLSControlled(const NodeSet& c, double laneSpeedThreshold, bool recheck) const {
    2035              :     bool tooFast = false;
    2036              :     double laneSpeedSum = 0;
    2037              :     std::set<NBEdge*> seen;
    2038         2157 :     for (NBNode* j : c) {
    2039         6308 :         for (const NBEdge* e : j->getEdges()) {
    2040         5147 :             if (c.find(e->getFromNode()) != c.end() && c.find(e->getToNode()) != c.end()) {
    2041              :                 // edges fully within the cluster do not count
    2042          624 :                 continue;
    2043              :             }
    2044         4523 :             if (j->hasIncoming(e)) {
    2045         2258 :                 if (recheck && !j->hasConflict(e)) {
    2046              :                     // edges without conflict do not count
    2047              :                     // we can only check this after connections have been computed
    2048            2 :                     continue;
    2049              :                 }
    2050         2256 :                 laneSpeedSum += (double)e->getNumLanes() * e->getLaneSpeed(0);
    2051              :             }
    2052         4521 :             if (e->getLaneSpeed(0) * 3.6 > 79) {
    2053              :                 tooFast = true;
    2054              :             }
    2055              :         }
    2056              :     }
    2057              :     //std::cout << " c=" << joinNamedToString(c, ' ') << " size=" << c.size() << " laneSpeedSum=" << laneSpeedSum << " thresh=" << laneSpeedThreshold << " tooFast=" << tooFast << "\n";
    2058         1992 :     return !tooFast && laneSpeedSum >= laneSpeedThreshold && c.size() != 0;
    2059              : }
    2060              : 
    2061              : bool
    2062           54 : NBNodeCont::onlyCrossings(const NodeSet& c) const {
    2063              :     // check whether all component nodes are solely pedestrian crossings
    2064              :     // (these work fine without joining)
    2065           96 :     for (NBNode* node : c) {
    2066              :         EdgeVector nonPedIncoming;
    2067              :         EdgeVector nonPedOutgoing;
    2068          367 :         for (NBEdge* e : node->getIncomingEdges()) {
    2069          276 :             if (e->getPermissions() != SVC_PEDESTRIAN) {
    2070          211 :                 nonPedIncoming.push_back(e);
    2071              :             }
    2072              :         }
    2073          369 :         for (NBEdge* e : node->getOutgoingEdges()) {
    2074          278 :             if (e->getPermissions() != SVC_PEDESTRIAN) {
    2075          210 :                 nonPedOutgoing.push_back(e);
    2076              :             }
    2077              :         }
    2078           91 :         if (!node->geometryLike(nonPedIncoming, nonPedOutgoing)) {
    2079              :             //for (NBNode* node : c) {
    2080              :             //    if (node->getID() == "2480337678") {
    2081              :             //        std::cout << " node=" << node->getID() << " nonPedIncoming=" << toString(nonPedIncoming) << " nonPedOutgoing=" << toString(nonPedOutgoing) << "\n";
    2082              :             //    }
    2083              :             //}
    2084              :             return false;
    2085              :         }
    2086           91 :     }
    2087              :     return true;
    2088              : }
    2089              : 
    2090              : 
    2091              : bool
    2092           49 : NBNodeCont::customTLID(const NodeSet& c) const {
    2093          174 :     for (NBNode* node : c) {
    2094          127 :         if (node->isTLControlled()) {
    2095          127 :             NBTrafficLightDefinition* tl = (*node->getControllingTLS().begin());
    2096          127 :             if (tl->getNodes().size() > 1) {
    2097              :                 // joined tls also imply a customID
    2098            2 :                 return true;
    2099              :             }
    2100              :             const std::string tlID = tl->getID();
    2101          125 :             if (tlID != node->getID()
    2102          127 :                     && !StringUtils::startsWith(tlID, "joinedS_")
    2103          126 :                     && !StringUtils::startsWith(tlID, "joinedG_")
    2104          129 :                     && !StringUtils::startsWith(tlID, "GS")) {
    2105              :                 return true;
    2106              :             }
    2107              :         }
    2108              :     }
    2109              :     return false;
    2110              : }
    2111              : 
    2112              : 
    2113              : void
    2114         1704 : NBNodeCont::guessTLs(OptionsCont& oc, NBTrafficLightLogicCont& tlc) {
    2115              :     myGuessedTLS.clear();
    2116              :     // build list of definitely not tls-controlled junctions
    2117         1704 :     const double laneSpeedThreshold = oc.getFloat("tls.guess.threshold");
    2118         3408 :     if (oc.isSet("tls.unset")) {
    2119            2 :         std::vector<std::string> notTLControlledNodes = oc.getStringVector("tls.unset");
    2120            2 :         for (std::vector<std::string>::const_iterator i = notTLControlledNodes.begin(); i != notTLControlledNodes.end(); ++i) {
    2121            1 :             NBNode* n = NBNodeCont::retrieve(*i);
    2122            1 :             if (n == nullptr) {
    2123            0 :                 throw ProcessError(TLF(" The junction '%' to set as not-controlled is not known.", *i));
    2124              :             }
    2125              :             std::set<NBTrafficLightDefinition*> tls = n->getControllingTLS();
    2126            1 :             for (std::set<NBTrafficLightDefinition*>::const_iterator j = tls.begin(); j != tls.end(); ++j) {
    2127            0 :                 (*j)->removeNode(n);
    2128              :             }
    2129            1 :             n->removeTrafficLights();
    2130              :             myUnsetTLS.insert(n);
    2131              :         }
    2132            1 :     }
    2133              : 
    2134         1704 :     TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
    2135              :     // loop#1 checking whether the node shall be tls controlled,
    2136              :     //  because it is assigned to a district
    2137         3320 :     if (oc.exists("tls.taz-nodes") && oc.getBool("tls.taz-nodes")) {
    2138            0 :         for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2139            0 :             NBNode* cur = (*i).second;
    2140            0 :             if (cur->isNearDistrict() && myUnsetTLS.count(cur) == 0) {
    2141            0 :                 setAsTLControlled(cur, tlc, type);
    2142              :             }
    2143              :         }
    2144              :     }
    2145              : 
    2146              :     // figure out which nodes mark the locations of TLS signals
    2147              :     // This assumes nodes are already joined
    2148         3320 :     if (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals")) {
    2149              :         // prepare candidate edges
    2150           84 :         const double signalDist = oc.getFloat("tls.guess-signals.dist");
    2151        12291 :         for (const auto& item : myNodes) {
    2152        12249 :             const NBNode* node = item.second;
    2153        12249 :             if (node->isTLControlled() && (node->getIncomingEdges().size() == 1 || node->geometryLike())) {
    2154              : #ifdef DEBUG_GUESSSIGNALS
    2155              :                 if (DEBUGCOND(node) || true) {
    2156              :                     std::cout << " propagate TLS from " << node->getID() << " downstream\n";
    2157              :                 }
    2158              : #endif
    2159          152 :                 for (NBEdge* edge : node->getOutgoingEdges()) {
    2160              :                     // do not overwrite closer signals
    2161           95 :                     if (edge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET) {
    2162              :                         edge->setSignalPosition(node->getPosition(), node);
    2163              :                     }
    2164              :                 }
    2165              :             }
    2166              :         }
    2167              :         std::set<NBEdge*> seen;
    2168              :         std::set<NBEdge*> check;
    2169        12291 :         for (const auto& item : myNodes) {
    2170        32845 :             for (NBEdge* edge : item.second->getOutgoingEdges()) {
    2171              :                 if (edge->getSignalPosition() != Position::INVALID) {
    2172              :                     check.insert(edge);
    2173              :                     seen.insert(edge);
    2174              : #ifdef DEBUG_GUESSSIGNALS
    2175              :                     if (DEBUGCOND(edge->getSignalNode()) || true) {
    2176              :                         std::cout << "   primary signalPosition edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
    2177              :                     }
    2178              : #endif
    2179              :                 }
    2180              :             }
    2181              :         }
    2182              :         // propagate signal position until the next real intersection
    2183         1101 :         while (check.size() > 0) {
    2184         1059 :             NBEdge* const edge = *check.begin();
    2185              :             check.erase(check.begin());
    2186              :             seen.insert(edge);
    2187              :             NBNode* const nextNode = edge->getToNode();
    2188         1059 :             if (nextNode->geometryLike() && !nextNode->isTLControlled()) {
    2189          502 :                 for (NBEdge* const outEdge : nextNode->getOutgoingEdges()) {
    2190              :                     if (seen.count(outEdge) == 0) {
    2191              :                         outEdge->setSignalPosition(edge->getSignalPosition(), edge->getSignalNode());
    2192              : #ifdef DEBUG_GUESSSIGNALS
    2193              :                         if (DEBUGCOND(edge->getSignalNode()) || true) {
    2194              :                             std::cout << "   setSignalPosition edge=" << outEdge->getID() << " pos=" << edge->getSignalPosition() << "\n";
    2195              :                         }
    2196              : #endif
    2197              :                         check.insert(outEdge);
    2198              :                     }
    2199              :                 }
    2200              :             }
    2201              :         }
    2202              : 
    2203              :         // check which nodes should be controlled
    2204           84 :         const int slack = oc.getInt("tls.guess-signals.slack");
    2205        12291 :         for (std::map<std::string, NBNode*>::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
    2206        12249 :             NBNode* node = i->second;
    2207            1 :             if (myUnsetTLS.count(node) != 0) {
    2208              :                 continue;
    2209              :             }
    2210              :             const EdgeVector& incoming = node->getIncomingEdges();
    2211              :             const EdgeVector& outgoing = node->getOutgoingEdges();
    2212        11918 :             if (!node->isTLControlled() && incoming.size() > 1 && !node->geometryLike()
    2213         5296 :                     && !NBNodeTypeComputer::isRailwayNode(node)
    2214        17330 :                     && node->getType() != SumoXMLNodeType::RAIL_CROSSING) {
    2215              :                 std::vector<const NBNode*> signals;
    2216              :                 int foundSignals = 0;
    2217              :                 int missingSignals = 0;
    2218              :                 // check if there is a signal at every incoming edge
    2219        12161 :                 for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
    2220         9383 :                     const NBEdge* inEdge = *it_i;
    2221         9383 :                     if (inEdge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET) {
    2222         9062 :                         if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
    2223              : #ifdef DEBUG_GUESSSIGNALS
    2224              :                             if (DEBUGCOND(node)) {
    2225              :                                 std::cout << " noTLS, edge=" << inEdge->getID() << "\n";
    2226              :                             }
    2227              : #endif
    2228         2188 :                             missingSignals++;
    2229         2188 :                             if (missingSignals > slack) {
    2230              :                                 break;
    2231              :                             }
    2232              :                         }
    2233              :                     } else {
    2234          321 :                         foundSignals++;
    2235              :                     }
    2236              :                 }
    2237              :                 missingSignals = 0;
    2238              :                 int foundSignalsAtDist = 0;
    2239         4962 :                 if (foundSignals > 1 && missingSignals <= slack && missingSignals < foundSignals) {
    2240           55 :                     node->updateSurroundingGeometry();
    2241              :                     // check if all signals are within the required distance
    2242              :                     // (requires detailed geometry computation)
    2243          227 :                     for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
    2244          181 :                         const NBEdge* inEdge = *it_i;
    2245          181 :                         if (inEdge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET || inEdge->getSignalOffset() > signalDist) {
    2246           19 :                             if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
    2247              : #ifdef DEBUG_GUESSSIGNALS
    2248              :                                 if (DEBUGCOND(node)) {
    2249              :                                     std::cout << " noTLS, edge=" << inEdge->getID() << " offset=" << inEdge->getSignalOffset() << " tlsPos=" << inEdge->getSignalPosition() << "\n";
    2250              :                                 }
    2251              : #endif
    2252            9 :                                 missingSignals++;
    2253            9 :                                 if (missingSignals > slack) {
    2254              :                                     break;
    2255              :                                 }
    2256              :                             }
    2257              :                         } else {
    2258          162 :                             foundSignalsAtDist++;
    2259              :                         }
    2260          172 :                         const NBNode* signal = inEdge->getSignalNode();
    2261          172 :                         if (signal != nullptr) {
    2262           10 :                             signals.push_back(signal);
    2263              :                         }
    2264              :                     }
    2265              :                     // outgoing edges may be tagged with pedestrian crossings. These
    2266              :                     // should also be merged into the main TLS
    2267          234 :                     for (const NBEdge* outEdge : outgoing) {
    2268          179 :                         NBNode* cand = outEdge->getToNode();
    2269          179 :                         if (cand->isTLControlled() && cand->geometryLike() && outEdge->getLength() <= signalDist) {
    2270              : #ifdef DEBUG_GUESSSIGNALS
    2271              :                             if (DEBUGCOND(node)) {
    2272              :                                 std::cout << " node=" << node->getID() << " outEdge=" << outEdge->getID() << " signalNode=" << cand->getID() << " len=" << outEdge->getLength() << "\n";
    2273              :                             }
    2274              : #endif
    2275            1 :                             signals.push_back(cand);
    2276              :                         }
    2277              :                     }
    2278              :                 }
    2279         4962 :                 if (foundSignalsAtDist > 1 && missingSignals <= slack && missingSignals < foundSignalsAtDist) {
    2280           57 :                     for (const NBNode* s : signals) {
    2281              :                         std::set<NBTrafficLightDefinition*> tls = s->getControllingTLS();
    2282           11 :                         const_cast<NBNode*>(s)->reinit(s->getPosition(), SumoXMLNodeType::PRIORITY);
    2283           21 :                         for (std::set<NBTrafficLightDefinition*>::iterator k = tls.begin(); k != tls.end(); ++k) {
    2284           20 :                             tlc.removeFully(s->getID());
    2285              :                         }
    2286              :                     }
    2287              :                     //if (true) std::cout << " node=" << node->getID() << " signals=" << toString(signals) << "\n";
    2288           46 :                     NBTrafficLightDefinition* tlDef = new NBOwnTLDef("GS_" + node->getID(), node, 0, type);
    2289              :                     // @todo patch endOffset for all incoming lanes according to the signal positions
    2290           46 :                     if (!tlc.insert(tlDef)) {
    2291              :                         // actually, nothing should fail here
    2292            0 :                         WRITE_WARNINGF(TL("Could not build joined tls '%'."), node->getID());
    2293            0 :                         delete tlDef;
    2294              :                         return;
    2295              :                     }
    2296              :                 }
    2297         4962 :             }
    2298              :         }
    2299              :     }
    2300              : 
    2301              :     // guess joined tls first, if wished
    2302         3408 :     if (oc.getBool("tls.guess.joining")) {
    2303              :         // get node clusters
    2304              :         NodeClusters cands;
    2305            8 :         generateNodeClusters(oc.getFloat("tls.join-dist"), cands);
    2306              :         // check these candidates (clusters) whether they should be controlled by a tls
    2307           38 :         for (NodeClusters::iterator i = cands.begin(); i != cands.end();) {
    2308              :             NodeSet& c = (*i);
    2309              :             // regard only junctions which are not yet controlled and are not
    2310              :             //  forbidden to be controlled
    2311          245 :             for (NodeSet::iterator j = c.begin(); j != c.end();) {
    2312          211 :                 if ((*j)->isTLControlled() || myUnsetTLS.count(*j) != 0) {
    2313              :                     c.erase(j++);
    2314              :                 } else {
    2315              :                     ++j;
    2316              :                 }
    2317              :             }
    2318              :             // check whether the cluster should be controlled
    2319              :             // to avoid gigantic clusters, assume that at most 4 nodes should be needed for a guessed-joined-tls
    2320           50 :             if (c.size() == 0 || !shouldBeTLSControlled(c, laneSpeedThreshold * (double)c.size() / MIN2((double)c.size(), 4.))) {
    2321              :                 i = cands.erase(i);
    2322              :             } else {
    2323              :                 ++i;
    2324              :             }
    2325              :         }
    2326              :         // cands now only contain sets of junctions that shall be joined into being tls-controlled
    2327           14 :         for (auto nodeSet : cands) {
    2328              :             std::vector<NBNode*> nodes;
    2329           53 :             for (NBNode* node : nodeSet) {
    2330           43 :                 nodes.push_back(node);
    2331              :                 myGuessedTLS.insert(node);
    2332              :             }
    2333           10 :             const std::string& id = createClusterId(nodeSet, "joinedG_");
    2334           10 :             NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
    2335           10 :             if (!tlc.insert(tlDef)) {
    2336              :                 // actually, nothing should fail here
    2337            0 :                 WRITE_WARNING(TL("Could not build guessed, joined tls."));
    2338            0 :                 delete tlDef;
    2339              :                 return;
    2340              :             }
    2341           10 :         }
    2342            4 :     }
    2343              : 
    2344              :     // guess single tls
    2345         3408 :     if (oc.getBool("tls.guess")) {
    2346          927 :         for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2347          907 :             NBNode* cur = (*i).second;
    2348              :             //  do nothing if already is tl-controlled
    2349          907 :             if (cur->isTLControlled()) {
    2350          765 :                 continue;
    2351              :             }
    2352              :             // do nothing if in the list of explicit non-controlled junctions
    2353            1 :             if (myUnsetTLS.count(cur) != 0) {
    2354            1 :                 continue;
    2355              :             }
    2356              :             NodeSet c;
    2357              :             c.insert(cur);
    2358          836 :             if (!shouldBeTLSControlled(c, laneSpeedThreshold) || cur->geometryLike()) {
    2359              :                 continue;
    2360              :             }
    2361          284 :             setAsTLControlled(cur, tlc, type);
    2362              :             myGuessedTLS.insert(cur);
    2363              :         }
    2364              :     }
    2365              : }
    2366              : 
    2367              : 
    2368              : void
    2369         1703 : NBNodeCont::recheckGuessedTLS(NBTrafficLightLogicCont& tlc) {
    2370              :     std::set<NBTrafficLightDefinition*> recompute;
    2371         1888 :     for (NBNode* node : myGuessedTLS) {
    2372          185 :         if (!node->hasConflict() || !recheckTLSThreshold(node)) {
    2373              :             const std::set<NBTrafficLightDefinition*>& tlDefs = node->getControllingTLS();
    2374           21 :             recompute.insert(tlDefs.begin(), tlDefs.end());
    2375           21 :             node->removeTrafficLights(true);
    2376           49 :             for (NBEdge* edge : node->getIncomingEdges()) {
    2377           28 :                 edge->clearControllingTLInformation();
    2378              :             }
    2379              :         }
    2380              :     }
    2381         1715 :     for (NBTrafficLightDefinition* def : recompute) {
    2382           12 :         if (def->getNodes().size() == 0) {
    2383           10 :             tlc.removeFully(def->getID());
    2384              :         } else {
    2385            7 :             def->setParticipantsInformation();
    2386            7 :             def->setTLControllingInformation();
    2387            7 :             tlc.computeSingleLogic(OptionsCont::getOptions(), def);
    2388              :         }
    2389              :     }
    2390         1703 : }
    2391              : 
    2392              : 
    2393              : bool
    2394          166 : NBNodeCont::recheckTLSThreshold(NBNode* node) {
    2395          166 :     if (!node->isTLControlled()) {
    2396              :         return false;
    2397              :     }
    2398          166 :     if ((*node->getControllingTLS().begin())->getNodes().size() != 1) {
    2399              :         // unable to perform check for a joined tls
    2400              :         return true;
    2401              :     }
    2402              :     NodeSet c;
    2403              :     c.insert(node);
    2404          127 :     const double laneSpeedThreshold = OptionsCont::getOptions().getFloat("tls.guess.threshold");
    2405          127 :     return shouldBeTLSControlled(c, laneSpeedThreshold, true);
    2406              : }
    2407              : 
    2408              : 
    2409              : void
    2410         1703 : NBNodeCont::computeKeepClear() {
    2411        52502 :     for (const auto& item : myNodes) {
    2412        50799 :         item.second->computeKeepClear();
    2413              :     }
    2414         1703 : }
    2415              : 
    2416              : 
    2417              : void
    2418           36 : NBNodeCont::joinTLS(NBTrafficLightLogicCont& tlc, double maxdist) {
    2419           72 :     const std::vector<std::string> excludeList = OptionsCont::getOptions().getStringVector("tls.join-exclude");
    2420           38 :     for (const std::string& tlsID : excludeList) {
    2421            2 :         if (!tlc.exist(tlsID, false)) {
    2422            3 :             WRITE_WARNINGF("Unknown tls ID '%' in option tls.join-exclude", tlsID);
    2423              :         }
    2424              :     }
    2425           36 :     std::set<std::string> exclude(excludeList.begin(), excludeList.end());
    2426              :     NodeClusters cands;
    2427           36 :     generateNodeClusters(maxdist, cands);
    2428          661 :     for (NodeSet& c : cands) {
    2429         2416 :         for (NodeSet::iterator j = c.begin(); j != c.end();) {
    2430         1791 :             if (!(*j)->isTLControlled() || exclude.count((*(*j)->getControllingTLS().begin())->getID()) != 0) {
    2431              :                 c.erase(j++);
    2432              :             } else {
    2433              :                 ++j;
    2434              :             }
    2435              :         }
    2436          625 :         if (c.size() < 2 || onlyCrossings(c) || customTLID(c)) {
    2437          578 :             continue;
    2438              :         }
    2439              :         // figure out type of the joined TLS
    2440              :         Position dummyPos;
    2441           47 :         bool dummySetTL = false;
    2442           47 :         std::string id = "joinedS_"; // prefix (see #3871)
    2443              :         TrafficLightType type;
    2444           47 :         SumoXMLNodeType nodeType = SumoXMLNodeType::UNKNOWN;
    2445           94 :         analyzeCluster(c, id, dummyPos, dummySetTL, type, nodeType);
    2446          172 :         for (NBNode* j : c) {
    2447              :             std::set<NBTrafficLightDefinition*> tls = j->getControllingTLS();
    2448          125 :             j->removeTrafficLights();
    2449          250 :             for (NBTrafficLightDefinition* k : tls) {
    2450          250 :                 tlc.removeFully(k->getID());
    2451              :             }
    2452              :         }
    2453              :         std::vector<NBNode*> nodes;
    2454          172 :         for (NBNode* j : c) {
    2455          125 :             nodes.push_back(j);
    2456              :         }
    2457           47 :         NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
    2458           47 :         if (!tlc.insert(tlDef)) {
    2459              :             // actually, nothing should fail here
    2460            0 :             WRITE_WARNING(TL("Could not build a joined tls."));
    2461            0 :             delete tlDef;
    2462              :             return;
    2463              :         }
    2464           47 :     }
    2465           72 : }
    2466              : 
    2467              : 
    2468              : void
    2469          150 : NBNodeCont::setAsTLControlled(NBNode* node, NBTrafficLightLogicCont& tlc,
    2470              :                               TrafficLightType type, std::string id) {
    2471          150 :     if (id == "") {
    2472              :         id = node->getID();
    2473              :     }
    2474          150 :     NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, node, 0, type);
    2475          150 :     if (!tlc.insert(tlDef)) {
    2476              :         // actually, nothing should fail here
    2477            0 :         WRITE_WARNINGF(TL("Building a tl-logic for junction '%' twice is not possible."), id);
    2478            0 :         delete tlDef;
    2479            0 :         return;
    2480              :     }
    2481              : }
    2482              : 
    2483              : 
    2484              : // -----------
    2485              : void
    2486         1704 : NBNodeCont::computeLanes2Lanes() {
    2487        52507 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2488        50803 :         (*i).second->computeLanes2Lanes();
    2489              :     }
    2490         1704 : }
    2491              : 
    2492              : 
    2493              : // computes the "wheel" of incoming and outgoing edges for every node
    2494              : void
    2495         1703 : NBNodeCont::computeLogics(const NBEdgeCont& ec) {
    2496        52502 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2497        50799 :         (*i).second->computeLogic(ec);
    2498              :     }
    2499         1703 : }
    2500              : 
    2501              : 
    2502              : void
    2503         1703 : NBNodeCont::computeLogics2(const NBEdgeCont& ec, OptionsCont& oc) {
    2504              :     std::set<NBNode*> roundaboutNodes;
    2505         1703 :     const bool checkLaneFoesAll = oc.getBool("check-lane-foes.all");
    2506         1703 :     const bool checkLaneFoesRoundabout = !checkLaneFoesAll && oc.getBool("check-lane-foes.roundabout");
    2507         1703 :     if (checkLaneFoesRoundabout) {
    2508         1698 :         const std::set<EdgeSet>& roundabouts = ec.getRoundabouts();
    2509         1770 :         for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
    2510          422 :             for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
    2511          350 :                 roundaboutNodes.insert((*j)->getToNode());
    2512              :             }
    2513              :         }
    2514              :     }
    2515        52502 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2516        50799 :         const bool checkLaneFoes = checkLaneFoesAll || (checkLaneFoesRoundabout && roundaboutNodes.count((*i).second) > 0);
    2517        50799 :         (*i).second->computeLogic2(checkLaneFoes);
    2518              :     }
    2519         1703 : }
    2520              : 
    2521              : 
    2522              : void
    2523         2002 : NBNodeCont::clear() {
    2524        53519 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2525        51517 :         delete ((*i).second);
    2526              :     }
    2527              :     myNodes.clear();
    2528        10535 :     for (auto& item : myExtractedNodes) {
    2529         8533 :         delete item.second;
    2530              :     }
    2531              :     myExtractedNodes.clear();
    2532         2002 : }
    2533              : 
    2534              : 
    2535              : std::string
    2536            0 : NBNodeCont::getFreeID() {
    2537            0 :     int counter = 0;
    2538            0 :     std::string freeID = "SUMOGenerated" + toString<int>(counter);
    2539              :     // While there is a node with id equal to freeID
    2540            0 :     while (retrieve(freeID) != nullptr) {
    2541              :         // update counter and generate a new freeID
    2542            0 :         counter++;
    2543            0 :         freeID = "SUMOGenerated" + toString<int>(counter);
    2544              :     }
    2545            0 :     return freeID;
    2546              : }
    2547              : 
    2548              : 
    2549              : void
    2550         1853 : NBNodeCont::computeNodeShapes(double mismatchThreshold) {
    2551        75302 :     for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2552        73449 :         (*i).second->computeNodeShape(mismatchThreshold);
    2553              :     }
    2554         1853 : }
    2555              : 
    2556              : 
    2557              : void
    2558         1700 : NBNodeCont::printBuiltNodesStatistics() const {
    2559         1700 :     WRITE_MESSAGE(TL("-----------------------------------------------------"));
    2560         1700 :     WRITE_MESSAGE(TL("Summary:"));
    2561              : 
    2562         1700 :     int numUnregulatedJunctions = 0;
    2563         1700 :     int numDeadEndJunctions = 0;
    2564         1700 :     int numTrafficLightJunctions = 0;
    2565         1700 :     int numPriorityJunctions = 0;
    2566         1700 :     int numRightBeforeLeftJunctions = 0;
    2567         1700 :     int numLeftBeforeRightJunctions = 0;
    2568         1700 :     int numAllWayStopJunctions = 0;
    2569         1700 :     int numZipperJunctions = 0;
    2570         1700 :     int numDistrictJunctions = 0;
    2571         1700 :     int numRailCrossing = 0;
    2572         1700 :     int numRailSignals = 0;
    2573        52486 :     for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); i++) {
    2574        50786 :         switch ((*i).second->getType()) {
    2575          113 :             case SumoXMLNodeType::NOJUNCTION:
    2576          113 :                 ++numUnregulatedJunctions;
    2577          113 :                 break;
    2578        14068 :             case SumoXMLNodeType::DEAD_END:
    2579        14068 :                 ++numDeadEndJunctions;
    2580        14068 :                 break;
    2581         2330 :             case SumoXMLNodeType::TRAFFIC_LIGHT:
    2582              :             case SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED:
    2583              :             case SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION:
    2584         2330 :                 ++numTrafficLightJunctions;
    2585         2330 :                 break;
    2586        29544 :             case SumoXMLNodeType::PRIORITY:
    2587              :             case SumoXMLNodeType::PRIORITY_STOP:
    2588        29544 :                 ++numPriorityJunctions;
    2589        29544 :                 break;
    2590         3175 :             case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
    2591         3175 :                 ++numRightBeforeLeftJunctions;
    2592         3175 :                 break;
    2593            2 :             case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
    2594            2 :                 ++numLeftBeforeRightJunctions;
    2595            2 :                 break;
    2596            1 :             case SumoXMLNodeType::ALLWAY_STOP:
    2597            1 :                 ++numAllWayStopJunctions;
    2598            1 :                 break;
    2599           13 :             case SumoXMLNodeType::ZIPPER:
    2600           13 :                 ++numZipperJunctions;
    2601           13 :                 break;
    2602            0 :             case SumoXMLNodeType::DISTRICT:
    2603            0 :                 ++numDistrictJunctions;
    2604            0 :                 break;
    2605          165 :             case SumoXMLNodeType::RAIL_CROSSING:
    2606          165 :                 ++numRailCrossing;
    2607          165 :                 break;
    2608         1375 :             case SumoXMLNodeType::RAIL_SIGNAL:
    2609         1375 :                 ++numRailSignals;
    2610         1375 :                 break;
    2611              :             case SumoXMLNodeType::UNKNOWN:
    2612              :                 // should not happen
    2613              :                 break;
    2614              :             default:
    2615              :                 break;
    2616              :         }
    2617              :     }
    2618         1700 :     WRITE_MESSAGE(TL(" Node type statistics:"));
    2619         3400 :     WRITE_MESSAGE("  Unregulated junctions       : " + toString(numUnregulatedJunctions));
    2620         1700 :     if (numDeadEndJunctions > 0) {
    2621         3336 :         WRITE_MESSAGE("  Dead-end junctions          : " + toString(numDeadEndJunctions));
    2622              :     }
    2623         3400 :     WRITE_MESSAGE("  Priority junctions          : " + toString(numPriorityJunctions));
    2624         3400 :     WRITE_MESSAGE("  Right-before-left junctions : " + toString(numRightBeforeLeftJunctions));
    2625         1700 :     if (numLeftBeforeRightJunctions > 0) {
    2626            6 :         WRITE_MESSAGE("  Left-before-right junctions      : " + toString(numLeftBeforeRightJunctions));
    2627              :     }
    2628         1700 :     if (numTrafficLightJunctions > 0) {
    2629         1896 :         WRITE_MESSAGE("  Traffic light junctions      : " + toString(numTrafficLightJunctions));
    2630              :     }
    2631         1700 :     if (numAllWayStopJunctions > 0) {
    2632            3 :         WRITE_MESSAGE("  All-way stop junctions      : " + toString(numAllWayStopJunctions));
    2633              :     }
    2634         1700 :     if (numZipperJunctions > 0) {
    2635           30 :         WRITE_MESSAGE("  Zipper-merge junctions      : " + toString(numZipperJunctions));
    2636              :     }
    2637         1700 :     if (numRailCrossing > 0) {
    2638           72 :         WRITE_MESSAGE("  Rail crossing junctions      : " + toString(numRailCrossing));
    2639              :     }
    2640         1700 :     if (numRailSignals > 0) {
    2641          135 :         WRITE_MESSAGE("  Rail signal junctions      : " + toString(numRailSignals));
    2642              :     }
    2643         1700 :     if (numDistrictJunctions > 0) {
    2644            0 :         WRITE_MESSAGE("  District junctions      : " + toString(numDistrictJunctions));
    2645              :     }
    2646              :     const GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
    2647         1700 :     WRITE_MESSAGE(TL(" Network boundaries:"));
    2648         3400 :     WRITE_MESSAGE("  Original boundary  : " + toString(geoConvHelper.getOrigBoundary()));
    2649         3400 :     WRITE_MESSAGE("  Applied offset     : " + toString(geoConvHelper.getOffsetBase()));
    2650         3400 :     WRITE_MESSAGE("  Converted boundary : " + toString(geoConvHelper.getConvBoundary()));
    2651         1700 :     WRITE_MESSAGE(TL("-----------------------------------------------------"));
    2652         1700 : }
    2653              : 
    2654              : 
    2655              : std::vector<std::string>
    2656           39 : NBNodeCont::getAllNames() const {
    2657              :     std::vector<std::string> ret;
    2658         8718 :     for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
    2659         8679 :         ret.push_back((*i).first);
    2660              :     }
    2661           39 :     return ret;
    2662            0 : }
    2663              : 
    2664              : 
    2665              : void
    2666            0 : NBNodeCont::addPrefix(const std::string& prefix) {
    2667              :     // make a copy of node containers
    2668              :     const auto nodeContainerCopy = myNodes;
    2669              :     myNodes.clear();
    2670            0 :     for (const auto& node : nodeContainerCopy) {
    2671            0 :         node.second->setID(prefix + node.second->getID());
    2672            0 :         myNodes[node.second->getID()] = node.second;
    2673              :     }
    2674            0 : }
    2675              : 
    2676              : 
    2677              : void
    2678           74 : NBNodeCont::rename(NBNode* node, const std::string& newID) {
    2679              :     if (myNodes.count(newID) != 0) {
    2680            0 :         throw ProcessError(TLF("Attempt to rename node using existing id '%'", newID));
    2681              :     }
    2682              :     myNodes.erase(node->getID());
    2683           74 :     node->setID(newID);
    2684           74 :     myNodes[newID] = node;
    2685           74 : }
    2686              : 
    2687              : 
    2688              : void
    2689           54 : NBNodeCont::discardTrafficLights(NBTrafficLightLogicCont& tlc, bool geometryLike, bool guessSignals) {
    2690        19015 :     for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
    2691        18961 :         NBNode* node = i->second;
    2692        18961 :         if (node->isTLControlled() && (!geometryLike || node->geometryLike())) {
    2693              :             // make a copy of tldefs
    2694              :             const std::set<NBTrafficLightDefinition*> tldefs = node->getControllingTLS();
    2695          940 :             if (geometryLike && (*tldefs.begin())->getNodes().size() > 1) {
    2696              :                 // do not remove joined tls when only removing geometry-like tls
    2697            1 :                 continue;
    2698              :             }
    2699          939 :             if (node->getCrossings().size() > 0) {
    2700              :                 // keep controlled pedestrian crossings
    2701            5 :                 continue;
    2702              :             }
    2703          934 :             if (guessSignals && node->geometryLike()) {
    2704              :                 // record signal location
    2705         2117 :                 for (NBEdge* edge : node->getOutgoingEdges()) {
    2706              :                     edge->setSignalPosition(node->getPosition(), nullptr);
    2707              : #ifdef DEBUG_GUESSSIGNALS
    2708              :                     std::cout << "   discard-simple " << node->getID() << "  edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
    2709              : #endif
    2710              :                 }
    2711              :             }
    2712         1869 :             for (std::set<NBTrafficLightDefinition*>::const_iterator it = tldefs.begin(); it != tldefs.end(); ++it) {
    2713          935 :                 NBTrafficLightDefinition* tlDef = *it;
    2714          935 :                 node->removeTrafficLight(tlDef);
    2715          935 :                 tlc.extract(tlDef);
    2716              :             }
    2717          934 :             SumoXMLNodeType newType = NBNodeTypeComputer::isRailwayNode(node) ? SumoXMLNodeType::RAIL_SIGNAL : SumoXMLNodeType::UNKNOWN;
    2718          934 :             node->reinit(node->getPosition(), newType);
    2719              :         }
    2720              :     }
    2721           54 : }
    2722              : 
    2723              : 
    2724              : void
    2725            1 : NBNodeCont::discardRailSignals() {
    2726           35 :     for (auto& item : myNodes) {
    2727           34 :         NBNode* node = item.second;
    2728           34 :         if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
    2729            5 :             node->reinit(node->getPosition(), SumoXMLNodeType::PRIORITY);
    2730              :         }
    2731              :     }
    2732            1 : }
    2733              : 
    2734              : 
    2735              : int
    2736         1704 : NBNodeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBTrafficLightLogicCont& tlc) {
    2737         1704 :     bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.node-start");
    2738         1704 :     if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
    2739              :         return 0;
    2740              :     }
    2741              :     std::vector<std::string> avoid;
    2742           33 :     if (startGiven) {
    2743            6 :         avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.node-start") - 1));
    2744              :     } else {
    2745           30 :         avoid = getAllNames();
    2746              :     }
    2747              :     std::set<std::string> reserve;
    2748           33 :     if (reservedIDs) {
    2749            4 :         NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "node:", reserve); // backward compatibility
    2750            4 :         NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "junction:", reserve); // selection format
    2751            2 :         avoid.insert(avoid.end(), reserve.begin(), reserve.end());
    2752              :     }
    2753           66 :     IDSupplier idSupplier("", avoid);
    2754              :     NodeSet toChange;
    2755         8202 :     for (NodeCont::iterator it = myNodes.begin(); it != myNodes.end(); it++) {
    2756         8169 :         if (startGiven) {
    2757           36 :             toChange.insert(it->second);
    2758           36 :             continue;
    2759              :         }
    2760         8133 :         if (numericaIDs) {
    2761              :             try {
    2762         8063 :                 StringUtils::toLong(it->first);
    2763          287 :             } catch (NumberFormatException&) {
    2764          287 :                 toChange.insert(it->second);
    2765          287 :             }
    2766              :         }
    2767         8133 :         if (reservedIDs && reserve.count(it->first) > 0) {
    2768            2 :             toChange.insert(it->second);
    2769              :         }
    2770              :     }
    2771           66 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    2772          358 :     for (NBNode* node : toChange) {
    2773              :         myNodes.erase(node->getID());
    2774              :     }
    2775          358 :     for (NBNode* node : toChange) {
    2776          853 :         if (origNames && node->getParameter(SUMO_PARAM_ORIGID) == "") {
    2777          248 :             node->setParameter(SUMO_PARAM_ORIGID, node->getID());
    2778              :         }
    2779          650 :         node->setID(idSupplier.getNext());
    2780          380 :         for (NBTrafficLightDefinition* tlDef : node->getControllingTLS()) {
    2781           55 :             tlc.rename(tlDef, node->getID());
    2782              :         }
    2783          325 :         myNodes[node->getID()] = node;
    2784              :     }
    2785           33 :     if (prefix.empty()) {
    2786           25 :         return (int)toChange.size();
    2787              :     } else {
    2788              :         int renamed = 0;
    2789              :         // make a copy because we will modify the map
    2790              :         auto oldNodes = myNodes;
    2791           83 :         for (auto item : oldNodes) {
    2792          150 :             if (!StringUtils::startsWith(item.first, prefix)) {
    2793           74 :                 rename(item.second, prefix + item.first);
    2794           80 :                 for (NBTrafficLightDefinition* tlDef : item.second->getControllingTLS()) {
    2795           12 :                     if (!StringUtils::startsWith(tlDef->getID(), prefix)) {
    2796            8 :                         tlc.rename(tlDef, prefix + tlDef->getID());
    2797              :                     }
    2798              :                 }
    2799           74 :                 renamed++;
    2800              :             }
    2801              :         }
    2802              :         return renamed;
    2803              :     }
    2804           66 : }
    2805              : 
    2806              : 
    2807              : int
    2808            2 : NBNodeCont::guessFringe() {
    2809              :     // guess outer fringe by topology and being on the pareto-boundary
    2810              :     NodeSet topRightFront;
    2811              :     NodeSet topLeftFront;
    2812              :     NodeSet bottomRightFront;
    2813              :     NodeSet bottomLeftFront;
    2814           67 :     for (const auto& item : myNodes) {
    2815           65 :         paretoCheck(item.second, topRightFront, 1, 1);
    2816           65 :         paretoCheck(item.second, topLeftFront, -1, 1);
    2817           65 :         paretoCheck(item.second, bottomRightFront, 1, -1);
    2818           65 :         paretoCheck(item.second, bottomLeftFront, -1, -1);
    2819              :     }
    2820              :     NodeSet front;
    2821            2 :     front.insert(topRightFront.begin(), topRightFront.end());
    2822            2 :     front.insert(topLeftFront.begin(), topLeftFront.end());
    2823            2 :     front.insert(bottomRightFront.begin(), bottomRightFront.end());
    2824            2 :     front.insert(bottomLeftFront.begin(), bottomLeftFront.end());
    2825              :     int numFringe = 0;
    2826           19 :     for (NBNode* n : front) {
    2827           17 :         const int in = (int)n->getIncomingEdges().size();
    2828           17 :         const int out = (int)n->getOutgoingEdges().size();
    2829           17 :         if ((in <= 1 && out <= 1) &&
    2830           12 :                 (in == 0 || out == 0
    2831            8 :                  || n->getIncomingEdges().front()->isTurningDirectionAt(n->getOutgoingEdges().front()))) {
    2832              :             n->setFringeType(FringeType::OUTER);
    2833            9 :             numFringe++;
    2834              :         }
    2835              :     }
    2836              :     // guess outer fringe by topology and speed
    2837            4 :     const double speedThreshold = OptionsCont::getOptions().getFloat("fringe.guess.speed-threshold");
    2838           67 :     for (const auto& item : myNodes) {
    2839           65 :         NBNode* n = item.second;
    2840           17 :         if (front.count(n) != 0) {
    2841           17 :             continue;
    2842              :         }
    2843           48 :         if (n->getEdges().size() == 1 && n->getEdges().front()->getSpeed() > speedThreshold) {
    2844              :             n->setFringeType(FringeType::OUTER);
    2845            2 :             numFringe++;
    2846              :         }
    2847              :     }
    2848            2 :     return numFringe;
    2849              : }
    2850              : 
    2851              : 
    2852              : void
    2853          260 : NBNodeCont::paretoCheck(NBNode* node, NodeSet& frontier, int xSign, int ySign) {
    2854          260 :     const double x = node->getPosition().x() * xSign;
    2855          260 :     const double y = node->getPosition().y() * ySign;
    2856              :     std::vector<NBNode*> dominated;
    2857          556 :     for (NBNode* fn : frontier) {
    2858          484 :         const double x2 = fn->getPosition().x() * xSign;
    2859          484 :         const double y2 = fn->getPosition().y() * ySign;
    2860          484 :         if (x2 >= x && y2 >= y) {
    2861          188 :             return;
    2862          296 :         } else if (x2 <= x && y2 <= y) {
    2863           52 :             dominated.push_back(fn);
    2864              :         }
    2865              :     }
    2866              :     frontier.insert(node);
    2867          124 :     for (NBNode* r : dominated) {
    2868              :         frontier.erase(r);
    2869              :     }
    2870          260 : }
    2871              : 
    2872              : 
    2873              : void
    2874         1739 : NBNodeCont::applyConditionalDefaults() {
    2875        57944 :     for (const auto& item : myNodes) {
    2876        56205 :         NBNode* n = item.second;
    2877        56205 :         if (n->isTLControlled() && n->getRightOfWay() == RightOfWay::DEFAULT) {
    2878              :             bool hasNEMA = false;
    2879         5425 :             for (NBTrafficLightDefinition* tl : n->getControllingTLS()) {
    2880         2726 :                 if (tl->getType() == TrafficLightType::NEMA) {
    2881              :                     hasNEMA = true;
    2882              :                     break;
    2883              :                 }
    2884              :             }
    2885         2712 :             if (hasNEMA) {
    2886              :                 // NEMA controller defaults to allway_stop behavior when switched off
    2887              :                 n->setRightOfWay(RightOfWay::ALLWAYSTOP);
    2888              :             }
    2889              :         }
    2890              :     }
    2891         1739 : }
    2892              : 
    2893              : 
    2894              : bool
    2895         1704 : NBNodeCont::resetNodeShapes() {
    2896              :     bool hadShapes = false;
    2897        62995 :     for (const auto& item : myNodes) {
    2898        61291 :         if (item.second->getShape().size() > 0 && !item.second->hasCustomShape()) {
    2899              :             hadShapes = true;
    2900              :             item.second->resetShape();
    2901              :         }
    2902              :     }
    2903         1704 :     return hadShapes;
    2904              : }
    2905              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1