LCOV - code coverage report
Current view: top level - src/netbuild - NBEdgeCont.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.6 % 1069 990
Test Date: 2026-03-02 16:00:03 Functions: 97.3 % 73 71

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    NBEdgeCont.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @date    Tue, 20 Nov 2001
      20              : ///
      21              : // Storage for edges, including some functionality operating on multiple edges
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <vector>
      26              : #include <string>
      27              : #include <cassert>
      28              : #include <algorithm>
      29              : #include <cmath>
      30              : #include <utils/geom/Boundary.h>
      31              : #include <utils/geom/GeomHelper.h>
      32              : #include <utils/geom/GeoConvHelper.h>
      33              : #include <utils/geom/GeomConvHelper.h>
      34              : #include <utils/common/MsgHandler.h>
      35              : #include <utils/common/ToString.h>
      36              : #include <utils/common/StringUtils.h>
      37              : #include <utils/common/IDSupplier.h>
      38              : #include <utils/common/StringUtils.h>
      39              : #include <utils/common/StringTokenizer.h>
      40              : #include <utils/common/UtilExceptions.h>
      41              : #include <utils/iodevices/OutputDevice.h>
      42              : #include <utils/options/OptionsCont.h>
      43              : #include "NBNetBuilder.h"
      44              : #include "NBEdgeCont.h"
      45              : #include "NBNodeCont.h"
      46              : #include "NBPTLineCont.h"
      47              : #include "NBPTStop.h"
      48              : #include "NBHelpers.h"
      49              : #include "NBCont.h"
      50              : #include "NBTrafficLightLogicCont.h"
      51              : #include "NBDistrictCont.h"
      52              : #include "NBTypeCont.h"
      53              : 
      54              : #define JOIN_TRAM_MAX_ANGLE 10
      55              : #define JOIN_TRAM_MIN_LENGTH 3
      56              : 
      57              : //#define DEBUG_GUESS_ROUNDABOUT
      58              : //#define DEBUG_JOIN_TRAM
      59              : #define DEBUG_EDGE_ID ""
      60              : 
      61              : // ===========================================================================
      62              : // method definitions
      63              : // ===========================================================================
      64         2141 : NBEdgeCont::NBEdgeCont(NBTypeCont& tc) :
      65         2141 :     myTypeCont(tc),
      66         2141 :     myVehicleClasses2Keep(0),
      67         2141 :     myVehicleClasses2Remove(0),
      68         2141 :     myNeedGeoTransformedPruningBoundary(false) {
      69         2141 : }
      70              : 
      71              : 
      72         2141 : NBEdgeCont::~NBEdgeCont() {
      73         2141 :     clear();
      74         2141 : }
      75              : 
      76              : 
      77              : void
      78         2141 : NBEdgeCont::applyOptions(OptionsCont& oc) {
      79              :     // set edges dismiss/accept options
      80         2141 :     myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
      81         4182 :     myRemoveEdgesAfterLoading = oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload");
      82              :     // we possibly have to load the edges to keep/remove
      83         4282 :     if (oc.isSet("keep-edges.input-file")) {
      84            4 :         NBHelpers::loadEdgesFromFile(oc.getString("keep-edges.input-file"), myEdges2Keep);
      85              :     }
      86         4282 :     if (oc.isSet("remove-edges.input-file")) {
      87            2 :         NBHelpers::loadEdgesFromFile(oc.getString("remove-edges.input-file"), myEdges2Remove);
      88              :     }
      89         4282 :     if (oc.isSet("keep-edges.explicit")) {
      90           22 :         const std::vector<std::string> edges = oc.getStringVector("keep-edges.explicit");
      91              :         myEdges2Keep.insert(edges.begin(), edges.end());
      92           11 :     }
      93         4282 :     if (oc.isSet("remove-edges.explicit")) {
      94           28 :         const std::vector<std::string> edges = oc.getStringVector("remove-edges.explicit");
      95              :         myEdges2Remove.insert(edges.begin(), edges.end());
      96           14 :     }
      97         4182 :     if (oc.exists("keep-edges.by-vclass") && oc.isSet("keep-edges.by-vclass")) {
      98           72 :         myVehicleClasses2Keep = parseVehicleClasses(oc.getStringVector("keep-edges.by-vclass"));
      99              :     }
     100         4182 :     if (oc.exists("remove-edges.by-vclass") && oc.isSet("remove-edges.by-vclass")) {
     101           39 :         myVehicleClasses2Remove = parseVehicleClasses(oc.getStringVector("remove-edges.by-vclass"));
     102              :     }
     103         4168 :     if (oc.exists("keep-edges.by-type") && oc.isSet("keep-edges.by-type")) {
     104           14 :         const std::vector<std::string> types = oc.getStringVector("keep-edges.by-type");
     105              :         myTypes2Keep.insert(types.begin(), types.end());
     106            7 :     }
     107         4168 :     if (oc.exists("remove-edges.by-type") && oc.isSet("remove-edges.by-type")) {
     108           18 :         const std::vector<std::string> types = oc.getStringVector("remove-edges.by-type");
     109              :         myTypes2Remove.insert(types.begin(), types.end());
     110            9 :     }
     111              : 
     112         4258 :     if (oc.isSet("keep-edges.in-boundary") || oc.isSet("keep-edges.in-geo-boundary")) {
     113              : 
     114           14 :         std::string polyPlainString = oc.getValueString(oc.isSet("keep-edges.in-boundary") ?
     115           18 :                                       "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
     116              :         // try interpreting the boundary like shape attribute with spaces
     117           14 :         bool ok = true;
     118           17 :         PositionVector boundaryShape = GeomConvHelper::parseShapeReporting(polyPlainString, "pruning-boundary", 0, ok, false, false);
     119           14 :         if (ok) {
     120            2 :             if (boundaryShape.size() < 2) {
     121            2 :                 throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
     122            1 :             } else if (boundaryShape.size() == 2) {
     123              :                 // prunning boundary (box)
     124            0 :                 myPruningBoundary.push_back(boundaryShape[0]);
     125            0 :                 myPruningBoundary.push_back(Position(boundaryShape[1].x(), boundaryShape[0].y()));
     126            0 :                 myPruningBoundary.push_back(boundaryShape[1]);
     127            0 :                 myPruningBoundary.push_back(Position(boundaryShape[0].x(), boundaryShape[1].y()));
     128              :             } else {
     129              :                 myPruningBoundary = boundaryShape;
     130              :             }
     131              :         } else {
     132              :             // maybe positions are separated by ',' instead of ' '
     133           28 :             std::vector<std::string> polyS = oc.getStringVector(oc.isSet("keep-edges.in-boundary") ?
     134           12 :                                              "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
     135              :             std::vector<double> poly;
     136           83 :             for (std::vector<std::string>::iterator i = polyS.begin(); i != polyS.end(); ++i) {
     137           72 :                 poly.push_back(StringUtils::toDouble((*i))); // !!! may throw something anyhow...
     138              :             }
     139           11 :             if (poly.size() < 4) {
     140            0 :                 throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
     141           11 :             } else if (poly.size() % 2 != 0) {
     142            2 :                 throw ProcessError(TL("Invalid boundary: malformed coordinate"));
     143           10 :             } else if (poly.size() == 4) {
     144              :                 // prunning boundary (box)
     145            8 :                 myPruningBoundary.push_back(Position(poly[0], poly[1]));
     146            8 :                 myPruningBoundary.push_back(Position(poly[2], poly[1]));
     147            8 :                 myPruningBoundary.push_back(Position(poly[2], poly[3]));
     148            8 :                 myPruningBoundary.push_back(Position(poly[0], poly[3]));
     149              :             } else {
     150           19 :                 for (std::vector<double>::iterator j = poly.begin(); j != poly.end();) {
     151           17 :                     double x = *j++;
     152           17 :                     double y = *j++;
     153           17 :                     myPruningBoundary.push_back(Position(x, y));
     154              :                 }
     155              :             }
     156           14 :         }
     157           11 :         myNeedGeoTransformedPruningBoundary = oc.isSet("keep-edges.in-geo-boundary");
     158           14 :     }
     159         2131 : }
     160              : 
     161              : 
     162              : void
     163         2141 : NBEdgeCont::clear() {
     164       106019 :     for (const auto& i : myEdges) {
     165       103878 :         delete i.second;
     166              :     }
     167              :     myEdges.clear();
     168        11397 :     for (const auto& i : myExtractedEdges) {
     169         9256 :         delete i.second;
     170              :     }
     171              :     myExtractedEdges.clear();
     172         2161 :     for (NBEdge* const e : myEdgeCemetery) {
     173           20 :         delete e;
     174              :     }
     175              :     myEdgeCemetery.clear();
     176         2141 : }
     177              : 
     178              : 
     179              : 
     180              : // ----- edge access methods
     181              : bool
     182       121276 : NBEdgeCont::insert(NBEdge* edge, bool ignorePrunning) {
     183       121276 :     if (myEdges.count(edge->getID()) != 0) {
     184            0 :         return false;
     185              :     }
     186       121276 :     if (!ignorePrunning && ignoreFilterMatch(edge)) {
     187         7734 :         edge->getFromNode()->removeEdge(edge);
     188         7734 :         edge->getToNode()->removeEdge(edge);
     189         7734 :         myIgnoredEdges.insert(edge->getID());
     190         7734 :         delete edge;
     191              :     } else {
     192       113542 :         OptionsCont& oc = OptionsCont::getOptions();
     193       211665 :         if (oc.exists("dismiss-vclasses") && oc.getBool("dismiss-vclasses")) {
     194          200 :             edge->dismissVehicleClassInformation();
     195              :         }
     196       113542 :         myEdges[edge->getID()] = edge;
     197              :     }
     198              :     return true;
     199              : }
     200              : 
     201              : 
     202              : bool
     203       154395 : NBEdgeCont::ignoreFilterMatch(NBEdge* edge) {
     204       154395 :     if (!myRemoveEdgesAfterLoading) {
     205              :         // check whether the edge is a named edge to keep
     206       154282 :         if (myEdges2Keep.size() != 0) {
     207          142 :             if (myEdges2Keep.count(edge->getID()) == 0) {
     208              :                 // explicit whitelisting may be combined additively with other filters
     209           63 :                 if (myVehicleClasses2Keep == 0 && myVehicleClasses2Remove == 0
     210           63 :                         && myTypes2Keep.size() == 0 && myTypes2Remove.size() == 0
     211          104 :                         && myPruningBoundary.size() == 0) {
     212              :                     return true;
     213              :                 }
     214              :             } else {
     215              :                 // explicit whitelisting overrides other filters
     216           79 :                 return false;
     217              :             }
     218              :         }
     219              :         // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
     220       154162 :         if (edge->getSpeed() < myEdgesMinSpeed) {
     221              :             return true;
     222              :         }
     223              :         // check whether the edge shall be removed because it does not allow any of the wished classes
     224       154156 :         if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
     225              :             return true;
     226              :         }
     227              :         // check whether the edge shall be removed due to allowing unwished classes only
     228       149067 :         if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
     229              :             return true;
     230              :         }
     231              :     }
     232              :     // check whether the edge is a named edge to remove
     233       148861 :     if (myEdges2Remove.size() != 0) {
     234          292 :         if (myEdges2Remove.count(edge->getID()) != 0) {
     235           33 :             return true;
     236              :         }
     237              :     }
     238              :     // check whether the edge shall be removed because it does not have one of the requested types
     239       148828 :     if (myTypes2Keep.size() != 0) {
     240              :         if (myTypes2Keep.count(edge->getTypeID()) == 0) {
     241         1443 :             return true;
     242              :         }
     243              :     }
     244              :     // check whether the edge shall be removed because it has one of the forbidden types
     245       147385 :     if (myTypes2Remove.size() != 0) {
     246              :         if (myTypes2Remove.count(edge->getTypeID()) > 0) {
     247           52 :             return true;
     248              :         }
     249              :     }
     250              :     // check whether the edge is within the pruning boundary
     251       147333 :     if (myPruningBoundary.size() != 0) {
     252          987 :         if (myNeedGeoTransformedPruningBoundary) {
     253            4 :             if (GeoConvHelper::getProcessing().usingGeoProjection()) {
     254            2 :                 NBNetBuilder::transformCoordinates(myPruningBoundary, false);
     255            2 :             } else if (GeoConvHelper::getLoaded().usingGeoProjection()) {
     256              :                 // XXX what if input file with different projections are loaded?
     257           10 :                 for (int i = 0; i < (int) myPruningBoundary.size(); i++) {
     258            8 :                     GeoConvHelper::getLoaded().x2cartesian_const(myPruningBoundary[i]);
     259              :                 }
     260              :             } else {
     261            0 :                 WRITE_ERROR(TL("Cannot prune edges using a geo-boundary because no projection has been loaded"));
     262              :             }
     263            4 :             myNeedGeoTransformedPruningBoundary = false;
     264              :         }
     265          987 :         if (!(edge->getGeometry().getBoxBoundary().grow(POSITION_EPS).overlapsWith(myPruningBoundary))) {
     266              :             return true;
     267          691 :         } else if (!(edge->getGeometry().partialWithin(myPruningBoundary, 2 * POSITION_EPS) || edge->getGeometry().intersects(myPruningBoundary))) {
     268              :             // a more detailed check is necessary because the bounding box may be much bigger than the edge
     269              :             // @note: overlapsWith implicitly closes the edge shape but this is not wanted here
     270              :             return true;
     271              :         }
     272              :     }
     273       147035 :     if (myTypeCont.knows(edge->getTypeID()) && myTypeCont.getEdgeTypeShallBeDiscarded(edge->getTypeID())) {
     274              :         return true;
     275              :     }
     276              :     return false;
     277              : }
     278              : 
     279              : 
     280              : NBEdge*
     281       342234 : NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
     282              :     EdgeCont::const_iterator i = myEdges.find(id);
     283       342234 :     if (i == myEdges.end()) {
     284        90969 :         if (retrieveExtracted) {
     285              :             i = myExtractedEdges.find(id);
     286        18735 :             if (i == myExtractedEdges.end()) {
     287              :                 return nullptr;
     288              :             }
     289              :         } else {
     290              :             return nullptr;
     291              :         }
     292              :     }
     293       251303 :     return (*i).second;
     294              : }
     295              : 
     296              : // FIXME: This can't work
     297              : /*
     298              : NBEdge*
     299              : NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
     300              :     NBEdge* edge = retrieve(id);
     301              :     if (edge == 0) {
     302              :         return 0;
     303              :     }
     304              :     const EdgeVector* candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
     305              :     while (candidates->size() == 1) {
     306              :         const std::string& nextID = candidates->front()->getID();
     307              :         if (nextID.find(id) != 0 || nextID.size() <= id.size() + 1 || (nextID[id.size()] != '.' && nextID[id.size()] != '-')) {
     308              :             break;
     309              :         }
     310              :         edge = candidates->front();
     311              :         candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
     312              :     }
     313              :     return edge;
     314              : }*/
     315              : 
     316              : NBEdge*
     317          104 : NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
     318          104 :     NBEdge* edge = retrieve(id);
     319          104 :     if (edge != nullptr) {
     320              :         return edge;
     321              :     }
     322              :     // NOTE: (TODO) for multiply split edges (e.g. 15[0][0]) one could try recursion
     323            0 :     if ((retrieve(id + "[0]") != nullptr) && (retrieve(id + "[1]") != nullptr)) {
     324              :         // Edge was split during the netbuilding process
     325            0 :         if (downstream) {
     326            0 :             return retrieve(id + "[1]");
     327              :         } else {
     328            0 :             return retrieve(id + "[0]");
     329              :         }
     330              :     }
     331              :     return edge;
     332              : }
     333              : 
     334              : 
     335              : NBEdge*
     336         2402 : NBEdgeCont::retrievePossiblySplit(const std::string& id, const std::string& hint, bool incoming) const {
     337              :     // try to retrieve using the given name (iterative)
     338         2402 :     NBEdge* edge = retrieve(id);
     339         2402 :     if (edge != nullptr) {
     340              :         return edge;
     341              :     }
     342              :     // now, we did not find it; we have to look over all possibilities
     343              :     EdgeVector hints;
     344              :     // check whether at least the hint was not splitted
     345          710 :     NBEdge* hintedge = retrieve(hint);
     346          710 :     if (hintedge == nullptr) {
     347          132 :         hints = getGeneratedFrom(hint);
     348              :     } else {
     349          578 :         hints.push_back(hintedge);
     350              :     }
     351          710 :     EdgeVector candidates = getGeneratedFrom(id);
     352          965 :     for (const NBEdge* const currHint : hints) {
     353         3555 :         for (NBEdge* const poss_searched : candidates) {
     354         3300 :             const NBNode* const node = incoming ? poss_searched->myTo : poss_searched->myFrom;
     355         3300 :             const EdgeVector& cont = incoming ? node->getOutgoingEdges() : node->getIncomingEdges();
     356         3300 :             if (find(cont.begin(), cont.end(), currHint) != cont.end()) {
     357              :                 return poss_searched;
     358              :             }
     359              :         }
     360              :     }
     361           33 :     return nullptr;
     362          710 : }
     363              : 
     364              : 
     365              : NBEdge*
     366          261 : NBEdgeCont::retrievePossiblySplit(const std::string& id, double pos) const {
     367              :     // check whether the edge was not split, yet
     368          261 :     NBEdge* edge = retrieve(id);
     369          261 :     if (edge != nullptr) {
     370              :         return edge;
     371              :     }
     372              :     int maxLength = 0;
     373          111 :     std::string tid = id + "[";
     374        21423 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     375        21312 :         if ((*i).first.find(tid) == 0) {
     376          660 :             maxLength = MAX2(maxLength, (int)(*i).first.length());
     377              :         }
     378              :     }
     379              :     // find the part of the edge which matches the position
     380              :     double seen = 0;
     381              :     std::vector<std::string> names;
     382          111 :     names.push_back(id + "[1]");
     383          111 :     names.push_back(id + "[0]");
     384          576 :     while (names.size() > 0) {
     385              :         // retrieve the first subelement (to follow)
     386              :         std::string cid = names.back();
     387              :         names.pop_back();
     388          558 :         edge = retrieve(cid);
     389              :         // The edge was splitted; check its subparts within the
     390              :         //  next step
     391          558 :         if (edge == nullptr) {
     392          231 :             if ((int)cid.length() + 3 < maxLength) {
     393          213 :                 names.push_back(cid + "[1]");
     394          426 :                 names.push_back(cid + "[0]");
     395              :             }
     396              :         }
     397              :         // an edge with the name was found,
     398              :         //  check whether the position lies within it
     399              :         else {
     400          327 :             seen += edge->getLength();
     401          327 :             if (seen >= pos) {
     402              :                 return edge;
     403              :             }
     404              :         }
     405              :     }
     406              :     return nullptr;
     407          111 : }
     408              : 
     409              : 
     410              : void
     411          363 : NBEdgeCont::erase(NBDistrictCont& dc, NBEdge* edge) {
     412          363 :     extract(dc, edge);
     413          363 :     delete edge;
     414          363 : }
     415              : 
     416              : 
     417              : void
     418         9666 : NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
     419         9666 :     if (remember) {
     420         9278 :         const auto& prevExtracted = myExtractedEdges.find(edge->getID());
     421         9278 :         if (prevExtracted != myExtractedEdges.end()) {
     422           22 :             if (edge != prevExtracted->second) {
     423           20 :                 myEdgeCemetery.insert(prevExtracted->second);
     424           20 :                 prevExtracted->second = edge;
     425              :             }
     426              :         } else {
     427         9256 :             myExtractedEdges[edge->getID()] = edge;
     428              :         }
     429              :     }
     430         9666 :     myEdges.erase(edge->getID());
     431         9666 :     edge->myFrom->removeEdge(edge);
     432         9666 :     edge->myTo->removeEdge(edge);
     433         9666 :     dc.removeFromSinksAndSources(edge);
     434         9666 : }
     435              : 
     436              : 
     437              : void
     438          193 : NBEdgeCont::rename(NBEdge* edge, const std::string& newID) {
     439              :     if (myEdges.count(newID) != 0) {
     440            0 :         throw ProcessError(TLF("Attempt to rename edge using existing id '%'", newID));
     441              :     }
     442          193 :     myEdges.erase(edge->getID());
     443          193 :     edge->setID(newID);
     444          193 :     myEdges[newID] = edge;
     445              :     // update oppositeID
     446          193 :     if (edge->getLanes().back().oppositeID != "") {
     447            0 :         NBEdge* oppo = retrieve(SUMOXMLDefinitions::getEdgeIDFromLane(edge->getLanes().back().oppositeID));
     448            0 :         if (oppo != nullptr) {
     449            0 :             oppo->getLaneStruct(oppo->getNumLanes() - 1).oppositeID = edge->getLaneID(edge->getNumLanes() - 1);
     450              :         }
     451              :     }
     452          193 : }
     453              : 
     454              : 
     455              : // ----- explicit edge manipulation methods
     456              : 
     457              : void
     458        19052 : NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
     459              :                           NBNodeCont& nc, NBDistrictCont& dc, NBTrafficLightLogicCont& tlc) {
     460        19052 :     if (splits.empty()) {
     461        18815 :         return;
     462              :     }
     463          239 :     const std::string origID = e->getID();
     464          239 :     sort(splits.begin(), splits.end(), split_sorter());
     465              :     int maxNumLanes = e->getNumLanes();
     466              :     // compute the node positions and sort the lanes
     467          562 :     for (Split& split : splits) {
     468          323 :         sort(split.lanes.begin(), split.lanes.end());
     469          323 :         maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
     470              :     }
     471              :     // split the edge
     472              :     std::vector<int> currLanes;
     473          684 :     for (int l = 0; l < e->getNumLanes(); ++l) {
     474          445 :         currLanes.push_back(l);
     475              :     }
     476          239 :     if (e->getNumLanes() != (int)splits.back().lanes.size()) {
     477              :         // invalidate traffic light definitions loaded from a SUMO network
     478          178 :         e->getToNode()->invalidateTLS(tlc, true, true);
     479              :         // if the number of lanes changes the connections should be
     480              :         // recomputed
     481          178 :         e->invalidateConnections(true);
     482              :     }
     483              : 
     484          239 :     std::string firstID = "";
     485              :     double seen = 0;
     486          560 :     for (const Split& exp : splits) {
     487              :         assert(exp.lanes.size() != 0);
     488          603 :         if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
     489          279 :             nc.insert(exp.node);
     490          279 :             nc.markAsSplit(exp.node);
     491              :             //  split the edge
     492          279 :             const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
     493          279 :             const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
     494          279 :             if (firstID == "") {
     495              :                 firstID = idBefore;
     496              :             }
     497          279 :             const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
     498          279 :                                     idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
     499          279 :             if (!ok) {
     500            6 :                 WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
     501              :                 return;
     502              :             }
     503          277 :             seen = exp.pos;
     504          277 :             std::vector<int> newLanes = exp.lanes;
     505          277 :             NBEdge* pe = retrieve(idBefore);
     506          277 :             NBEdge* ne = retrieve(idAfter);
     507              :             // reconnect lanes
     508          277 :             pe->invalidateConnections(true);
     509              :             //  new on right
     510          277 :             int rightMostP = currLanes[0];
     511          277 :             int rightMostN = newLanes[0];
     512          316 :             for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
     513           78 :                 pe->addLane2LaneConnection(0, ne, l, NBEdge::Lane2LaneInfoType::VALIDATED, true);
     514              :             }
     515              :             //  new on left
     516          277 :             int leftMostP = currLanes.back();
     517          277 :             int leftMostN = newLanes.back();
     518          450 :             for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
     519          346 :                 pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
     520              :             }
     521              :             //  all other connected
     522          989 :             for (int l = 0; l < maxNumLanes; ++l) {
     523          712 :                 if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
     524          226 :                     continue;
     525              :                 }
     526          486 :                 if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
     527           56 :                     continue;
     528              :                 }
     529          860 :                 pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
     530              :             }
     531              :             //  if there are edges at this node which are not connected
     532              :             //  we can assume that this split was attached to an
     533              :             //  existing node. Reset all connections to let the default
     534              :             //  algorithm recompute them
     535          277 :             if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1 || exp.node->getType() == SumoXMLNodeType::ZIPPER) {
     536           30 :                 for (NBEdge* in : exp.node->getIncomingEdges()) {
     537           20 :                     in->invalidateConnections(true);
     538              :                 }
     539              :             }
     540              :             // move to next
     541              :             e = ne;
     542          277 :             currLanes = newLanes;
     543          321 :         }  else if (exp.pos == 0) {
     544           43 :             const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
     545           43 :             if (laneCountDiff < 0) {
     546            1 :                 e->incLaneNo(-laneCountDiff);
     547              :             } else {
     548           42 :                 e->decLaneNo(laneCountDiff);
     549              :             }
     550           43 :             currLanes = exp.lanes;
     551              :             // invalidate traffic light definition loaded from a SUMO network
     552              :             // XXX it would be preferable to reconstruct the phase definitions heuristically
     553           43 :             e->getFromNode()->invalidateTLS(tlc, true, true);
     554           43 :             if (exp.speed != -1.) {
     555           43 :                 e->setSpeed(-1, exp.speed);
     556              :             }
     557              :         } else {
     558            3 :             WRITE_WARNINGF(TL("Split at '%' lies beyond the edge's length (edge '%')."), toString(exp.pos), origID);
     559              :         }
     560              :     }
     561              :     // patch lane offsets
     562          237 :     e = retrieve(firstID);
     563          237 :     if (e != nullptr) {
     564          234 :         if (splits.front().pos != 0) {
     565              :             // add a dummy split at the beginning to ensure correct offset
     566              :             Split start;
     567          193 :             start.pos = 0;
     568          544 :             for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
     569          351 :                 start.lanes.push_back(lane);
     570              :             }
     571          193 :             start.offset = splits.front().offset;
     572          193 :             start.offsetFactor = splits.front().offsetFactor;
     573          193 :             splits.insert(splits.begin(), start);
     574          193 :         }
     575          744 :         for (const Split& split : splits) {
     576          510 :             int maxLeft = split.lanes.back();
     577          510 :             double offset = split.offset;
     578          510 :             if (maxLeft < maxNumLanes) {
     579          510 :                 if (e->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
     580          425 :                     offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
     581              :                 } else {
     582           85 :                     offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
     583              :                 }
     584              :             }
     585          510 :             int maxRight = split.lanes.front();
     586          510 :             if (maxRight > 0 && e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
     587           19 :                 offset -= split.offsetFactor * SUMO_const_halfLaneWidth * maxRight;
     588              :             }
     589              :             //std::cout << " processSplits " << origID << " splitOffset=" << (*i).offset << " offset=" << offset << "\n";
     590          510 :             if (offset != 0) {
     591              :                 PositionVector g = e->getGeometry();
     592          269 :                 g.move2side(offset);
     593          269 :                 e->setGeometry(g);
     594          269 :             }
     595          510 :             if (e->getToNode()->getOutgoingEdges().size() != 0) {
     596          429 :                 e = e->getToNode()->getOutgoingEdges()[0];
     597              :             }
     598              :         }
     599              :     }
     600          239 : }
     601              : 
     602              : 
     603              : bool
     604          188 : NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node) {
     605          376 :     return splitAt(dc, edge, node, edge->getID() + "[0]", edge->getID() + "[1]",
     606          188 :                    (int) edge->myLanes.size(), (int) edge->myLanes.size());
     607              : }
     608              : 
     609              : 
     610              : bool
     611          468 : NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node,
     612              :                     const std::string& firstEdgeName,
     613              :                     const std::string& secondEdgeName,
     614              :                     int noLanesFirstEdge, int noLanesSecondEdge,
     615              :                     const double speed, const double friction,
     616              :                     const int changedLeft) {
     617              :     double pos;
     618          468 :     pos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
     619          468 :     if (pos <= 0) {
     620            3 :         pos = GeomHelper::nearest_offset_on_line_to_point2D(
     621            3 :                   edge->myFrom->getPosition(), edge->myTo->getPosition(),
     622              :                   node->getPosition());
     623              :     }
     624          468 :     if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
     625            3 :         return false;
     626              :     }
     627          465 :     return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
     628          465 :                    noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
     629              : }
     630              : 
     631              : 
     632              : bool
     633          781 : NBEdgeCont::splitAt(NBDistrictCont& dc,
     634              :                     NBEdge* edge, double pos, NBNode* node,
     635              :                     const std::string& firstEdgeName,
     636              :                     const std::string& secondEdgeName,
     637              :                     int noLanesFirstEdge, int noLanesSecondEdge,
     638              :                     const double speed, const double friction,
     639              :                     const int changedLeft) {
     640          781 :     if (firstEdgeName != edge->getID() && myEdges.count(firstEdgeName) != 0) {
     641            0 :         WRITE_ERRORF(TL("Could not insert edge '%' before split of edge '%'."), firstEdgeName, edge->getID());
     642            0 :         return false;
     643              :     }
     644          781 :     if (secondEdgeName == firstEdgeName || (secondEdgeName != edge->getID() && myEdges.count(secondEdgeName) != 0)) {
     645            6 :         WRITE_ERRORF(TL("Could not insert edge '%' after split of edge '%'."), secondEdgeName, edge->getID());
     646            2 :         return false;
     647              :     }
     648              :     // there must be at least some overlap between first and second edge
     649              :     assert(changedLeft > -((int)noLanesFirstEdge));
     650              :     assert(changedLeft < (int)noLanesSecondEdge);
     651              : 
     652              :     // build the new edges' geometries
     653              :     double geomPos = pos;
     654          779 :     if (edge->hasLoadedLength()) {
     655            7 :         geomPos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
     656            7 :         if (geomPos <= 0) {
     657            0 :             geomPos = pos * edge->getGeometry().length() / edge->getLoadedLength();
     658              :         }
     659              :     }
     660          779 :     std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
     661              :     // reduce inaccuracies and preserve bidi
     662         1558 :     if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
     663          624 :         geoms.first[-1] = node->getPosition();
     664          624 :         geoms.second[0] = node->getPosition();
     665              :     }
     666              :     // build and insert the edges
     667          779 :     NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
     668          779 :     NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
     669         1558 :     if (OptionsCont::getOptions().getBool("output.original-names")) {
     670           54 :         const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
     671           18 :         if (firstEdgeName != origID) {
     672           34 :             one->setOrigID(origID, false);
     673              :         }
     674           18 :         if (secondEdgeName != origID) {
     675           20 :             two->setOrigID(origID, false);
     676              :         }
     677              :     }
     678          779 :     two->copyConnectionsFrom(edge);
     679          779 :     if (speed != -1.) {
     680          277 :         two->setSpeed(-1, speed);
     681              :     }
     682          779 :     if (friction != -1.) {
     683          779 :         two->setFriction(-1, friction);
     684              :     }
     685          779 :     if (edge->getDistance() != 0) {
     686              :         one->setDistance(edge->getDistance());
     687            3 :         two->setDistance(one->getDistance() + pos);
     688              :     }
     689          779 :     if (edge->hasLoadedLength()) {
     690            7 :         one->setLoadedLength(pos);
     691            7 :         two->setLoadedLength(edge->getLoadedLength() - pos);
     692              :     }
     693              :     // replace information about this edge within the nodes
     694          779 :     edge->myFrom->replaceOutgoing(edge, one, 0);
     695          779 :     edge->myTo->replaceIncoming(edge, two, 0);
     696              :     // patch tls
     697          803 :     for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
     698           24 :         tld->replaceRemoved(edge, -1, one, -1, false);
     699              :     }
     700          829 :     for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
     701           50 :         tld->replaceRemoved(edge, -1, two, -1, true);
     702              :     }
     703              :     // the edge is now occuring twice in both nodes...
     704              :     //  clean up
     705          779 :     edge->myFrom->removeDoubleEdges();
     706          779 :     edge->myTo->removeDoubleEdges();
     707              :     // add connections from the first to the second edge
     708              :     // there will be as many connections as there are lanes on the second edge
     709              :     // by default lanes will be added / discontinued on the right side
     710              :     // (appropriate for highway on-/off-ramps)
     711          779 :     const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
     712         2243 :     for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
     713         1464 :         const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
     714         2928 :         if (!one->addLane2LaneConnection(i1, two, i2, NBEdge::Lane2LaneInfoType::COMPUTED)) {
     715            0 :             throw ProcessError(TL("Could not set connection!"));
     716              :         }
     717              :     }
     718          779 :     if (myRemoveEdgesAfterLoading) {
     719            0 :         if (myEdges2Keep.count(edge->getID()) != 0) {
     720            0 :             myEdges2Keep.insert(one->getID());
     721            0 :             myEdges2Keep.insert(two->getID());
     722              :         }
     723            0 :         if (myEdges2Remove.count(edge->getID()) != 0) {
     724            0 :             myEdges2Remove.insert(one->getID());
     725            0 :             myEdges2Remove.insert(two->getID());
     726              :         }
     727              :     }
     728              :     // erase the splitted edge
     729          779 :     patchRoundabouts(edge, one, two, myRoundabouts);
     730          779 :     patchRoundabouts(edge, one, two, myGuessedRoundabouts);
     731          779 :     const std::string oldID = edge->getID();
     732          779 :     extract(dc, edge, true);
     733          779 :     insert(one, true);  // duplicate id check happened earlier
     734          779 :     insert(two, true);  // duplicate id check happened earlier
     735          779 :     myEdgesSplit[edge] = {one, two};
     736              :     myWasSplit.insert(one);
     737              :     myWasSplit.insert(two);
     738              :     return true;
     739              : }
     740              : 
     741              : 
     742              : void
     743         1558 : NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
     744              :     std::set<EdgeSet> addLater;
     745         1561 :     for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
     746              :         EdgeSet roundaboutSet = *it;
     747              :         if (roundaboutSet.count(orig) > 0) {
     748              :             roundaboutSet.erase(orig);
     749              :             roundaboutSet.insert(part1);
     750              :             roundaboutSet.insert(part2);
     751              :         }
     752              :         addLater.insert(roundaboutSet);
     753              :     }
     754              :     roundabouts.clear();
     755         1558 :     roundabouts.insert(addLater.begin(), addLater.end());
     756         1558 : }
     757              : 
     758              : 
     759              : // ----- container access methods
     760              : std::vector<std::string>
     761           86 : NBEdgeCont::getAllNames() const {
     762              :     std::vector<std::string> ret;
     763        27710 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     764        27624 :         ret.push_back((*i).first);
     765              :     }
     766           86 :     return ret;
     767            0 : }
     768              : 
     769              : 
     770              : NBEdge*
     771            3 : NBEdgeCont::getSplitBase(const std::string& edgeID) const {
     772              :     NBEdge* longest = nullptr;
     773            7 :     for (auto item : myEdgesSplit) {
     774            4 :         if (item.first->getID() == edgeID) {
     775            3 :             if (longest == nullptr || longest->getLoadedLength() < item.first->getLoadedLength()) {
     776              :                 longest = const_cast<NBEdge*>(item.first);
     777              :             }
     778              :         }
     779              :     }
     780            3 :     return longest;
     781              : }
     782              : 
     783              : // ----- Adapting the input
     784              : int
     785            2 : NBEdgeCont::removeUnwishedEdges(NBDistrictCont& dc) {
     786              :     EdgeVector toRemove;
     787           32 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     788           30 :         NBEdge* edge = (*i).second;
     789           30 :         if (!myEdges2Keep.count(edge->getID())) {
     790           13 :             edge->getFromNode()->removeEdge(edge);
     791           13 :             edge->getToNode()->removeEdge(edge);
     792           13 :             toRemove.push_back(edge);
     793              :         }
     794              :     }
     795           15 :     for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
     796           13 :         erase(dc, *j);
     797              :     }
     798            2 :     return (int)toRemove.size();
     799            2 : }
     800              : 
     801              : 
     802              : void
     803            4 : NBEdgeCont::splitGeometry(NBDistrictCont& dc, NBNodeCont& nc) {
     804              :     // make a copy of myEdges because splitting will modify it
     805              :     EdgeCont edges = myEdges;
     806          208 :     for (auto& item : edges) {
     807          204 :         NBEdge* edge = item.second;
     808          204 :         if (edge->getGeometry().size() < 3) {
     809          108 :             continue;
     810              :         }
     811              :         PositionVector geom = edge->getGeometry();
     812           96 :         const std::string id = edge->getID();
     813              :         double offset = 0;
     814          361 :         for (int i = 1; i < (int)geom.size() - 1; i++) {
     815          265 :             offset += geom[i - 1].distanceTo(geom[i]);
     816          530 :             std::string nodeID = id + "." + toString((int)offset);
     817          265 :             if (!nc.insert(nodeID, geom[i])) {
     818          126 :                 WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
     819              :                 continue;
     820              :             }
     821          223 :             NBNode* node = nc.retrieve(nodeID);
     822          223 :             splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
     823          223 :             edge = retrieve(nodeID);
     824              :         }
     825           96 :     }
     826            4 : }
     827              : 
     828              : 
     829              : void
     830           12 : NBEdgeCont::reduceGeometries(const double minDist) {
     831          528 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     832          516 :         (*i).second->reduceGeometry(minDist);
     833              :     }
     834           12 : }
     835              : 
     836              : 
     837              : void
     838         1792 : NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
     839         1792 :     if (maxAngle > 0 || minRadius > 0) {
     840        98462 :         for (auto& item : myEdges) {
     841        96670 :             if ((item.second->getPermissions() & (SVC_PUBLIC_CLASSES | SVC_PASSENGER)) == 0) {
     842        25541 :                 continue;
     843              :             }
     844       142258 :             item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
     845              :         }
     846              :     }
     847         1792 : }
     848              : 
     849              : 
     850              : // ----- processing methods
     851              : void
     852         1841 : NBEdgeCont::clearControllingTLInformation() const {
     853       105286 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     854       103445 :         (*i).second->clearControllingTLInformation();
     855              :     }
     856         1841 : }
     857              : 
     858              : 
     859              : void
     860         1841 : NBEdgeCont::sortOutgoingLanesConnections() {
     861       105286 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     862       103445 :         (*i).second->sortOutgoingConnectionsByAngle();
     863              :     }
     864         1841 : }
     865              : 
     866              : 
     867              : void
     868         1841 : NBEdgeCont::computeEdge2Edges(bool noLeftMovers) {
     869       105286 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     870       103445 :         (*i).second->computeEdge2Edges(noLeftMovers);
     871              :     }
     872         1841 : }
     873              : 
     874              : 
     875              : void
     876         1841 : NBEdgeCont::computeLanes2Edges() {
     877       105286 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     878       103445 :         (*i).second->computeLanes2Edges();
     879              :     }
     880         1841 : }
     881              : 
     882              : 
     883              : void
     884         1841 : NBEdgeCont::recheckLanes() {
     885         3682 :     const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
     886       105286 :     for (const auto& edgeIt : myEdges) {
     887       103445 :         NBEdge* const edge = edgeIt.second;
     888       103445 :         edge->recheckLanes();
     889       103445 :         edge->recheckOpposite(*this, fixOppositeLengths);
     890              :     }
     891         1841 : }
     892              : 
     893              : 
     894              : void
     895         1083 : NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
     896        67220 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     897        66137 :         (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
     898              :     }
     899         1083 : }
     900              : 
     901              : 
     902              : void
     903          758 : NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
     904          758 :     for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
     905            0 :         myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
     906              :     }
     907          758 : }
     908              : 
     909              : 
     910              : void
     911           28 : NBEdgeCont::appendRailwayTurnarounds(const NBPTStopCont& sc) {
     912              :     std::set<std::string> stopEdgeIDs;
     913          444 :     for (auto& stopItem : sc.getStops()) {
     914          416 :         stopEdgeIDs.insert(stopItem.second->getEdgeId());
     915              :     }
     916         7193 :     for (auto& item : myEdges) {
     917         7165 :         NBEdge* edge = item.second;
     918         7165 :         if (edge->isBidiRail()
     919         7165 :                 && (stopEdgeIDs.count(item.first) > 0 ||
     920         2147 :                     stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
     921          314 :             NBEdge* to = edge->getTurnDestination(true);
     922              :             assert(to != 0);
     923          628 :             edge->setConnection(edge->getNumLanes() - 1,
     924              :                                 to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
     925              :                                 KEEPCLEAR_UNSPECIFIED,
     926              :                                 NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
     927              :                                 SUMO_const_haltingSpeed);
     928              :         }
     929              :     }
     930           28 : }
     931              : 
     932              : void
     933         1999 : NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
     934       138646 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     935       136647 :         (*i).second->computeEdgeShape(smoothElevationThreshold);
     936              :     }
     937              :     // equalize length of opposite edges
     938       138646 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     939       136647 :         NBEdge* edge = i->second;
     940       136647 :         const std::string& oppositeID = edge->getLanes().back().oppositeID;
     941       136647 :         if (oppositeID != "" && oppositeID != "-") {
     942           94 :             NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
     943          187 :             if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
     944            1 :                 continue;
     945              :             }
     946           93 :             if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
     947            5 :                 double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
     948            5 :                 edge->setAverageLengthWithOpposite(avgLength);
     949            5 :                 oppEdge->setAverageLengthWithOpposite(avgLength);
     950              :             }
     951              :         }
     952              :     }
     953         1999 : }
     954              : 
     955              : 
     956              : void
     957         1999 : NBEdgeCont::computeLaneShapes() {
     958       138646 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     959       136647 :         (*i).second->computeLaneShapes();
     960              :     }
     961         1999 : }
     962              : 
     963              : 
     964              : void
     965           12 : NBEdgeCont::joinSameNodeConnectingEdges(NBDistrictCont& dc,
     966              :                                         NBTrafficLightLogicCont& tlc,
     967              :                                         EdgeVector edges) {
     968              :     // !!! Attention!
     969              :     //  No merging of the geometry to come is being done
     970              :     //  The connections are moved from one edge to another within
     971              :     //   the replacement where the edge is a node's incoming edge.
     972              : 
     973              :     // count the number of lanes, the speed and the id
     974              :     int nolanes = 0;
     975              :     double speed = 0;
     976              :     int priority = -1;
     977              :     bool joinEdges = true;
     978              :     std::string id;
     979           12 :     sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
     980              :     // retrieve the connected nodes
     981           12 :     NBEdge* tpledge = *(edges.begin());
     982              :     NBNode* from = tpledge->getFromNode();
     983              :     NBNode* to = tpledge->getToNode();
     984              :     EdgeVector::const_iterator i;
     985              :     int myPriority = (*edges.begin())->getPriority();
     986           36 :     for (i = edges.begin(); i != edges.end(); i++) {
     987              :         // some assertions
     988              :         assert((*i)->getFromNode() == from);
     989              :         assert((*i)->getToNode() == to);
     990              :         // ad the number of lanes the current edge has
     991           24 :         nolanes += (*i)->getNumLanes();
     992              :         // build the id
     993           24 :         if (i != edges.begin()) {
     994              :             id += "+";
     995              :         }
     996           24 :         id += (*i)->getID();
     997              :         // compute the speed
     998           24 :         speed += (*i)->getSpeed();
     999              :         // build the priority
    1000              :         // merged edges should have the same inherited priority
    1001           24 :         if (myPriority == (*i)->getPriority()) {
    1002              :             priority = myPriority;
    1003              :         } else {
    1004              :             priority = -1;
    1005              :             joinEdges = false;
    1006              :         }
    1007              :     }
    1008           12 :     if (joinEdges) {
    1009           11 :         speed /= (double)edges.size();
    1010              :         // build the new edge
    1011              :         NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
    1012              :                                      NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
    1013           22 :                                      tpledge->myLaneSpreadFunction, tpledge->getStreetName());
    1014              :         // copy lane attributes
    1015              :         int laneIndex = 0;
    1016           33 :         for (i = edges.begin(); i != edges.end(); ++i) {
    1017           22 :             const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
    1018           49 :             for (int j = 0; j < (int)lanes.size(); ++j) {
    1019           27 :                 newEdge->setPermissions(lanes[j].permissions, laneIndex);
    1020           27 :                 newEdge->setLaneWidth(laneIndex, lanes[j].width);
    1021           27 :                 newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
    1022           27 :                 laneIndex++;
    1023              :             }
    1024              :         }
    1025           11 :         insert(newEdge, true);
    1026              :         // replace old edge by current within the nodes
    1027              :         //  and delete the old
    1028           11 :         from->replaceOutgoing(edges, newEdge);
    1029           11 :         to->replaceIncoming(edges, newEdge);
    1030              :         // patch connections
    1031              :         //  add edge2edge-information
    1032           33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1033           22 :             EdgeVector ev = (*i)->getConnectedEdges();
    1034           59 :             for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
    1035           37 :                 newEdge->addEdge2EdgeConnection(*j);
    1036              :             }
    1037           22 :         }
    1038              :         //  copy outgoing connections to the new edge
    1039              :         int currLane = 0;
    1040           33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1041           22 :             newEdge->moveOutgoingConnectionsFrom(*i, currLane);
    1042           22 :             currLane += (*i)->getNumLanes();
    1043              :         }
    1044              :         // patch tl-information
    1045              :         currLane = 0;
    1046           33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1047           22 :             int noLanes = (*i)->getNumLanes();
    1048           49 :             for (int j = 0; j < noLanes; j++, currLane++) {
    1049              :                 // replace in traffic lights
    1050           27 :                 tlc.replaceRemoved(*i, j, newEdge, currLane, true);
    1051           27 :                 tlc.replaceRemoved(*i, j, newEdge, currLane, false);
    1052              :             }
    1053              :         }
    1054              :         // delete joined edges
    1055           33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1056           22 :             extract(dc, *i, true);
    1057              :         }
    1058              :     }
    1059           12 : }
    1060              : 
    1061              : 
    1062              : void
    1063           16 : NBEdgeCont::guessOpposites() {
    1064              :     //@todo magic values
    1065           32 :     const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
    1066              :     // ensure consistency of loaded values before starting to guess
    1067           63 :     for (const auto& edgeIt : myEdges) {
    1068           47 :         NBEdge* const edge = edgeIt.second;
    1069           47 :         edge->recheckOpposite(*this, fixOppositeLengths);
    1070              :     }
    1071           63 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1072           47 :         NBEdge* edge = i->second;
    1073           47 :         edge->guessOpposite();
    1074              :     }
    1075           16 : }
    1076              : 
    1077              : 
    1078              : void
    1079           49 : NBEdgeCont::recheckLaneSpread() {
    1080          704 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1081          655 :         NBEdge* opposite = getOppositeByID(i->first);
    1082          655 :         if (opposite != nullptr) {
    1083          470 :             i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
    1084          470 :             opposite->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
    1085              :         } else {
    1086          185 :             i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
    1087              :         }
    1088              :     }
    1089           49 : }
    1090              : 
    1091              : 
    1092              : NBEdge*
    1093         1144 : NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
    1094         1144 :     const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) :  "-" + edgeID;
    1095              :     EdgeCont::const_iterator it = myEdges.find(oppositeID);
    1096         1144 :     return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
    1097              : }
    1098              : 
    1099              : NBEdge*
    1100        19864 : NBEdgeCont::getByID(const std::string& edgeID) const {
    1101              :     EdgeCont::const_iterator it = myEdges.find(edgeID);
    1102        19864 :     return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
    1103              : }
    1104              : 
    1105              : // ----- other
    1106              : void
    1107           18 : NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
    1108              :                                      KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
    1109              :                                      const PositionVector& customShape, bool uncontrolled, bool warnOnly,
    1110              :                                      SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
    1111           36 :     myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
    1112              :                                   speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
    1113           18 : }
    1114              : 
    1115              : bool
    1116         8130 : NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
    1117              :     if (myConnections.count(from) == 0) {
    1118         8126 :         return false;
    1119              :     } else {
    1120            4 :         if (to == "") {
    1121              :             // wildcard
    1122              :             return true;
    1123              :         }
    1124            0 :         for (const auto& ppc : myConnections[from]) {
    1125            0 :             if (ppc.to == to) {
    1126              :                 return true;
    1127              :             }
    1128              :         }
    1129              :         return false;
    1130              :     }
    1131              : }
    1132              : 
    1133              : void
    1134         1841 : NBEdgeCont::recheckPostProcessConnections() {
    1135         3594 :     const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
    1136         1848 :     for (const auto& item : myConnections) {
    1137           25 :         for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
    1138           18 :             NBEdge* from = retrievePossiblySplit((*i).from, true);
    1139           18 :             NBEdge* to = retrievePossiblySplit((*i).to, false);
    1140           36 :             if (from == nullptr || to == nullptr ||
    1141           18 :                     !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
    1142           18 :                                                   (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
    1143           18 :                                                   (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
    1144              :                                                   true)) {
    1145            1 :                 const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
    1146            1 :                 if (warnOnly || (*i).warnOnly) {
    1147            0 :                     WRITE_WARNING(msg);
    1148              :                 } else {
    1149            3 :                     WRITE_ERROR(msg);
    1150              :                 }
    1151              :             }
    1152              :         }
    1153              :     }
    1154              :     // during loading we also kept some ambiguous connections in hope they might be valid after processing
    1155              :     // we need to make sure that all invalid connections are removed now
    1156       105286 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
    1157       103445 :         NBEdge* edge = it->second;
    1158              :         NBNode* to = edge->getToNode();
    1159              :         // make a copy because we may delete connections
    1160       103445 :         std::vector<NBEdge::Connection> connections = edge->getConnections();
    1161       176700 :         for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
    1162              :             NBEdge::Connection& c = *it_con;
    1163        73255 :             if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
    1164           12 :                 WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
    1165              :                               "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
    1166            6 :                 edge->removeFromConnections(c.toEdge);
    1167              :             }
    1168              :         }
    1169       103445 :     }
    1170         1841 : }
    1171              : 
    1172              : 
    1173              : EdgeVector
    1174          842 : NBEdgeCont::getGeneratedFrom(const std::string& id) const {
    1175          842 :     int len = (int)id.length();
    1176              :     EdgeVector ret;
    1177       149420 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1178              :         std::string curr = (*i).first;
    1179              :         // the next check makes it possibly faster - we don not have
    1180              :         //  to compare the names
    1181       148578 :         if ((int)curr.length() <= len) {
    1182        55988 :             continue;
    1183              :         }
    1184              :         // the name must be the same as the given id but something
    1185              :         //  beginning with a '[' must be appended to it
    1186       185180 :         if (curr.substr(0, len) == id && curr[len] == '[') {
    1187         4828 :             ret.push_back((*i).second);
    1188         4828 :             continue;
    1189              :         }
    1190              :         // ok, maybe the edge is a compound made during joining of edges
    1191              :         std::string::size_type pos = curr.find(id);
    1192              :         // surely not
    1193        87762 :         if (pos == std::string::npos) {
    1194        75438 :             continue;
    1195              :         }
    1196              :         // check leading char
    1197        12324 :         if (pos > 0) {
    1198         3975 :             if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
    1199              :                 // actually, this is another id
    1200         3975 :                 continue;
    1201              :             }
    1202              :         }
    1203         8349 :         if (pos + id.length() < curr.length()) {
    1204         8349 :             if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
    1205              :                 // actually, this is another id
    1206         8349 :                 continue;
    1207              :             }
    1208              :         }
    1209            0 :         ret.push_back((*i).second);
    1210              :     }
    1211          842 :     return ret;
    1212            0 : }
    1213              : 
    1214              : 
    1215              : int
    1216         2016 : NBEdgeCont::guessRoundabouts() {
    1217              :     myGuessedRoundabouts.clear();
    1218              :     std::set<NBEdge*> loadedRoundaboutEdges;
    1219         2103 :     for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
    1220           87 :         loadedRoundaboutEdges.insert(it->begin(), it->end());
    1221              :     }
    1222              :     // step 1: keep only those edges which have no turnarounds and which are not
    1223              :     // part of a loaded roundabout
    1224              :     std::set<NBEdge*> candidates;
    1225         2016 :     SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
    1226       139679 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1227       137663 :         NBEdge* e = (*i).second;
    1228              :         NBNode* const to = e->getToNode();
    1229       137663 :         if (e->getTurnDestination() == nullptr
    1230        67138 :                 && to->getConnectionTo(e->getFromNode()) == nullptr
    1231       188693 :                 && (e->getPermissions() & valid) != 0) {
    1232              :             candidates.insert(e);
    1233              :         }
    1234              :     }
    1235              : 
    1236              :     // step 2:
    1237              :     std::set<NBEdge*> visited;
    1238        33317 :     for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
    1239              :         EdgeVector loopEdges;
    1240              :         // start with a random edge (this doesn't have to be a roundabout edge)
    1241              :         // loop over connected edges (using always the leftmost one)
    1242              :         // and keep the list in loopEdges
    1243              :         // continue until we loop back onto a loopEdges and extract the loop
    1244        31301 :         NBEdge* e = (*i);
    1245        14121 :         if (visited.count(e) > 0) {
    1246              :             // already seen
    1247              :             continue;
    1248              :         }
    1249        17180 :         loopEdges.push_back(e);
    1250              :         bool doLoop = true;
    1251              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1252              :         gDebugFlag1 = e->getID() == DEBUG_EDGE_ID;
    1253              : #endif
    1254        21611 :         do {
    1255              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1256              :             if (gDebugFlag1) {
    1257              :                 std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
    1258              :                 gDebugFlag1 = true;
    1259              :             }
    1260              : #endif
    1261              :             visited.insert(e);
    1262        34215 :             const EdgeVector& edges = e->getToNode()->getEdges();
    1263        32805 :             if ((e->getToNode()->getType() == SumoXMLNodeType::RIGHT_BEFORE_LEFT || e->getToNode()->getType() == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
    1264        34224 :                     && !e->getToNode()->typeWasGuessed()) {
    1265              :                 doLoop = false;
    1266              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1267              :                 if (gDebugFlag1) {
    1268              :                     std::cout << " rbl\n";
    1269              :                 }
    1270              :                 gDebugFlag1 = false;
    1271              : #endif
    1272        12604 :                 break;
    1273              :             }
    1274        33175 :             if (e->getToNode()->getRoundaboutType() == RoundaboutType::NO) {
    1275              :                 doLoop = false;
    1276              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1277              :                 if (gDebugFlag1) {
    1278              :                     std::cout << " disabled\n";
    1279              :                 }
    1280              :                 gDebugFlag1 = false;
    1281              : #endif
    1282              :                 break;
    1283              :             }
    1284        33174 :             if (edges.size() < 2) {
    1285              :                 doLoop = false;
    1286              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1287              :                 if (gDebugFlag1) {
    1288              :                     std::cout << " deadend\n";
    1289              :                 }
    1290              :                 gDebugFlag1 = false;
    1291              : #endif
    1292              :                 break;
    1293              :             }
    1294        29812 :             if (e->getTurnDestination(true) != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
    1295              :                 // do not follow turn-arounds while in a (tentative) loop
    1296              :                 doLoop = false;
    1297              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1298              :                 if (gDebugFlag1) {
    1299              :                     std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
    1300              :                 }
    1301              :                 gDebugFlag1 = false;
    1302              : #endif
    1303              :                 break;
    1304              :             }
    1305        26219 :             EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
    1306        26219 :             NBContHelper::nextCW(edges, me);
    1307        26219 :             NBEdge* left = *me;
    1308        27870 :             while ((left->getPermissions() & valid) == 0 && left != e) {
    1309         1651 :                 NBContHelper::nextCW(edges, me);
    1310         1651 :                 left = *me;
    1311              :             }
    1312        26219 :             if (left == e) {
    1313              :                 // no usable continuation edge found
    1314              :                 doLoop = false;
    1315              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1316              :                 if (gDebugFlag1) {
    1317              :                     std::cout << " noContinuation\n";
    1318              :                 }
    1319              :                 gDebugFlag1 = false;
    1320              : #endif
    1321              :                 break;
    1322              :             }
    1323        26190 :             NBContHelper::nextCW(edges, me);
    1324        26190 :             NBEdge* nextLeft = *me;
    1325        26190 :             double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
    1326        26190 :             double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
    1327              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1328              :             if (gDebugFlag1) {
    1329              :                 std::cout << "   e=" << e->getID() << " left=" << left->getID() << " nextLeft=" << nextLeft->getID() << " angle=" << angle << " nextAngle=" << nextAngle << " eLength=" << e->getLength() << " lLength=" << left->getLength() << " dist=" << e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) << "\n";
    1330              :             }
    1331              : #endif
    1332              :             // there should be no straigher edge further left
    1333        26190 :             if (angle >= 90 && nextAngle < 45) {
    1334              :                 doLoop = false;
    1335              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1336              :                 if (gDebugFlag1) {
    1337              :                     std::cout << "     failed nextAngle=" << nextAngle << "\n";
    1338              :                 }
    1339              :                 gDebugFlag1 = false;
    1340              : #endif
    1341              :                 break;
    1342              :             }
    1343              :             // roundabouts do not have sharp turns (or they wouldn't be called 'round')
    1344              :             // however, if the roundabout is very small then most of the roundness may be in the junction so the angle may be as high as 180 (for smooth attachments at a joined junction)
    1345              :             if (angle >= 90) {
    1346         2496 :                 double edgeAngle = fabs(NBHelpers::relAngle(e->getStartAngle(), e->getEndAngle()));
    1347         2496 :                 double edgeAngle2 = fabs(NBHelpers::relAngle(left->getStartAngle(), left->getEndAngle()));
    1348         2496 :                 double edgeRadius = e->getGeometry().length2D() / DEG2RAD(edgeAngle);
    1349         2496 :                 double edgeRadius2 = left->getGeometry().length2D() / DEG2RAD(edgeAngle2);
    1350         2496 :                 const double avgRadius = 0.5 * (edgeRadius + edgeRadius2);
    1351         2496 :                 double junctionRadius = e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) / DEG2RAD(angle);
    1352              :                 //std::cout << "     junction=" << e->getToNode()->getID() << " e=" << e->getID() << " left=" << left->getID() << " angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
    1353         2496 :                 if (junctionRadius < 0.8 * avgRadius) {
    1354              :                     doLoop = false;
    1355              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1356              :                     if (gDebugFlag1) {
    1357              :                         std::cout << "     failed angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
    1358              :                     }
    1359              :                     gDebugFlag1 = false;
    1360              : #endif
    1361              :                     break;
    1362              :                 }
    1363              :             }
    1364              : 
    1365        21789 :             EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
    1366        21789 :             const int loopSize = (int)(loopEdges.end() - loopClosed);
    1367        21789 :             if (loopSize > 0) {
    1368              :                 // loop found
    1369          178 :                 if (loopSize < 3) {
    1370              :                     doLoop = false; // need at least 3 edges for a roundabout
    1371          110 :                 } else if (loopSize < (int)loopEdges.size()) {
    1372              :                     // remove initial edges not belonging to the loop
    1373           46 :                     EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
    1374              :                 }
    1375              :                 // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
    1376              :                 int attachments = 0;
    1377          997 :                 for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
    1378          819 :                     if ((*j)->getToNode()->getEdges().size() > 2) {
    1379          542 :                         attachments++;
    1380              :                     }
    1381              :                 }
    1382          178 :                 if (attachments < 3) {
    1383              :                     doLoop = false;
    1384              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1385              :                     if (gDebugFlag1) {
    1386              :                         std::cout << " attachments=" << attachments << "\n";
    1387              :                     }
    1388              :                     gDebugFlag1 = false;
    1389              : #endif
    1390              :                 }
    1391              :                 break;
    1392              :             }
    1393              :             if (visited.count(left) > 0) {
    1394              :                 doLoop = false;
    1395              :             } else {
    1396              :                 // keep going
    1397        17035 :                 loopEdges.push_back(left);
    1398        17035 :                 e = left;
    1399              :             }
    1400              :         } while (doLoop);
    1401        17108 :         if (doLoop) {
    1402              :             // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
    1403              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1404              :             if (gDebugFlag1) {
    1405              :                 std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
    1406              :             }
    1407              : #endif
    1408              :             double loopLength = 0;
    1409          637 :             for (const NBEdge* const le : loopEdges) {
    1410          536 :                 loopLength += le->getLoadedLength();
    1411              :             }
    1412          101 :             if (formFactor(loopEdges) > 0.6
    1413          199 :                     && loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
    1414              :                 // collected edges are marked in markRoundabouts
    1415           96 :                 EdgeSet guessed(loopEdges.begin(), loopEdges.end());
    1416              :                 if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
    1417           79 :                     if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
    1418            2 :                         for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
    1419              :                             if ((*it).count(loopEdges.front()) != 0) {
    1420            4 :                                 WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
    1421              :                                 myRoundabouts.erase(it);
    1422              :                                 break;
    1423              :                             }
    1424              :                         }
    1425              :                         myGuessedRoundabouts.insert(guessed);
    1426              :                     }
    1427              :                 } else {
    1428              :                     myGuessedRoundabouts.insert(guessed);
    1429              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1430              :                     if (gDebugFlag1) {
    1431              :                         std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
    1432              :                     }
    1433              : #endif
    1434              :                 }
    1435              :             }
    1436              :         }
    1437              : #ifdef DEBUG_GUESS_ROUNDABOUT
    1438              :         gDebugFlag1 = false;
    1439              : #endif
    1440        31301 :     }
    1441         4032 :     return (int)myGuessedRoundabouts.size();
    1442              : }
    1443              : 
    1444              : 
    1445              : int
    1446          188 : NBEdgeCont::extractRoundabouts() {
    1447              :     std::set<NBEdge*> candidateEdges;
    1448        41827 :     for (const auto& edge : myEdges) {
    1449        41639 :         NBEdge* const e = edge.second;
    1450        41639 :         if (e->getJunctionPriority(e->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT || e->getJunctionPriority(e->getFromNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
    1451              :             candidateEdges.insert(e);
    1452              :         }
    1453              :     }
    1454              :     std::set<NBEdge*> visited;
    1455              :     int extracted = 0;
    1456          247 :     for (const auto& edgeIt : candidateEdges) {
    1457              :         EdgeVector loopEdges;
    1458           59 :         NBEdge* e = edgeIt;
    1459           48 :         if (visited.count(e) > 0) {
    1460              :             // already seen
    1461              :             continue;
    1462              :         }
    1463           11 :         loopEdges.push_back(e);
    1464              :         bool doLoop = true;
    1465              :         //
    1466           59 :         do {
    1467           70 :             if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
    1468           11 :                 if (loopEdges.size() > 1) {
    1469           11 :                     addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
    1470           11 :                     ++extracted;
    1471              :                 }
    1472              :                 doLoop = false;
    1473              :                 break;
    1474              :             }
    1475              :             visited.insert(e);
    1476           59 :             loopEdges.push_back(e);
    1477           59 :             const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
    1478              :             EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
    1479           83 :                 return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
    1480              :             });
    1481           59 :             if (me == outgoingEdges.end()) { // no closed loop
    1482              :                 doLoop = false;
    1483              :             } else {
    1484           59 :                 e = *me;
    1485              :             }
    1486              :         } while (doLoop);
    1487           59 :     }
    1488          188 :     return extracted;
    1489              : }
    1490              : 
    1491              : 
    1492              : void
    1493           18 : NBEdgeCont::cleanupRoundabouts() {
    1494              :     // only loaded roundabouts are of concern here since guessing comes later
    1495              :     std::set<EdgeSet> validRoundabouts;
    1496              :     std::set<NBEdge*> validEdges;
    1497         1320 :     for (auto item : myEdges) {
    1498              :         validEdges.insert(item.second);
    1499              :     }
    1500           21 :     for (EdgeSet roundabout : myRoundabouts) {
    1501              :         EdgeSet validRoundabout;
    1502            5 :         for (NBEdge* cand : roundabout) {
    1503              :             if (validEdges.count(cand) != 0) {
    1504              :                 validRoundabout.insert(cand);
    1505              :             }
    1506              :         }
    1507            3 :         if (validRoundabout.size() > 0) {
    1508              :             validRoundabouts.insert(validRoundabout);
    1509              :         }
    1510              :     }
    1511              :     myRoundabouts = validRoundabouts;
    1512           18 : }
    1513              : 
    1514              : 
    1515              : double
    1516          101 : NBEdgeCont::formFactor(const EdgeVector& loopEdges) {
    1517              :     // A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
    1518          101 :     PositionVector points;
    1519          637 :     for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
    1520          536 :         points.append((*it)->getGeometry());
    1521              :     }
    1522          101 :     double circumference = points.length2D();
    1523          202 :     return 4 * M_PI * points.area() / (circumference * circumference);
    1524          101 : }
    1525              : 
    1526              : 
    1527              : const std::set<EdgeSet>
    1528         7566 : NBEdgeCont::getRoundabouts() const {
    1529              :     std::set<EdgeSet> result = myRoundabouts;
    1530         7566 :     result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
    1531         7566 :     return result;
    1532              : }
    1533              : 
    1534              : 
    1535              : void
    1536           82 : NBEdgeCont::addRoundabout(const EdgeSet& roundabout) {
    1537           82 :     if (roundabout.size() > 0) {
    1538           80 :         if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
    1539            0 :             WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
    1540              :         } else {
    1541              :             myRoundabouts.insert(roundabout);
    1542              :         }
    1543              :     }
    1544           82 : }
    1545              : 
    1546              : void
    1547            4 : NBEdgeCont::removeRoundabout(const NBNode* node) {
    1548            4 :     for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
    1549            2 :         for (NBEdge* e : *it) {
    1550            2 :             if (e->getToNode() == node) {
    1551              :                 myRoundabouts.erase(it);
    1552              :                 return;
    1553              :             }
    1554              :         }
    1555              :     }
    1556              : }
    1557              : 
    1558              : void
    1559           23 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove) {
    1560           23 :     removeRoundaboutEdges(toRemove, myRoundabouts);
    1561           23 :     removeRoundaboutEdges(toRemove, myGuessedRoundabouts);
    1562           23 : }
    1563              : 
    1564              : void
    1565           46 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
    1566              :     // members of a set are constant so we have to do some tricks
    1567              :     std::vector<EdgeSet> rList;
    1568           48 :     for (const EdgeSet& r : roundabouts) {
    1569              :         EdgeSet r2;
    1570            2 :         std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
    1571            2 :         rList.push_back(r2);
    1572              :     }
    1573              :     roundabouts.clear();
    1574              :     roundabouts.insert(rList.begin(), rList.end());
    1575           46 : }
    1576              : 
    1577              : 
    1578              : void
    1579         1889 : NBEdgeCont::markRoundabouts() {
    1580         1981 :     for (const EdgeSet& roundaboutSet : getRoundabouts()) {
    1581          536 :         for (NBEdge* const edge : roundaboutSet) {
    1582              :             // disable turnarounds on incoming edges
    1583              :             NBNode* const node = edge->getToNode();
    1584         1201 :             for (NBEdge* const inEdge : node->getIncomingEdges()) {
    1585          444 :                 if (roundaboutSet.count(inEdge) > 0) {
    1586          444 :                     continue;
    1587              :                 }
    1588          313 :                 if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
    1589          199 :                     continue;
    1590              :                 }
    1591          114 :                 if (inEdge->getTurnDestination() != nullptr) {
    1592           52 :                     inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
    1593              :                 } else {
    1594              :                     // also remove connections that are effecively a turnaround but
    1595              :                     // where not correctly detector due to geometrical quirks
    1596           62 :                     const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
    1597          126 :                     for (const NBEdge::Connection& con : cons) {
    1598           64 :                         if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
    1599           10 :                             const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
    1600           10 :                             if (angle > 160) {
    1601            1 :                                 inEdge->removeFromConnections(con.toEdge, -1);
    1602              :                             }
    1603              :                         }
    1604              :                     }
    1605           62 :                 }
    1606              : 
    1607              :             }
    1608              :             // let the connections to succeeding roundabout edge have a higher priority
    1609          444 :             edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
    1610          444 :             edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
    1611          444 :             node->setRoundabout();
    1612              :         }
    1613              :     }
    1614         1889 : }
    1615              : 
    1616              : 
    1617              : void
    1618            1 : NBEdgeCont::generateStreetSigns() {
    1619           23 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1620           22 :         NBEdge* e = i->second;
    1621           22 :         const double offset = MAX2(0., e->getLength() - 3);
    1622           22 :         if (e->getToNode()->isSimpleContinuation(false)) {
    1623              :             // not a "real" junction?
    1624           10 :             continue;
    1625              :         }
    1626              :         const SumoXMLNodeType nodeType = e->getToNode()->getType();
    1627           12 :         switch (nodeType) {
    1628            4 :             case SumoXMLNodeType::PRIORITY:
    1629              :                 // yield or major?
    1630            4 :                 if (e->getJunctionPriority(e->getToNode()) > 0) {
    1631            6 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
    1632              :                 } else {
    1633            6 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_YIELD, offset));
    1634              :                 }
    1635              :                 break;
    1636            0 :             case SumoXMLNodeType::PRIORITY_STOP:
    1637              :                 // yield or major?
    1638            0 :                 if (e->getJunctionPriority(e->getToNode()) > 0) {
    1639            0 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
    1640              :                 } else {
    1641            0 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_STOP, offset));
    1642              :                 }
    1643              :                 break;
    1644            0 :             case SumoXMLNodeType::ALLWAY_STOP:
    1645            0 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_ALLWAY_STOP, offset));
    1646            0 :                 break;
    1647            4 :             case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
    1648            8 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_RIGHT_BEFORE_LEFT, offset));
    1649            4 :                 break;
    1650            0 :             case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
    1651            0 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_LEFT_BEFORE_RIGHT, offset));
    1652            0 :                 break;
    1653              :             default:
    1654              :                 break;
    1655              :         }
    1656              :     }
    1657            1 : }
    1658              : 
    1659              : 
    1660              : int
    1661           21 : NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
    1662              :                               NBTrafficLightLogicCont& tlc) {
    1663              :     int lanesCreated = 0;
    1664              :     std::vector<std::string> edges;
    1665           21 :     if (excludeOpt != "") {
    1666           21 :         edges = OptionsCont::getOptions().getStringVector(excludeOpt);
    1667              :     }
    1668           21 :     std::set<std::string> exclude(edges.begin(), edges.end());
    1669          443 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1670          422 :         NBEdge* edge = it->second;
    1671              :         if (// not excluded
    1672          422 :             exclude.count(edge->getID()) == 0
    1673              :             // does not yet have a sidewalk/bikelane
    1674          419 :             && !edge->hasRestrictedLane(svc)
    1675              :             // needs a sidewalk/bikelane
    1676          301 :             && ((edge->getPermissions() & ~SVC_VULNERABLE) != 0 || (edge->getPermissions() & svc) == 0)
    1677          284 :             && (
    1678              :                 // guess.from-permissions
    1679          138 :                 (fromPermissions && (edge->getPermissions() & svc) != 0)
    1680              :                 // guess from speed
    1681          146 :                 || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
    1682              :             )) {
    1683          236 :             edge->addRestrictedLane(width, svc);
    1684          236 :             lanesCreated += 1;
    1685          236 :             if (svc != SVC_PEDESTRIAN) {
    1686           36 :                 if (edge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
    1687              :                     // preserve existing connections and only add new ones
    1688              :                     edge->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::LANES2LANES_DONE);
    1689            9 :                     edge->getFromNode()->recheckVClassConnections(edge);
    1690           30 :                     for (NBEdge* to : edge->getToNode()->getOutgoingEdges()) {
    1691           21 :                         edge->getToNode()->recheckVClassConnections(to);
    1692              :                     }
    1693              :                     // patching TLS is not feasible because existing states may
    1694              :                     // change from 'G' to 'g' when bike lanes are added (i.e. right-turns)
    1695              :                 } else {
    1696           27 :                     edge->invalidateConnections(true);
    1697           27 :                     edge->getFromNode()->invalidateOutgoingConnections(true);
    1698              :                 }
    1699           36 :                 edge->getFromNode()->invalidateTLS(tlc, true, false);
    1700           36 :                 edge->getToNode()->invalidateTLS(tlc, true, false);
    1701              :             }
    1702              :         }
    1703              :     }
    1704           21 :     return lanesCreated;
    1705           21 : }
    1706              : 
    1707              : 
    1708              : void
    1709            3 : NBEdgeCont::updateAllChangeRestrictions(SVCPermissions ignoring) {
    1710            9 :     for (auto item : myEdges) {
    1711            6 :         item.second->updateChangeRestrictions(ignoring);
    1712              :     }
    1713            3 : }
    1714              : 
    1715              : 
    1716              : void
    1717            0 : NBEdgeCont::addPrefix(const std::string& prefix) {
    1718              :     // make a copy of node containers
    1719              :     const auto nodeContainerCopy = myEdges;
    1720              :     myEdges.clear();
    1721            0 :     for (const auto& node : nodeContainerCopy) {
    1722            0 :         node.second->setID(prefix + node.second->getID());
    1723            0 :         myEdges[node.second->getID()] = node.second;
    1724              :     }
    1725            0 : }
    1726              : 
    1727              : 
    1728              : int
    1729         1841 : NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBPTStopCont& sc) {
    1730         1841 :     bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
    1731         1841 :     if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
    1732              :         return 0;
    1733              :     }
    1734              :     std::vector<std::string> avoid;
    1735           66 :     if (startGiven) {
    1736            6 :         avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
    1737              :     } else {
    1738           63 :         avoid = getAllNames();
    1739              :     }
    1740              :     std::set<std::string> reserve;
    1741           66 :     if (reservedIDs) {
    1742            4 :         NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
    1743            2 :         avoid.insert(avoid.end(), reserve.begin(), reserve.end());
    1744              :     }
    1745          132 :     IDSupplier idSupplier("", avoid);
    1746              :     std::set<NBEdge*, ComparatorIdLess> toChange;
    1747        25635 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1748        25569 :         if (startGiven) {
    1749           78 :             toChange.insert(it->second);
    1750           78 :             continue;
    1751              :         }
    1752        25491 :         if (numericaIDs) {
    1753              :             try {
    1754        25301 :                 StringUtils::toLong(it->first);
    1755         9637 :             } catch (NumberFormatException&) {
    1756         9637 :                 toChange.insert(it->second);
    1757         9637 :             }
    1758              :         }
    1759        25491 :         if (reservedIDs && reserve.count(it->first) > 0) {
    1760            2 :             toChange.insert(it->second);
    1761              :         }
    1762              :     }
    1763              :     std::set<std::string> keep;
    1764           66 :     if (keptIDs) {
    1765            4 :         NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "edge:", keep);
    1766           10 :         for (auto it = toChange.begin(); it != toChange.end();) {
    1767            8 :             if (keep.count((*it)->getID()) != 0) {
    1768              :                 toChange.erase(it++);
    1769              :             } else {
    1770              :                 it++;
    1771              :             }
    1772              :         }
    1773              :     }
    1774              : 
    1775              :     std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
    1776           92 :     for (const auto& item : sc.getStops()) {
    1777           26 :         stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
    1778              :     }
    1779              : 
    1780          132 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    1781         9779 :     for (NBEdge* edge : toChange) {
    1782         9713 :         myEdges.erase(edge->getID());
    1783              :     }
    1784         9779 :     for (NBEdge* edge : toChange) {
    1785         9713 :         const std::string origID = edge->getID();
    1786         9713 :         if (origNames) {
    1787        18898 :             edge->setOrigID(origID, false);
    1788              :         }
    1789         9713 :         edge->setID(idSupplier.getNext());
    1790         9713 :         myEdges[edge->getID()] = edge;
    1791         9732 :         for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
    1792           57 :             stop->setEdgeId(prefix + edge->getID(), *this);
    1793              :         }
    1794              :     }
    1795           66 :     if (prefix.empty()) {
    1796           56 :         return (int)toChange.size();
    1797              :     } else {
    1798              :         int renamed = 0;
    1799              :         // make a copy because we will modify the map
    1800              :         auto oldEdges = myEdges;
    1801          208 :         for (auto item : oldEdges) {
    1802          396 :             if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
    1803          193 :                 rename(item.second, prefix + item.first);
    1804          193 :                 renamed++;
    1805              :             }
    1806              :         }
    1807              :         return renamed;
    1808              :     }
    1809          132 : }
    1810              : 
    1811              : 
    1812              : void
    1813            0 : NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
    1814            0 :     for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1815            0 :         const NBEdge* e1 = it->second;
    1816            0 :         Boundary b1 = e1->getGeometry().getBoxBoundary();
    1817            0 :         b1.grow(e1->getTotalWidth());
    1818            0 :         PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
    1819            0 :         outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
    1820              :         // check is symmetric. only check once per pair
    1821            0 :         for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
    1822            0 :             const NBEdge* e2 = it2->second;
    1823            0 :             if (e1 == e2) {
    1824            0 :                 continue;
    1825              :             }
    1826            0 :             Boundary b2 = e2->getGeometry().getBoxBoundary();
    1827            0 :             b2.grow(e2->getTotalWidth());
    1828            0 :             if (b1.overlapsWith(b2)) {
    1829            0 :                 PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
    1830            0 :                 outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
    1831            0 :                 const double overlap = outline1.getOverlapWith(outline2, zThreshold);
    1832            0 :                 if (overlap > threshold) {
    1833            0 :                     WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
    1834              :                 }
    1835            0 :             }
    1836              :         }
    1837            0 :     }
    1838            0 : }
    1839              : 
    1840              : 
    1841              : void
    1842           25 : NBEdgeCont::checkGrade(double threshold) const {
    1843         3251 :     for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1844         3226 :         const NBEdge* edge = it->second;
    1845         6176 :         for (int i = 0; i < (int)edge->getNumLanes(); i++) {
    1846         3308 :             double maxJump = 0;
    1847         3308 :             const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
    1848         3308 :             if (maxJump > 0.01) {
    1849            0 :                 WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
    1850         3308 :             } else if (grade > threshold) {
    1851         1074 :                 WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
    1852          358 :                 break;
    1853              :             }
    1854              :         }
    1855              :         const std::vector<NBEdge::Connection>& connections = edge->getConnections();
    1856         4306 :         for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
    1857              :             const NBEdge::Connection& c = *it_con;
    1858         1084 :             double maxJump = 0;
    1859         1084 :             const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
    1860         1084 :             if (maxJump > 0.01) {
    1861            0 :                 WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
    1862         1084 :             } else if (grade > threshold) {
    1863            8 :                 WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
    1864            4 :                 break;
    1865              :             }
    1866              :         }
    1867              :     }
    1868           25 : }
    1869              : 
    1870              : 
    1871              : int
    1872            2 : NBEdgeCont::joinLanes(SVCPermissions perms) {
    1873              :     int affectedEdges = 0;
    1874           38 :     for (auto item : myEdges) {
    1875           36 :         if (item.second->joinLanes(perms)) {
    1876           18 :             affectedEdges++;
    1877              :         }
    1878              :     }
    1879            2 :     return affectedEdges;
    1880              : }
    1881              : 
    1882              : 
    1883              : bool
    1884         2940 : NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
    1885         2940 :     if (a.first->getID() == b.first->getID()) {
    1886           44 :         return a.second < b.second;
    1887              :     }
    1888         2896 :     return a.first->getID() < b.first->getID();
    1889              : }
    1890              : 
    1891              : int
    1892            7 : NBEdgeCont::joinTramEdges(NBDistrictCont& dc, NBPTStopCont& sc, NBPTLineCont& lc, double maxDist) {
    1893              :     // this is different from joinSimilarEdges because there don't need to be
    1894              :     // shared nodes and tram edges may be split
    1895              :     std::vector<NBEdge*> tramEdges;
    1896              :     std::vector<NBEdge*> targetEdges;
    1897         2250 :     for (auto item : myEdges) {
    1898         2243 :         SVCPermissions permissions = item.second->getPermissions();
    1899         2243 :         if (isTram(permissions)) {
    1900          494 :             if (item.second->getNumLanes() == 1) {
    1901          466 :                 tramEdges.push_back(item.second);
    1902              :             } else {
    1903           84 :                 WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
    1904              :             }
    1905         1749 :         } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
    1906         1181 :             targetEdges.push_back(item.second);
    1907              :         }
    1908              :     }
    1909            7 :     if (tramEdges.empty() || targetEdges.empty()) {
    1910              :         return 0;
    1911              :     }
    1912              :     int numJoined = 0;
    1913              :     NamedRTree tramTree;
    1914          473 :     for (NBEdge* const edge : tramEdges) {
    1915          466 :         const Boundary& bound = edge->getGeometry().getBoxBoundary();
    1916          466 :         float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
    1917          466 :         float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
    1918          932 :         tramTree.Insert(min, max, edge);
    1919              :     }
    1920              :     // {targetEdge, laneIndex : tramEdge}
    1921              :     std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
    1922              : 
    1923         1188 :     for (NBEdge* const edge : targetEdges) {
    1924         1181 :         Boundary bound = edge->getGeometry().getBoxBoundary();
    1925         1181 :         bound.grow(maxDist + edge->getTotalWidth());
    1926         1181 :         float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
    1927         1181 :         float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
    1928              :         std::set<const Named*> near;
    1929              :         Named::StoringVisitor visitor(near);
    1930              :         tramTree.Search(min, max, visitor);
    1931              :         // the nearby set is actually just re-sorting according to the id to make the tests comparable
    1932              :         std::set<NBEdge*, ComparatorIdLess> nearby;
    1933         4795 :         for (const Named* namedEdge : near) {
    1934         3614 :             nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
    1935              :         }
    1936         4795 :         for (NBEdge* const tramEdge : nearby) {
    1937              :             // find a continous stretch of tramEdge that runs along one of the lanes of the road edge
    1938              :             PositionVector tramShape = tramEdge->getGeometry();
    1939         3614 :             if (tramEdge->getToNode() == edge->getToNode()) {
    1940           87 :                 tramShape.extrapolate(tramShape.back().distanceTo2D(edge->getGeometry().back()), false, true);
    1941              :             }
    1942         3614 :             double minEdgeDist = maxDist + 1;
    1943              :             int minLane = -1;
    1944              :             // find the lane where the maximum distance from the tram geometry
    1945              :             // is minimal and within maxDist
    1946        10529 :             for (int i = 0; i < edge->getNumLanes(); i++) {
    1947              :                 double maxLaneDist = -1;
    1948         6915 :                 if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
    1949         6393 :                     const PositionVector& laneShape = edge->getLaneShape(i);
    1950         7475 :                     for (Position pos : laneShape) {
    1951         7216 :                         const double dist = tramShape.distance2D(pos, false);
    1952              : #ifdef DEBUG_JOIN_TRAM
    1953              :                         //if (edge->getID() == "106838214#1") {
    1954              :                         //    std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
    1955              :                         //}
    1956              : #endif
    1957         7216 :                         if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
    1958              :                             maxLaneDist = -1;
    1959              :                             break;
    1960              :                         }
    1961              :                         maxLaneDist = MAX2(maxLaneDist, dist);
    1962              :                     }
    1963         6393 :                     if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
    1964              :                         minEdgeDist = maxLaneDist;
    1965              :                         minLane = i;
    1966              :                     }
    1967              :                 }
    1968              :             }
    1969         3614 :             if (minLane >= 0) {
    1970              :                 // edge could run in the wrong direction and still fit the threshold we check the angle as well
    1971          253 :                 const PositionVector& laneShape = edge->getLaneShape(minLane);
    1972          253 :                 const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
    1973          253 :                 const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
    1974          253 :                 Position p1 = tramShape.positionAtOffset2D(offset1);
    1975          253 :                 Position p2 = tramShape.positionAtOffset2D(offset2);
    1976          253 :                 double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
    1977          253 :                 bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
    1978          253 :                 if (angleOK && offset2 > offset1) {
    1979          205 :                     std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
    1980              :                     if (matches.count(key) == 0) {
    1981          195 :                         matches[key] = tramEdge;
    1982              :                     } else {
    1983           40 :                         WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
    1984              :                     }
    1985              : #ifdef DEBUG_JOIN_TRAM
    1986              :                     std::cout << edge->getLaneID(minLane) << " is close to tramEdge " << tramEdge->getID() << " maxLaneDist=" << minEdgeDist << " tramLength=" << tramEdge->getLength() << " edgeLength=" << edge->getLength() << " tramAngle=" << tramAngle << " edgeAngle=" << edge->getTotalAngle() << "\n";
    1987              : #endif
    1988              :                 }
    1989              :             }
    1990         3614 :         }
    1991              :     }
    1992            7 :     if (matches.size() == 0) {
    1993              :         return 0;
    1994              :     }
    1995           14 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    1996              :     // find continous runs of matched edges for each tramEdge
    1997          473 :     for (NBEdge* tramEdge : tramEdges) {
    1998              :         std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
    1999        14774 :         for (auto item : matches) {
    2000        14308 :             if (item.second == tramEdge) {
    2001              :                 NBEdge* road = item.first.first;
    2002              :                 int laneIndex = item.first.second;
    2003          195 :                 const PositionVector& laneShape = road->getLaneShape(laneIndex);
    2004          195 :                 double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
    2005              :                 //std::cout << " road=" << road->getID() << " tramEdge=" << tramEdge->getID() << " tramShape=" << tramEdge->getGeometry() << " laneFront=" << laneShape.front() << " tramPos=" << tramPos << "\n";
    2006          195 :                 roads.push_back(std::make_pair(tramPos, item.first));
    2007              :             }
    2008              :         }
    2009          466 :         if (roads.size() != 0) {
    2010              : 
    2011           62 :             sort(roads.begin(), roads.end());
    2012              : #ifdef DEBUG_JOIN_TRAM
    2013              :             std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
    2014              :             for (auto item : roads) {
    2015              :                 std::cout << item.second.first->getLaneID(item.second.second) << ",";
    2016              :             }
    2017              :             std::cout << " offsets=";
    2018              :             for (auto item : roads) {
    2019              :                 std::cout << item.first << ",";
    2020              :             }
    2021              :             std::cout << "\n";
    2022              : #endif
    2023              :             // merge tramEdge into road lanes
    2024              :             EdgeVector replacement;
    2025              :             double pos = 0;
    2026              :             int tramPart = 0;
    2027           62 :             std::string tramEdgeID = tramEdge->getID();
    2028              :             NBNode* tramFrom = tramEdge->getFromNode();
    2029              :             PositionVector tramShape = tramEdge->getGeometry();
    2030           62 :             const double tramLength = tramShape.length();
    2031           62 :             EdgeVector incoming = tramFrom->getIncomingEdges();
    2032              :             bool erasedLast = false;
    2033          257 :             for (const auto& item : roads) {
    2034          195 :                 const double gap = item.first - pos;
    2035          195 :                 NBEdge* road = item.second.first;
    2036          195 :                 int laneIndex = item.second.second;
    2037          195 :                 if (gap >= JOIN_TRAM_MIN_LENGTH && road->getFromNode() != tramEdge->getFromNode()) {
    2038              : #ifdef DEBUG_JOIN_TRAM
    2039              :                     std::cout << "    splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
    2040              : #endif
    2041           74 :                     const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
    2042           37 :                     splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
    2043           37 :                     tramEdge = retrieve(tramEdgeID); // second part;
    2044           37 :                     NBEdge* firstPart = retrieve(firstPartID);
    2045           37 :                     firstPart->invalidateConnections(true);
    2046              :                     incoming.clear();
    2047           37 :                     incoming.push_back(firstPart);
    2048           37 :                     replacement.push_back(firstPart);
    2049              :                 }
    2050          195 :                 pos = item.first + road->getGeometry().length();
    2051          195 :                 numJoined++;
    2052          195 :                 replacement.push_back(road);
    2053              :                 // merge section of tramEdge into road lane
    2054          195 :                 if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
    2055          164 :                     tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
    2056          164 :                     tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
    2057              :                     erasedLast = false;
    2058              : #ifdef DEBUG_JOIN_TRAM
    2059              :                     std::cout << "    shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
    2060              : #endif
    2061              :                 } else {
    2062              : #ifdef DEBUG_JOIN_TRAM
    2063              :                     std::cout << "    erased tramEdge=" << tramEdge->getID() << "\n";
    2064              : #endif
    2065           31 :                     extract(dc, tramEdge, true);
    2066              :                     erasedLast = true;
    2067              :                 }
    2068          195 :                 road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
    2069          195 :                 if (origNames) {
    2070          114 :                     road->setOrigID(tramEdgeID, true, laneIndex);
    2071              :                 }
    2072          296 :                 for (NBEdge* in : incoming) {
    2073          101 :                     if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
    2074           53 :                         if (in->getFromNode() != road->getFromNode()) {
    2075           50 :                             in->reinitNodes(in->getFromNode(), road->getFromNode());
    2076              :                         } else {
    2077            3 :                             extract(dc, in, true);
    2078              : #ifdef DEBUG_JOIN_TRAM
    2079              :                             std::cout << "    erased incoming tramEdge=" << in->getID() << "\n";
    2080              : #endif
    2081              :                         }
    2082              :                     }
    2083              :                 }
    2084              :                 incoming.clear();
    2085              :             }
    2086           62 :             NBEdge* lastRoad = roads.back().second.first;
    2087           62 :             if (erasedLast) {
    2088              :                 // copy to avoid concurrent modification
    2089           29 :                 auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
    2090           94 :                 for (NBEdge* out : outEdges) {
    2091           65 :                     if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
    2092           31 :                         if (lastRoad->getToNode() != out->getToNode()) {
    2093           30 :                             out->reinitNodes(lastRoad->getToNode(), out->getToNode());
    2094              :                         } else {
    2095            1 :                             extract(dc, out, true);
    2096              : #ifdef DEBUG_JOIN_TRAM
    2097              :                             std::cout << "    erased outgoing tramEdge=" << out->getID() << "\n";
    2098              : #endif
    2099              : 
    2100              :                         }
    2101              :                     }
    2102              :                 }
    2103           29 :             } else {
    2104           33 :                 replacement.push_back(tramEdge);
    2105              :             }
    2106              :             // update ptstops and ptlines
    2107           62 :             sc.replaceEdge(tramEdgeID, replacement);
    2108           62 :             lc.replaceEdge(tramEdgeID, replacement);
    2109          124 :         }
    2110          466 :     }
    2111              : 
    2112            7 :     return numJoined;
    2113            7 : }
    2114              : 
    2115              : 
    2116              : EdgeVector
    2117          179 : NBEdgeCont::getAllEdges() const {
    2118              :     EdgeVector result;
    2119        53401 :     for (auto item : myEdges) {
    2120        53222 :         item.second->setNumericalID((int)result.size());
    2121        53222 :         result.push_back(item.second);
    2122              :     }
    2123          179 :     return result;
    2124            0 : }
    2125              : 
    2126              : RouterEdgeVector
    2127           76 : NBEdgeCont::getAllRouterEdges() const {
    2128           76 :     EdgeVector all = getAllEdges();
    2129          152 :     return RouterEdgeVector(all.begin(), all.end());
    2130           76 : }
    2131              : 
    2132              : bool
    2133         1932 : NBEdgeCont::checkConsistency(const NBNodeCont& nc) {
    2134              :     bool ok = true;
    2135        97954 :     for (const auto& item : myEdges) {
    2136        96022 :         NBEdge* e = item.second;
    2137        96022 :         if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
    2138            4 :             WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
    2139              :             ok = false;
    2140              :         }
    2141        96022 :         if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
    2142            4 :             WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
    2143              :             ok = false;
    2144              :         }
    2145              : 
    2146              :     }
    2147         1932 :     return ok;
    2148              : }
    2149              : 
    2150              : 
    2151              : void
    2152           85 : NBEdgeCont::fixSplitCustomLength() {
    2153         2241 :     for (auto item : myEdges) {
    2154              :         NBEdge* e = item.second;
    2155         2156 :         if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
    2156              :             // subtract half the length of the longest incoming / outgoing connection
    2157              :             double maxLengthOut = 0;
    2158           15 :             for (const NBEdge::Connection& c : e->getConnections()) {
    2159            9 :                 maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
    2160              :             }
    2161              :             double maxLengthIn = 0;
    2162           10 :             for (const NBEdge* in : e->getIncomingEdges()) {
    2163           15 :                 for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
    2164           11 :                     maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
    2165            4 :                 }
    2166            6 :             }
    2167           12 :             e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
    2168              :         }
    2169              :     }
    2170           85 : }
    2171              : 
    2172              : void
    2173          156 : NBEdgeCont::computeAngles() {
    2174        25296 :     for (auto item : myEdges) {
    2175        25140 :         item.second->computeAngle();
    2176              :     }
    2177          156 : }
    2178              : 
    2179              : 
    2180              : std::set<std::string>
    2181         1769 : NBEdgeCont::getUsedTypes() const {
    2182              :     std::set<std::string> result;
    2183       103406 :     for (auto item : myEdges) {
    2184       101637 :         if (item.second->getTypeID() != "") {
    2185              :             result.insert(item.second->getTypeID());
    2186              :         }
    2187              :     }
    2188         1769 :     return result;
    2189              : }
    2190              : 
    2191              : 
    2192              : int
    2193            4 : NBEdgeCont::removeEdgesBySpeed(NBDistrictCont& dc) {
    2194              :     EdgeSet toRemove;
    2195           84 :     for (auto item : myEdges) {
    2196           80 :         NBEdge* edge = item.second;
    2197              :         // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
    2198           80 :         if (edge->getSpeed() < myEdgesMinSpeed) {
    2199              :             toRemove.insert(edge);
    2200              :         }
    2201              :     }
    2202              :     int numRemoved = 0;
    2203            4 :     for (NBEdge* edge : toRemove) {
    2204              :         // explicit whitelist overrides removal
    2205            0 :         if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
    2206            0 :             extract(dc, edge);
    2207            0 :             numRemoved++;
    2208              :         }
    2209              :     }
    2210            4 :     return numRemoved;
    2211              : }
    2212              : 
    2213              : 
    2214              : int
    2215            4 : NBEdgeCont::removeEdgesByPermissions(NBDistrictCont& dc) {
    2216              :     EdgeSet toRemove;
    2217           84 :     for (auto item : myEdges) {
    2218           80 :         NBEdge* edge = item.second;
    2219              :         // check whether the edge shall be removed because it does not allow any of the wished classes
    2220           80 :         if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
    2221              :             toRemove.insert(edge);
    2222              :         }
    2223              :         // check whether the edge shall be removed due to allowing unwished classes only
    2224           80 :         if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
    2225              :             toRemove.insert(edge);
    2226              :         }
    2227              :     }
    2228              :     int numRemoved = 0;
    2229           29 :     for (NBEdge* edge : toRemove) {
    2230              :         // explicit whitelist overrides removal
    2231           25 :         if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
    2232           25 :             extract(dc, edge);
    2233           25 :             numRemoved++;
    2234              :         }
    2235              :     }
    2236            4 :     return numRemoved;
    2237              : }
    2238              : 
    2239              : 
    2240              : int
    2241         1841 : NBEdgeCont::removeLanesByWidth(NBDistrictCont& dc, const double minWidth) {
    2242              :     EdgeSet toRemove;
    2243       112319 :     for (auto item : myEdges) {
    2244       110478 :         NBEdge* const edge = item.second;
    2245              :         std::vector<int> indices;
    2246       110478 :         int idx = 0;
    2247       255110 :         for (const auto& lane : edge->getLanes()) {
    2248       144632 :             if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
    2249            4 :                 indices.push_back(idx);
    2250              :             }
    2251       144632 :             idx++;
    2252              :         }
    2253       110478 :         if ((int)indices.size() == edge->getNumLanes()) {
    2254              :             toRemove.insert(edge);
    2255              :         } else {
    2256              :             std::reverse(indices.begin(), indices.end());
    2257       110482 :             for (const int i : indices) {
    2258            4 :                 edge->deleteLane(i, false, true);
    2259              :             }
    2260              :         }
    2261       110478 :     }
    2262              :     int numRemoved = 0;
    2263         1841 :     for (NBEdge* edge : toRemove) {
    2264              :         // explicit whitelist overrides removal
    2265            0 :         if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
    2266            0 :             extract(dc, edge);
    2267            0 :             numRemoved++;
    2268              :         }
    2269              :     }
    2270         1841 :     return numRemoved;
    2271              : }
    2272              : 
    2273              : 
    2274              : int
    2275            1 : NBEdgeCont::attachRemoved(NBNodeCont& nc, NBDistrictCont& dc, const double maxDist) {
    2276              :     int numSplit = 0;
    2277              :     std::map<std::string, std::vector<std::string> > node2edge;
    2278          172 :     for (auto item : myEdges) {
    2279          171 :         if (item.second->hasParameter(SUMO_PARAM_REMOVED_NODES)) {
    2280           45 :             for (std::string& nodeID : StringTokenizer(item.second->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
    2281           19 :                 node2edge[nodeID].push_back(item.first);
    2282           13 :             }
    2283              :         }
    2284              :     }
    2285           84 :     for (auto item : nc) {
    2286              :         NBNode* n = item.second;
    2287              :         auto itRN = node2edge.find(n->getID());
    2288           83 :         if (itRN != node2edge.end()) {
    2289              :             bool rebuildConnections = false;
    2290              :             // make a copy because we modify the original
    2291            6 :             std::vector<std::string> edgeIDs = itRN->second;
    2292           20 :             for (const std::string& eID : edgeIDs) {
    2293           14 :                 NBEdge* edge = retrieve(eID);
    2294              :                 assert(edge != nullptr);
    2295           14 :                 const double dist = edge->getGeometry().distance2D(n->getPosition(), true);
    2296           14 :                 if (dist != GeomHelper::INVALID_OFFSET && dist <= maxDist) {
    2297           12 :                     std::string idAfter = edge->getID();
    2298           12 :                     int index = 1;
    2299              :                     size_t spos = idAfter.find("#");
    2300           12 :                     if (spos != std::string::npos && spos > 1) {
    2301           24 :                         idAfter = idAfter.substr(0, spos);
    2302              :                     }
    2303           62 :                     while (retrieve(idAfter + "#" + toString(index), true) != nullptr) {
    2304           19 :                         index++;
    2305              :                     }
    2306           24 :                     idAfter += "#" + toString(index);
    2307           12 :                     const bool ok = splitAt(dc, edge, n, edge->getID(), idAfter, edge->getNumLanes(), edge->getNumLanes());
    2308           12 :                     if (ok) {
    2309              :                         rebuildConnections = true;
    2310           12 :                         numSplit++;
    2311           12 :                         NBEdge* secondEdge = retrieve(eID); // original was extracted on splitting
    2312           44 :                         for (std::string& nodeID : StringTokenizer(secondEdge->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
    2313           20 :                             node2edge[nodeID].push_back(idAfter);
    2314           12 :                         }
    2315              :                     }
    2316              :                 }
    2317              :             }
    2318            6 :             if (rebuildConnections) {
    2319           24 :                 for (NBEdge* e : n->getIncomingEdges()) {
    2320           18 :                     e->invalidateConnections(true);
    2321              :                 }
    2322              :             }
    2323            6 :         }
    2324              :     }
    2325            1 :     return numSplit;
    2326              : }
    2327              : 
    2328              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1