LCOV - code coverage report
Current view: top level - src/netbuild - NBEdgeCont.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 943 1033 91.3 %
Date: 2024-05-06 15:32:35 Functions: 66 69 95.7 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    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        2337 : NBEdgeCont::NBEdgeCont(NBTypeCont& tc) :
      65        2337 :     myTypeCont(tc),
      66        2337 :     myVehicleClasses2Keep(0),
      67        2337 :     myVehicleClasses2Remove(0),
      68        2337 :     myNeedGeoTransformedPruningBoundary(false) {
      69        2337 : }
      70             : 
      71             : 
      72        2337 : NBEdgeCont::~NBEdgeCont() {
      73        2337 :     clear();
      74        2337 : }
      75             : 
      76             : 
      77             : void
      78        2337 : NBEdgeCont::applyOptions(OptionsCont& oc) {
      79             :     // set edges dismiss/accept options
      80        2337 :     myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
      81        6801 :     myRemoveEdgesAfterLoading = oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload");
      82             :     // we possibly have to load the edges to keep/remove
      83        4674 :     if (oc.isSet("keep-edges.input-file")) {
      84           6 :         NBHelpers::loadEdgesFromFile(oc.getString("keep-edges.input-file"), myEdges2Keep);
      85             :     }
      86        4674 :     if (oc.isSet("remove-edges.input-file")) {
      87           3 :         NBHelpers::loadEdgesFromFile(oc.getString("remove-edges.input-file"), myEdges2Remove);
      88             :     }
      89        4674 :     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        4674 :     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        9087 :     if (oc.exists("keep-edges.by-vclass") && oc.isSet("keep-edges.by-vclass")) {
      98         110 :         myVehicleClasses2Keep = parseVehicleClasses(oc.getStringVector("keep-edges.by-vclass"));
      99             :     }
     100        9103 :     if (oc.exists("remove-edges.by-vclass") && oc.isSet("remove-edges.by-vclass")) {
     101          71 :         myVehicleClasses2Remove = parseVehicleClasses(oc.getStringVector("remove-edges.by-vclass"));
     102             :     }
     103        9109 :     if (oc.exists("keep-edges.by-type") && oc.isSet("keep-edges.by-type")) {
     104          10 :         const std::vector<std::string> types = oc.getStringVector("keep-edges.by-type");
     105             :         myTypes2Keep.insert(types.begin(), types.end());
     106           5 :     }
     107        9105 :     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        6985 :     if (oc.isSet("keep-edges.in-boundary") || oc.isSet("keep-edges.in-geo-boundary")) {
     113             : 
     114          13 :         std::string polyPlainString = oc.getValueString(oc.isSet("keep-edges.in-boundary") ?
     115          30 :                                       "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
     116             :         // try interpreting the boundary like shape attribute with spaces
     117          13 :         bool ok = true;
     118          16 :         PositionVector boundaryShape = GeomConvHelper::parseShapeReporting(polyPlainString, "pruning-boundary", 0, ok, false, false);
     119          13 :         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          37 :             std::vector<std::string> polyS = oc.getStringVector(oc.isSet("keep-edges.in-boundary") ?
     134          11 :                                              "keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
     135             :             std::vector<double> poly;
     136          78 :             for (std::vector<std::string>::iterator i = polyS.begin(); i != polyS.end(); ++i) {
     137          69 :                 poly.push_back(StringUtils::toDouble((*i))); // !!! may throw something anyhow...
     138             :             }
     139          10 :             if (poly.size() < 4) {
     140           0 :                 throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
     141          10 :             } else if (poly.size() % 2 != 0) {
     142           2 :                 throw ProcessError(TL("Invalid boundary: malformed coordinate"));
     143           9 :             } else if (poly.size() == 4) {
     144             :                 // prunning boundary (box)
     145           7 :                 myPruningBoundary.push_back(Position(poly[0], poly[1]));
     146           7 :                 myPruningBoundary.push_back(Position(poly[2], poly[1]));
     147           7 :                 myPruningBoundary.push_back(Position(poly[2], poly[3]));
     148           7 :                 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          19 :                     myPruningBoundary.push_back(Position(x, y));
     154             :                 }
     155             :             }
     156          11 :         }
     157          10 :         myNeedGeoTransformedPruningBoundary = oc.isSet("keep-edges.in-geo-boundary");
     158          13 :     }
     159        2327 : }
     160             : 
     161             : 
     162             : void
     163        2337 : NBEdgeCont::clear() {
     164      131090 :     for (const auto& i : myEdges) {
     165      128753 :         delete i.second;
     166             :     }
     167             :     myEdges.clear();
     168       16705 :     for (const auto& i : myExtractedEdges) {
     169       14368 :         delete i.second;
     170             :     }
     171             :     myExtractedEdges.clear();
     172        2355 :     for (NBEdge* const e : myEdgeCemetery) {
     173          18 :         delete e;
     174             :     }
     175             :     myEdgeCemetery.clear();
     176        2337 : }
     177             : 
     178             : 
     179             : 
     180             : // ----- edge access methods
     181             : bool
     182      151226 : NBEdgeCont::insert(NBEdge* edge, bool ignorePrunning) {
     183      151226 :     if (myEdges.count(edge->getID()) != 0) {
     184           0 :         return false;
     185             :     }
     186      151226 :     if (!ignorePrunning && ignoreFilterMatch(edge)) {
     187        7696 :         edge->getFromNode()->removeEdge(edge);
     188        7696 :         edge->getToNode()->removeEdge(edge);
     189        7696 :         myIgnoredEdges.insert(edge->getID());
     190        7696 :         delete edge;
     191             :     } else {
     192      143530 :         OptionsCont& oc = OptionsCont::getOptions();
     193      541898 :         if (oc.exists("dismiss-vclasses") && oc.getBool("dismiss-vclasses")) {
     194         200 :             edge->dismissVehicleClassInformation();
     195             :         }
     196      143530 :         myEdges[edge->getID()] = edge;
     197             :     }
     198             :     return true;
     199             : }
     200             : 
     201             : 
     202             : bool
     203      188269 : NBEdgeCont::ignoreFilterMatch(NBEdge* edge) {
     204      188269 :     if (!myRemoveEdgesAfterLoading) {
     205             :         // check whether the edge is a named edge to keep
     206      188156 :         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      188036 :         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      188030 :         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      183298 :         if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
     229             :             return true;
     230             :         }
     231             :     }
     232             :     // check whether the edge is a named edge to remove
     233      183092 :     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      183059 :     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      181616 :     if (myTypes2Remove.size() != 0) {
     246             :         if (myTypes2Remove.count(edge->getTypeID()) > 0) {
     247          68 :             return true;
     248             :         }
     249             :     }
     250             :     // check whether the edge is within the pruning boundary
     251      181548 :     if (myPruningBoundary.size() != 0) {
     252         913 :         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         913 :         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      181324 :     if (myTypeCont.knows(edge->getTypeID()) && myTypeCont.getEdgeTypeShallBeDiscarded(edge->getTypeID())) {
     274         834 :         return true;
     275             :     }
     276             :     return false;
     277             : }
     278             : 
     279             : 
     280             : NBEdge*
     281      447892 : NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
     282             :     EdgeCont::const_iterator i = myEdges.find(id);
     283      447892 :     if (i == myEdges.end()) {
     284      124131 :         if (retrieveExtracted) {
     285             :             i = myExtractedEdges.find(id);
     286       29720 :             if (i == myExtractedEdges.end()) {
     287             :                 return nullptr;
     288             :             }
     289             :         } else {
     290             :             return nullptr;
     291             :         }
     292             :     }
     293      323798 :     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         264 :         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             : }
     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         558 :         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         366 : NBEdgeCont::erase(NBDistrictCont& dc, NBEdge* edge) {
     412         366 :     extract(dc, edge);
     413         366 :     delete edge;
     414         366 : }
     415             : 
     416             : 
     417             : void
     418       14779 : NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
     419       14779 :     if (remember) {
     420       14388 :         const auto& prevExtracted = myExtractedEdges.find(edge->getID());
     421       14388 :         if (prevExtracted != myExtractedEdges.end()) {
     422          20 :             if (edge != prevExtracted->second) {
     423          18 :                 myEdgeCemetery.insert(prevExtracted->second);
     424          18 :                 prevExtracted->second = edge;
     425             :             }
     426             :         } else {
     427       14368 :             myExtractedEdges[edge->getID()] = edge;
     428             :         }
     429             :     }
     430       14779 :     myEdges.erase(edge->getID());
     431       14779 :     edge->myFrom->removeEdge(edge);
     432       14779 :     edge->myTo->removeEdge(edge);
     433       14779 :     dc.removeFromSinksAndSources(edge);
     434       14779 : }
     435             : 
     436             : 
     437             : void
     438         181 : 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         181 :     myEdges.erase(edge->getID());
     443         181 :     edge->setID(newID);
     444         181 :     myEdges[newID] = edge;
     445             :     // update oppositeID
     446         181 :     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         181 : }
     453             : 
     454             : 
     455             : // ----- explicit edge manipulation methods
     456             : 
     457             : void
     458       23020 : NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
     459             :                           NBNodeCont& nc, NBDistrictCont& dc, NBTrafficLightLogicCont& tlc) {
     460       23020 :     if (splits.empty()) {
     461       22772 :         return;
     462             :     }
     463         250 :     const std::string origID = e->getID();
     464         250 :     sort(splits.begin(), splits.end(), split_sorter());
     465             :     int maxNumLanes = e->getNumLanes();
     466             :     // compute the node positions and sort the lanes
     467         599 :     for (Split& split : splits) {
     468         349 :         sort(split.lanes.begin(), split.lanes.end());
     469         349 :         maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
     470             :     }
     471             :     // split the edge
     472             :     std::vector<int> currLanes;
     473         696 :     for (int l = 0; l < e->getNumLanes(); ++l) {
     474         446 :         currLanes.push_back(l);
     475             :     }
     476         250 :     if (e->getNumLanes() != (int)splits.back().lanes.size()) {
     477             :         // invalidate traffic light definitions loaded from a SUMO network
     478         197 :         e->getToNode()->invalidateTLS(tlc, true, true);
     479             :         // if the number of lanes changes the connections should be
     480             :         // recomputed
     481         197 :         e->invalidateConnections(true);
     482             :     }
     483             : 
     484         250 :     std::string firstID = "";
     485             :     double seen = 0;
     486         597 :     for (const Split& exp : splits) {
     487             :         assert(exp.lanes.size() != 0);
     488         636 :         if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
     489         286 :             nc.insert(exp.node);
     490         286 :             nc.markAsSplit(exp.node);
     491             :             //  split the edge
     492         286 :             const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
     493         286 :             const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
     494         286 :             if (firstID == "") {
     495             :                 firstID = idBefore;
     496             :             }
     497         286 :             const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
     498         286 :                                     idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
     499         286 :             if (!ok) {
     500           6 :                 WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
     501             :                 return;
     502             :             }
     503         284 :             seen = exp.pos;
     504         284 :             std::vector<int> newLanes = exp.lanes;
     505         284 :             NBEdge* pe = retrieve(idBefore);
     506         284 :             NBEdge* ne = retrieve(idAfter);
     507             :             // reconnect lanes
     508         284 :             pe->invalidateConnections(true);
     509             :             //  new on right
     510         284 :             int rightMostP = currLanes[0];
     511         284 :             int rightMostN = newLanes[0];
     512         345 :             for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
     513         122 :                 pe->addLane2LaneConnection(0, ne, l, NBEdge::Lane2LaneInfoType::VALIDATED, true);
     514             :             }
     515             :             //  new on left
     516         284 :             int leftMostP = currLanes.back();
     517         284 :             int leftMostN = newLanes.back();
     518         455 :             for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
     519         342 :                 pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
     520             :             }
     521             :             //  all other connected
     522        1013 :             for (int l = 0; l < maxNumLanes; ++l) {
     523         729 :                 if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
     524         246 :                     continue;
     525             :                 }
     526         483 :                 if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
     527          55 :                     continue;
     528             :                 }
     529         856 :                 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         284 :             if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1) {
     536          19 :                 for (NBEdge* in : exp.node->getIncomingEdges()) {
     537          13 :                     in->invalidateConnections(true);
     538             :                 }
     539             :             }
     540             :             // move to next
     541             :             e = ne;
     542         284 :             currLanes = newLanes;
     543          63 :         }  else if (exp.pos == 0) {
     544          62 :             const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
     545          62 :             if (laneCountDiff < 0) {
     546           1 :                 e->incLaneNo(-laneCountDiff);
     547             :             } else {
     548          61 :                 e->decLaneNo(laneCountDiff);
     549             :             }
     550          62 :             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          62 :             e->getFromNode()->invalidateTLS(tlc, true, true);
     554          62 :             if (exp.speed != -1.) {
     555          62 :                 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         248 :     e = retrieve(firstID);
     563         248 :     if (e != nullptr) {
     564         246 :         if (splits.front().pos != 0) {
     565             :             // add a dummy split at the beginning to ensure correct offset
     566             :             Split start;
     567         185 :             start.pos = 0;
     568         520 :             for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
     569         335 :                 start.lanes.push_back(lane);
     570             :             }
     571         185 :             start.offset = splits.front().offset;
     572         185 :             start.offsetFactor = splits.front().offsetFactor;
     573         185 :             splits.insert(splits.begin(), start);
     574         185 :         }
     575         775 :         for (const Split& split : splits) {
     576         529 :             int maxLeft = split.lanes.back();
     577         529 :             double offset = split.offset;
     578         529 :             if (maxLeft < maxNumLanes) {
     579         529 :                 if (e->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
     580         450 :                     offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
     581             :                 } else {
     582          79 :                     offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
     583             :                 }
     584             :             }
     585         529 :             int maxRight = split.lanes.front();
     586         529 :             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         529 :             if (offset != 0) {
     591             :                 PositionVector g = e->getGeometry();
     592         267 :                 g.move2side(offset);
     593         267 :                 e->setGeometry(g);
     594         267 :             }
     595         529 :             if (e->getToNode()->getOutgoingEdges().size() != 0) {
     596         452 :                 e = e->getToNode()->getOutgoingEdges()[0];
     597             :             }
     598             :         }
     599             :     }
     600             : }
     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         313 : 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         313 :     pos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
     619         313 :     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         313 :     if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
     625           3 :         return false;
     626             :     }
     627         310 :     return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
     628         310 :                    noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
     629             : }
     630             : 
     631             : 
     632             : bool
     633         632 : 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         632 :     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         632 :     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         630 :     if (edge->hasLoadedLength()) {
     655           4 :         geomPos *= edge->getGeometry().length() / edge->getLoadedLength();
     656             :     }
     657         630 :     std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
     658             :     // reduce inaccuracies and preserve bidi
     659        1260 :     if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
     660         477 :         geoms.first[-1] = node->getPosition();
     661         477 :         geoms.second[0] = node->getPosition();
     662             :     }
     663             :     // build and insert the edges
     664         630 :     NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
     665         630 :     NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
     666        1260 :     if (OptionsCont::getOptions().getBool("output.original-names")) {
     667          57 :         const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
     668          19 :         if (firstEdgeName != origID) {
     669          54 :             one->setOrigID(origID, false);
     670             :         }
     671          19 :         if (secondEdgeName != origID) {
     672          22 :             two->setOrigID(origID, false);
     673             :         }
     674             :     }
     675         630 :     two->copyConnectionsFrom(edge);
     676         630 :     if (speed != -1.) {
     677         284 :         two->setSpeed(-1, speed);
     678             :     }
     679         630 :     if (friction != -1.) {
     680         630 :         two->setFriction(-1, friction);
     681             :     }
     682         630 :     if (edge->getDistance() != 0) {
     683           4 :         one->setDistance(edge->getDistance());
     684           4 :         two->setDistance(one->getDistance() + pos);
     685             :     }
     686         630 :     if (edge->hasLoadedLength()) {
     687           4 :         one->setLoadedLength(pos);
     688           4 :         two->setLoadedLength(edge->getLoadedLength() - pos);
     689             :     }
     690             :     // replace information about this edge within the nodes
     691         630 :     edge->myFrom->replaceOutgoing(edge, one, 0);
     692         630 :     edge->myTo->replaceIncoming(edge, two, 0);
     693             :     // patch tls
     694         661 :     for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
     695          31 :         tld->replaceRemoved(edge, -1, one, -1, false);
     696             :     }
     697         675 :     for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
     698          45 :         tld->replaceRemoved(edge, -1, two, -1, true);
     699             :     }
     700             :     // the edge is now occuring twice in both nodes...
     701             :     //  clean up
     702         630 :     edge->myFrom->removeDoubleEdges();
     703         630 :     edge->myTo->removeDoubleEdges();
     704             :     // add connections from the first to the second edge
     705             :     // there will be as many connections as there are lanes on the second edge
     706             :     // by default lanes will be added / discontinued on the right side
     707             :     // (appropriate for highway on-/off-ramps)
     708         630 :     const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
     709        1935 :     for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
     710        1305 :         const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
     711        2610 :         if (!one->addLane2LaneConnection(i1, two, i2, NBEdge::Lane2LaneInfoType::COMPUTED)) {
     712           0 :             throw ProcessError(TL("Could not set connection!"));
     713             :         }
     714             :     }
     715         630 :     if (myRemoveEdgesAfterLoading) {
     716           0 :         if (myEdges2Keep.count(edge->getID()) != 0) {
     717           0 :             myEdges2Keep.insert(one->getID());
     718           0 :             myEdges2Keep.insert(two->getID());
     719             :         }
     720           0 :         if (myEdges2Remove.count(edge->getID()) != 0) {
     721           0 :             myEdges2Remove.insert(one->getID());
     722           0 :             myEdges2Remove.insert(two->getID());
     723             :         }
     724             :     }
     725             :     // erase the splitted edge
     726         630 :     patchRoundabouts(edge, one, two, myRoundabouts);
     727         630 :     patchRoundabouts(edge, one, two, myGuessedRoundabouts);
     728         630 :     const std::string oldID = edge->getID();
     729         630 :     extract(dc, edge, true);
     730         630 :     insert(one, true);  // duplicate id check happened earlier
     731         630 :     insert(two, true);  // duplicate id check happened earlier
     732         630 :     myEdgesSplit[edge] = {one, two};
     733             :     myWasSplit.insert(one);
     734             :     myWasSplit.insert(two);
     735             :     return true;
     736             : }
     737             : 
     738             : 
     739             : void
     740        1260 : NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
     741             :     std::set<EdgeSet> addLater;
     742        1263 :     for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
     743             :         EdgeSet roundaboutSet = *it;
     744             :         if (roundaboutSet.count(orig) > 0) {
     745             :             roundaboutSet.erase(orig);
     746             :             roundaboutSet.insert(part1);
     747             :             roundaboutSet.insert(part2);
     748             :         }
     749             :         addLater.insert(roundaboutSet);
     750             :     }
     751             :     roundabouts.clear();
     752        1260 :     roundabouts.insert(addLater.begin(), addLater.end());
     753        1260 : }
     754             : 
     755             : 
     756             : // ----- container access methods
     757             : std::vector<std::string>
     758          67 : NBEdgeCont::getAllNames() const {
     759             :     std::vector<std::string> ret;
     760       32197 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     761       32130 :         ret.push_back((*i).first);
     762             :     }
     763          67 :     return ret;
     764           0 : }
     765             : 
     766             : 
     767             : // ----- Adapting the input
     768             : void
     769           2 : NBEdgeCont::removeUnwishedEdges(NBDistrictCont& dc) {
     770             :     EdgeVector toRemove;
     771          32 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     772          30 :         NBEdge* edge = (*i).second;
     773          30 :         if (!myEdges2Keep.count(edge->getID())) {
     774          13 :             edge->getFromNode()->removeEdge(edge);
     775          13 :             edge->getToNode()->removeEdge(edge);
     776          13 :             toRemove.push_back(edge);
     777             :         }
     778             :     }
     779          15 :     for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
     780          13 :         erase(dc, *j);
     781             :     }
     782           2 : }
     783             : 
     784             : 
     785             : void
     786           3 : NBEdgeCont::splitGeometry(NBDistrictCont& dc, NBNodeCont& nc) {
     787             :     // make a copy of myEdges because splitting will modify it
     788             :     EdgeCont edges = myEdges;
     789          36 :     for (auto& item : edges) {
     790          33 :         NBEdge* edge = item.second;
     791          33 :         if (edge->getGeometry().size() < 3) {
     792           0 :             continue;
     793             :         }
     794             :         PositionVector geom = edge->getGeometry();
     795          33 :         const std::string id = edge->getID();
     796             :         double offset = 0;
     797         155 :         for (int i = 1; i < (int)geom.size() - 1; i++) {
     798         122 :             offset += geom[i - 1].distanceTo(geom[i]);
     799         244 :             std::string nodeID = id + "." + toString((int)offset);
     800         122 :             if (!nc.insert(nodeID, geom[i])) {
     801          84 :                 WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
     802             :                 continue;
     803             :             }
     804          80 :             NBNode* node = nc.retrieve(nodeID);
     805          80 :             splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
     806          80 :             edge = retrieve(nodeID);
     807             :         }
     808          33 :     }
     809           3 : }
     810             : 
     811             : 
     812             : void
     813          11 : NBEdgeCont::reduceGeometries(const double minDist) {
     814         473 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
     815         462 :         (*i).second->reduceGeometry(minDist);
     816             :     }
     817          11 : }
     818             : 
     819             : 
     820             : void
     821        1783 : NBEdgeCont::checkGeometries(const double maxAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
     822        1783 :     if (maxAngle > 0 || minRadius > 0) {
     823      127988 :         for (auto& item : myEdges) {
     824      126205 :             if (isSidewalk(item.second->getPermissions()) || isForbidden(item.second->getPermissions())) {
     825       25263 :                 continue;
     826             :             }
     827      111189 :             item.second->checkGeometry(maxAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
     828             :         }
     829             :     }
     830        1783 : }
     831             : 
     832             : 
     833             : // ----- processing methods
     834             : void
     835        1836 : NBEdgeCont::clearControllingTLInformation() const {
     836      129957 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     837      128121 :         (*i).second->clearControllingTLInformation();
     838             :     }
     839        1836 : }
     840             : 
     841             : 
     842             : void
     843        1837 : NBEdgeCont::sortOutgoingLanesConnections() {
     844      129964 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     845      128127 :         (*i).second->sortOutgoingConnectionsByAngle();
     846             :     }
     847        1837 : }
     848             : 
     849             : 
     850             : void
     851        1837 : NBEdgeCont::computeEdge2Edges(bool noLeftMovers) {
     852      129964 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     853      128127 :         (*i).second->computeEdge2Edges(noLeftMovers);
     854             :     }
     855        1837 : }
     856             : 
     857             : 
     858             : void
     859        1837 : NBEdgeCont::computeLanes2Edges() {
     860      129964 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     861      128127 :         (*i).second->computeLanes2Edges();
     862             :     }
     863        1837 : }
     864             : 
     865             : 
     866             : void
     867        1837 : NBEdgeCont::recheckLanes() {
     868        3674 :     const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
     869      129958 :     for (const auto& edgeIt : myEdges) {
     870      128122 :         NBEdge* const edge = edgeIt.second;
     871      128122 :         edge->recheckLanes();
     872             :         // check opposites
     873      128122 :         if (edge->getNumLanes() > 0) {
     874      128122 :             const int leftmostLane = edge->getNumLanes() - 1;
     875             :             // check oppositeID stored in other lanes
     876      164128 :             for (int i = 0; i < leftmostLane; i++) {
     877       36006 :                 const std::string& oppositeID = edge->getLanes()[i].oppositeID;
     878       72012 :                 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
     879       36012 :                 if (oppositeID != "" && oppositeID != "-") {
     880           6 :                     if (edge->getLanes().back().oppositeID == "" && oppEdge != nullptr) {
     881           1 :                         edge->getLaneStruct(leftmostLane).oppositeID = oppositeID;
     882           3 :                         WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, edge->getLaneID(i), leftmostLane);
     883             :                     } else {
     884          15 :                         WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, edge->getLaneID(i));
     885             :                     }
     886           6 :                     edge->getLaneStruct(i).oppositeID = "";
     887             :                 }
     888             :             }
     889      128122 :             const std::string& oppositeID = edge->getLanes().back().oppositeID;
     890      128255 :             if (oppositeID != "" && oppositeID != "-") {
     891         128 :                 NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
     892         128 :                 if (oppEdge == nullptr) {
     893           3 :                     WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, edge->getID());
     894           1 :                     edge->getLaneStruct(leftmostLane).oppositeID = "";
     895           1 :                     continue;
     896         254 :                 } else if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
     897           1 :                     const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
     898           4 :                     WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, edge->getID(), oppEdgeLeftmost);
     899           1 :                     edge->getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
     900             :                 }
     901         127 :                 NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
     902         127 :                 if (oppLane.oppositeID == "") {
     903           3 :                     const std::string leftmostID = edge->getLaneID(leftmostLane);
     904           9 :                     WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
     905             :                     oppLane.oppositeID = leftmostID;
     906             :                 }
     907         127 :                 if (fabs(oppEdge->getLoadedLength() - edge->getLoadedLength()) > NUMERICAL_EPS) {
     908           0 :                     if (fixOppositeLengths) {
     909           0 :                         const double avgLength = 0.5 * (edge->getFinalLength() + oppEdge->getFinalLength());
     910           0 :                         WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
     911             :                                        oppositeID, oppEdge->getLoadedLength(), edge->getID(), edge->getLoadedLength());
     912           0 :                         edge->setLoadedLength(avgLength);
     913           0 :                         oppEdge->setLoadedLength(avgLength);
     914             :                     } else {
     915           0 :                         WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
     916             :                                     ") differs in length from edge '" + edge->getID() + "' (length " +
     917             :                                     toString(edge->getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
     918           0 :                         edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
     919           0 :                         continue;
     920             :                     }
     921             :                 }
     922         127 :                 if (oppEdge->getFromNode() != edge->getToNode() || oppEdge->getToNode() != edge->getFromNode()) {
     923           3 :                     WRITE_ERRORF(TL("Opposite lane '%' does not connect the same nodes as edge '%'!"), oppositeID, edge->getID());
     924           1 :                     edge->getLaneStruct(edge->getNumLanes() - 1).oppositeID = "";
     925             :                 }
     926             :             }
     927             :         }
     928             :         // check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
     929      128121 :         const NBEdge* bidi = edge->getBidiEdge();
     930      128121 :         if (bidi != nullptr && edge->getNumLanes() == 1 && bidi->getNumLanes() == 1 && edge->getID() < bidi->getID()) {
     931        4594 :             edge->getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
     932             :         }
     933             : 
     934             :         // check for valid offset and speed
     935      132704 :         const double startOffset = edge->isBidiRail() ? edge->getTurnDestination(true)->getEndOffset() : 0;
     936             :         int i = 0;
     937      292247 :         for (const NBEdge::Lane& l : edge->getLanes()) {
     938             :             std::string error;
     939      164127 :             if (startOffset + l.endOffset > edge->getLength()) {
     940           3 :                 error = TLF("Invalid endOffset % at lane '%' with length % (startOffset %).",
     941             :                             toString(l.endOffset), edge->getLaneID(i), toString(l.shape.length()), toString(startOffset));
     942      164126 :             } else if (l.speed < 0.) {
     943           0 :                 error = TLF("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this.", toString(l.speed), edge->getLaneID(i));
     944      164126 :             } else if (l.speed == 0.) {
     945           0 :                 WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), edge->getLaneID(i));
     946             :             }
     947      164127 :             if (!error.empty()) {
     948           2 :                 if (OptionsCont::getOptions().getBool("ignore-errors")) {
     949           1 :                     WRITE_ERROR(error);
     950             :                 } else {
     951           2 :                     throw ProcessError(error);
     952             :                 }
     953             :             }
     954      164126 :             i++;
     955             :         }
     956             :     }
     957        1836 : }
     958             : 
     959             : 
     960             : void
     961        1038 : NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
     962       88259 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
     963       87221 :         (*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
     964             :     }
     965        1038 : }
     966             : 
     967             : 
     968             : void
     969         799 : NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
     970         799 :     for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
     971           0 :         myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
     972             :     }
     973         799 : }
     974             : 
     975             : 
     976             : void
     977          27 : NBEdgeCont::appendRailwayTurnarounds(const NBPTStopCont& sc) {
     978             :     std::set<std::string> stopEdgeIDs;
     979         511 :     for (auto& stopItem : sc.getStops()) {
     980         484 :         stopEdgeIDs.insert(stopItem.second->getEdgeId());
     981             :     }
     982       11508 :     for (auto& item : myEdges) {
     983       11481 :         NBEdge* edge = item.second;
     984       11481 :         if (edge->isBidiRail()
     985       11481 :                 && (stopEdgeIDs.count(item.first) > 0 ||
     986        2524 :                     stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
     987         350 :             NBEdge* to = edge->getTurnDestination(true);
     988             :             assert(to != 0);
     989         700 :             edge->setConnection(edge->getNumLanes() - 1,
     990             :                                 to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
     991             :                                 KEEPCLEAR_UNSPECIFIED,
     992             :                                 NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
     993             :                                 SUMO_const_haltingSpeed);
     994             :         }
     995             :     }
     996          27 : }
     997             : 
     998             : void
     999        1988 : NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
    1000      184914 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
    1001      182926 :         (*i).second->computeEdgeShape(smoothElevationThreshold);
    1002             :     }
    1003             :     // equalize length of opposite edges
    1004      184914 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
    1005      182926 :         NBEdge* edge = i->second;
    1006      182926 :         const std::string& oppositeID = edge->getLanes().back().oppositeID;
    1007      183056 :         if (oppositeID != "" && oppositeID != "-") {
    1008         125 :             NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
    1009         249 :             if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
    1010           1 :                 continue;
    1011             :             }
    1012         124 :             if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
    1013           2 :                 double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
    1014           2 :                 edge->setAverageLengthWithOpposite(avgLength);
    1015           2 :                 oppEdge->setAverageLengthWithOpposite(avgLength);
    1016             :             }
    1017             :         }
    1018             :     }
    1019        1988 : }
    1020             : 
    1021             : 
    1022             : void
    1023        1988 : NBEdgeCont::computeLaneShapes() {
    1024      184914 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1025      182926 :         (*i).second->computeLaneShapes();
    1026             :     }
    1027        1988 : }
    1028             : 
    1029             : 
    1030             : void
    1031          12 : NBEdgeCont::joinSameNodeConnectingEdges(NBDistrictCont& dc,
    1032             :                                         NBTrafficLightLogicCont& tlc,
    1033             :                                         EdgeVector edges) {
    1034             :     // !!! Attention!
    1035             :     //  No merging of the geometry to come is being done
    1036             :     //  The connections are moved from one edge to another within
    1037             :     //   the replacement where the edge is a node's incoming edge.
    1038             : 
    1039             :     // count the number of lanes, the speed and the id
    1040             :     int nolanes = 0;
    1041             :     double speed = 0;
    1042             :     int priority = -1;
    1043             :     bool joinEdges = true;
    1044             :     std::string id;
    1045          12 :     sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
    1046             :     // retrieve the connected nodes
    1047          12 :     NBEdge* tpledge = *(edges.begin());
    1048             :     NBNode* from = tpledge->getFromNode();
    1049             :     NBNode* to = tpledge->getToNode();
    1050             :     EdgeVector::const_iterator i;
    1051             :     int myPriority = (*edges.begin())->getPriority();
    1052          36 :     for (i = edges.begin(); i != edges.end(); i++) {
    1053             :         // some assertions
    1054             :         assert((*i)->getFromNode() == from);
    1055             :         assert((*i)->getToNode() == to);
    1056             :         // ad the number of lanes the current edge has
    1057          24 :         nolanes += (*i)->getNumLanes();
    1058             :         // build the id
    1059          24 :         if (i != edges.begin()) {
    1060             :             id += "+";
    1061             :         }
    1062          24 :         id += (*i)->getID();
    1063             :         // compute the speed
    1064          24 :         speed += (*i)->getSpeed();
    1065             :         // build the priority
    1066             :         // merged edges should have the same inherited priority
    1067          24 :         if (myPriority == (*i)->getPriority()) {
    1068             :             priority = myPriority;
    1069             :         } else {
    1070             :             priority = -1;
    1071             :             joinEdges = false;
    1072             :         }
    1073             :     }
    1074          12 :     if (joinEdges) {
    1075          11 :         speed /= (double)edges.size();
    1076             :         // build the new edge
    1077             :         NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
    1078             :                                      NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
    1079          22 :                                      tpledge->myLaneSpreadFunction, tpledge->getStreetName());
    1080             :         // copy lane attributes
    1081             :         int laneIndex = 0;
    1082          33 :         for (i = edges.begin(); i != edges.end(); ++i) {
    1083          22 :             const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
    1084          49 :             for (int j = 0; j < (int)lanes.size(); ++j) {
    1085          27 :                 newEdge->setPermissions(lanes[j].permissions, laneIndex);
    1086          27 :                 newEdge->setLaneWidth(laneIndex, lanes[j].width);
    1087          27 :                 newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
    1088          27 :                 laneIndex++;
    1089             :             }
    1090             :         }
    1091          11 :         insert(newEdge, true);
    1092             :         // replace old edge by current within the nodes
    1093             :         //  and delete the old
    1094          11 :         from->replaceOutgoing(edges, newEdge);
    1095          11 :         to->replaceIncoming(edges, newEdge);
    1096             :         // patch connections
    1097             :         //  add edge2edge-information
    1098          33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1099          22 :             EdgeVector ev = (*i)->getConnectedEdges();
    1100          59 :             for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
    1101          37 :                 newEdge->addEdge2EdgeConnection(*j);
    1102             :             }
    1103             :         }
    1104             :         //  copy outgoing connections to the new edge
    1105             :         int currLane = 0;
    1106          33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1107          22 :             newEdge->moveOutgoingConnectionsFrom(*i, currLane);
    1108          22 :             currLane += (*i)->getNumLanes();
    1109             :         }
    1110             :         // patch tl-information
    1111             :         currLane = 0;
    1112          33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1113          22 :             int noLanes = (*i)->getNumLanes();
    1114          49 :             for (int j = 0; j < noLanes; j++, currLane++) {
    1115             :                 // replace in traffic lights
    1116          27 :                 tlc.replaceRemoved(*i, j, newEdge, currLane, true);
    1117          27 :                 tlc.replaceRemoved(*i, j, newEdge, currLane, false);
    1118             :             }
    1119             :         }
    1120             :         // delete joined edges
    1121          33 :         for (i = edges.begin(); i != edges.end(); i++) {
    1122          22 :             extract(dc, *i, true);
    1123             :         }
    1124             :     }
    1125          12 : }
    1126             : 
    1127             : 
    1128             : void
    1129          13 : NBEdgeCont::guessOpposites() {
    1130             :     //@todo magic values
    1131          54 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1132          41 :         NBEdge* edge = i->second;
    1133          41 :         edge->guessOpposite();
    1134             :     }
    1135          13 : }
    1136             : 
    1137             : 
    1138             : void
    1139          50 : NBEdgeCont::recheckLaneSpread() {
    1140         896 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1141         846 :         NBEdge* opposite = getOppositeByID(i->first);
    1142         846 :         if (opposite != nullptr) {
    1143         344 :             i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
    1144         344 :             opposite->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
    1145             :         } else {
    1146         502 :             i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
    1147             :         }
    1148             :     }
    1149          50 : }
    1150             : 
    1151             : 
    1152             : NBEdge*
    1153        1392 : NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
    1154        1392 :     const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) :  "-" + edgeID;
    1155             :     EdgeCont::const_iterator it = myEdges.find(oppositeID);
    1156        1392 :     return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
    1157             : }
    1158             : 
    1159             : NBEdge*
    1160       26655 : NBEdgeCont::getByID(const std::string& edgeID) const {
    1161             :     EdgeCont::const_iterator it = myEdges.find(edgeID);
    1162       26655 :     return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
    1163             : }
    1164             : 
    1165             : // ----- other
    1166             : void
    1167          18 : NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
    1168             :                                      KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
    1169             :                                      const PositionVector& customShape, bool uncontrolled, bool warnOnly,
    1170             :                                      SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
    1171          36 :     myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
    1172             :                                   speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
    1173          18 : }
    1174             : 
    1175             : bool
    1176       13048 : NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
    1177             :     if (myConnections.count(from) == 0) {
    1178       13045 :         return false;
    1179             :     } else {
    1180           3 :         if (to == "") {
    1181             :             // wildcard
    1182             :             return true;
    1183             :         }
    1184           0 :         for (const auto& ppc : myConnections[from]) {
    1185           0 :             if (ppc.to == to) {
    1186             :                 return true;
    1187             :             }
    1188             :         }
    1189             :         return false;
    1190             :     }
    1191             : }
    1192             : 
    1193             : void
    1194        1837 : NBEdgeCont::recheckPostProcessConnections() {
    1195        7166 :     const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
    1196        1844 :     for (const auto& item : myConnections) {
    1197          25 :         for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
    1198          18 :             NBEdge* from = retrievePossiblySplit((*i).from, true);
    1199          18 :             NBEdge* to = retrievePossiblySplit((*i).to, false);
    1200          36 :             if (from == nullptr || to == nullptr ||
    1201          18 :                     !from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
    1202          18 :                                                   (*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
    1203          18 :                                                   (*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
    1204             :                                                   true)) {
    1205           2 :                 const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
    1206           1 :                 if (warnOnly || (*i).warnOnly) {
    1207           0 :                     WRITE_WARNING(msg);
    1208             :                 } else {
    1209           3 :                     WRITE_ERROR(msg);
    1210             :                 }
    1211             :             }
    1212             :         }
    1213             :     }
    1214             :     // during loading we also kept some ambiguous connections in hope they might be valid after processing
    1215             :     // we need to make sure that all invalid connections are removed now
    1216      129964 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
    1217      128127 :         NBEdge* edge = it->second;
    1218             :         NBNode* to = edge->getToNode();
    1219             :         // make a copy because we may delete connections
    1220      128127 :         std::vector<NBEdge::Connection> connections = edge->getConnections();
    1221      212160 :         for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
    1222             :             NBEdge::Connection& c = *it_con;
    1223       84033 :             if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
    1224          12 :                 WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
    1225             :                               "' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
    1226           6 :                 edge->removeFromConnections(c.toEdge);
    1227             :             }
    1228             :         }
    1229      128127 :     }
    1230        1837 : }
    1231             : 
    1232             : 
    1233             : EdgeVector
    1234         842 : NBEdgeCont::getGeneratedFrom(const std::string& id) const {
    1235         842 :     int len = (int)id.length();
    1236             :     EdgeVector ret;
    1237      149420 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1238             :         std::string curr = (*i).first;
    1239             :         // the next check makes it possibly faster - we don not have
    1240             :         //  to compare the names
    1241      148578 :         if ((int)curr.length() <= len) {
    1242       55988 :             continue;
    1243             :         }
    1244             :         // the name must be the same as the given id but something
    1245             :         //  beginning with a '[' must be appended to it
    1246      185180 :         if (curr.substr(0, len) == id && curr[len] == '[') {
    1247        4828 :             ret.push_back((*i).second);
    1248        4828 :             continue;
    1249             :         }
    1250             :         // ok, maybe the edge is a compound made during joining of edges
    1251             :         std::string::size_type pos = curr.find(id);
    1252             :         // surely not
    1253       87762 :         if (pos == std::string::npos) {
    1254       75438 :             continue;
    1255             :         }
    1256             :         // check leading char
    1257       12324 :         if (pos > 0) {
    1258        3975 :             if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
    1259             :                 // actually, this is another id
    1260        3975 :                 continue;
    1261             :             }
    1262             :         }
    1263        8349 :         if (pos + id.length() < curr.length()) {
    1264        8349 :             if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
    1265             :                 // actually, this is another id
    1266        8349 :                 continue;
    1267             :             }
    1268             :         }
    1269           0 :         ret.push_back((*i).second);
    1270             :     }
    1271         842 :     return ret;
    1272             : }
    1273             : 
    1274             : 
    1275             : int
    1276        1933 : NBEdgeCont::guessRoundabouts() {
    1277             :     myGuessedRoundabouts.clear();
    1278             :     std::set<NBEdge*> loadedRoundaboutEdges;
    1279        2026 :     for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
    1280          93 :         loadedRoundaboutEdges.insert(it->begin(), it->end());
    1281             :     }
    1282             :     // step 1: keep only those edges which have no turnarounds and which are not
    1283             :     // part of a loaded roundabout
    1284             :     std::set<NBEdge*> candidates;
    1285        1933 :     SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
    1286      184433 :     for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1287      182500 :         NBEdge* e = (*i).second;
    1288             :         NBNode* const to = e->getToNode();
    1289      182500 :         if (e->getTurnDestination() == nullptr
    1290       90343 :                 && to->getConnectionTo(e->getFromNode()) == nullptr
    1291      256061 :                 && (e->getPermissions() & valid) != 0) {
    1292             :             candidates.insert(e);
    1293             :         }
    1294             :     }
    1295             : 
    1296             :     // step 2:
    1297             :     std::set<NBEdge*> visited;
    1298       40876 :     for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
    1299             :         EdgeVector loopEdges;
    1300             :         // start with a random edge (this doesn't have to be a roundabout edge)
    1301             :         // loop over connected edges (using always the leftmost one)
    1302             :         // and keep the list in loopEdges
    1303             :         // continue until we loop back onto a loopEdges and extract the loop
    1304       38943 :         NBEdge* e = (*i);
    1305       18383 :         if (visited.count(e) > 0) {
    1306             :             // already seen
    1307             :             continue;
    1308             :         }
    1309       20560 :         loopEdges.push_back(e);
    1310             :         bool doLoop = true;
    1311             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1312             :         gDebugFlag1 = e->getID() == DEBUG_EDGE_ID;
    1313             : #endif
    1314             :         do {
    1315             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1316             :             if (gDebugFlag1) {
    1317             :                 std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
    1318             :                 gDebugFlag1 = true;
    1319             :             }
    1320             : #endif
    1321             :             visited.insert(e);
    1322       42600 :             const EdgeVector& edges = e->getToNode()->getEdges();
    1323       40876 :             if ((e->getToNode()->getType() == SumoXMLNodeType::RIGHT_BEFORE_LEFT || e->getToNode()->getType() == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
    1324       42600 :                     && !e->getToNode()->typeWasGuessed()) {
    1325             :                 doLoop = false;
    1326             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1327             :                 if (gDebugFlag1) {
    1328             :                     std::cout << " rbl\n";
    1329             :                 }
    1330             :                 gDebugFlag1 = false;
    1331             : #endif
    1332       14734 :                 break;
    1333             :             }
    1334       41377 :             if (edges.size() < 2) {
    1335             :                 doLoop = false;
    1336             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1337             :                 if (gDebugFlag1) {
    1338             :                     std::cout << " deadend\n";
    1339             :                 }
    1340             :                 gDebugFlag1 = false;
    1341             : #endif
    1342             :                 break;
    1343             :             }
    1344       37723 :             if (e->getTurnDestination() != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
    1345             :                 // do not follow turn-arounds while in a (tentative) loop
    1346             :                 doLoop = false;
    1347             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1348             :                 if (gDebugFlag1) {
    1349             :                     std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
    1350             :                 }
    1351             :                 gDebugFlag1 = false;
    1352             : #endif
    1353             :                 break;
    1354             :             }
    1355       34066 :             EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
    1356       34066 :             NBContHelper::nextCW(edges, me);
    1357       34066 :             NBEdge* left = *me;
    1358       36766 :             while ((left->getPermissions() & valid) == 0 && left != e) {
    1359        2700 :                 NBContHelper::nextCW(edges, me);
    1360        2700 :                 left = *me;
    1361             :             }
    1362       34066 :             if (left == e) {
    1363             :                 // no usable continuation edge found
    1364             :                 doLoop = false;
    1365             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1366             :                 if (gDebugFlag1) {
    1367             :                     std::cout << " noContinuation\n";
    1368             :                 }
    1369             :                 gDebugFlag1 = false;
    1370             : #endif
    1371             :                 break;
    1372             :             }
    1373       34031 :             NBContHelper::nextCW(edges, me);
    1374       34031 :             NBEdge* nextLeft = *me;
    1375       34031 :             double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
    1376       34031 :             double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
    1377             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1378             :             if (gDebugFlag1) {
    1379             :                 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";
    1380             :             }
    1381             : #endif
    1382             :             if (angle >= 120
    1383       36947 :                     || (angle >= 90 &&
    1384             :                         // if the edges are long or the junction shape is small we should expect roundness (low angles)
    1385        2916 :                         (MAX2(e->getLength(), left->getLength()) > 5
    1386         459 :                          || e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) < 10
    1387             :                          // there should be no straigher edge further left
    1388         120 :                          || (nextAngle < 45)
    1389             :                         ))) {
    1390             :                 // roundabouts do not have sharp turns (or they wouldn't be called 'round')
    1391             :                 // 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 120
    1392             :                 doLoop = false;
    1393             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1394             :                 if (gDebugFlag1) {
    1395             :                     std::cout << "     failed angle=" << angle << "\n";
    1396             :                 }
    1397             :                 gDebugFlag1 = false;
    1398             : #endif
    1399             :                 break;
    1400             :             }
    1401       28059 :             EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
    1402       28059 :             const int loopSize = (int)(loopEdges.end() - loopClosed);
    1403       28059 :             if (loopSize > 0) {
    1404             :                 // loop found
    1405         193 :                 if (loopSize < 3) {
    1406             :                     doLoop = false; // need at least 3 edges for a roundabout
    1407         118 :                 } else if (loopSize < (int)loopEdges.size()) {
    1408             :                     // remove initial edges not belonging to the loop
    1409         102 :                     EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
    1410             :                 }
    1411             :                 // count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
    1412             :                 int attachments = 0;
    1413        1137 :                 for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
    1414         944 :                     if ((*j)->getToNode()->getEdges().size() > 2) {
    1415         602 :                         attachments++;
    1416             :                     }
    1417             :                 }
    1418         193 :                 if (attachments < 3) {
    1419             :                     doLoop = false;
    1420             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1421             :                     if (gDebugFlag1) {
    1422             :                         std::cout << " attachments=" << attachments << "\n";
    1423             :                     }
    1424             :                     gDebugFlag1 = false;
    1425             : #endif
    1426             :                 }
    1427             :                 break;
    1428             :             }
    1429             :             if (visited.count(left) > 0) {
    1430             :                 doLoop = false;
    1431             :             } else {
    1432             :                 // keep going
    1433       22040 :                 loopEdges.push_back(left);
    1434       22040 :                 e = left;
    1435             :             }
    1436             :         } while (doLoop);
    1437       20480 :         if (doLoop) {
    1438             :             // check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
    1439             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1440             :             if (gDebugFlag1) {
    1441             :                 std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
    1442             :             }
    1443             : #endif
    1444             :             double loopLength = 0;
    1445         675 :             for (const NBEdge* const le : loopEdges) {
    1446         569 :                 loopLength += le->getLoadedLength();
    1447             :             }
    1448         106 :             if (formFactor(loopEdges) > 0.6
    1449         208 :                     && loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
    1450             :                 // collected edges are marked in markRoundabouts
    1451         100 :                 EdgeSet guessed(loopEdges.begin(), loopEdges.end());
    1452             :                 if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
    1453          85 :                     if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
    1454           2 :                         for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
    1455             :                             if ((*it).count(loopEdges.front()) != 0) {
    1456           8 :                                 WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
    1457             :                                 myRoundabouts.erase(it);
    1458             :                                 break;
    1459             :                             }
    1460             :                         }
    1461             :                         myGuessedRoundabouts.insert(guessed);
    1462             :                     }
    1463             :                 } else {
    1464             :                     myGuessedRoundabouts.insert(guessed);
    1465             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1466             :                     if (gDebugFlag1) {
    1467             :                         std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
    1468             :                     }
    1469             : #endif
    1470             :                 }
    1471             :             }
    1472             :         }
    1473             : #ifdef DEBUG_GUESS_ROUNDABOUT
    1474             :         gDebugFlag1 = false;
    1475             : #endif
    1476             :     }
    1477        3866 :     return (int)myGuessedRoundabouts.size();
    1478             : }
    1479             : 
    1480             : 
    1481             : int
    1482         172 : NBEdgeCont::extractRoundabouts() {
    1483             :     std::set<NBEdge*> candidateEdges;
    1484       62905 :     for (const auto& edge : myEdges) {
    1485       62733 :         NBEdge* const e = edge.second;
    1486       62733 :         if (e->getJunctionPriority(e->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT || e->getJunctionPriority(e->getFromNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
    1487             :             candidateEdges.insert(e);
    1488             :         }
    1489             :     }
    1490             :     std::set<NBEdge*> visited;
    1491             :     int extracted = 0;
    1492         231 :     for (const auto& edgeIt : candidateEdges) {
    1493             :         EdgeVector loopEdges;
    1494          59 :         NBEdge* e = edgeIt;
    1495          48 :         if (visited.count(e) > 0) {
    1496             :             // already seen
    1497             :             continue;
    1498             :         }
    1499          11 :         loopEdges.push_back(e);
    1500             :         bool doLoop = true;
    1501             :         //
    1502             :         do {
    1503          70 :             if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
    1504          11 :                 if (loopEdges.size() > 1) {
    1505          11 :                     addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
    1506          11 :                     ++extracted;
    1507             :                 }
    1508             :                 doLoop = false;
    1509             :                 break;
    1510             :             }
    1511             :             visited.insert(e);
    1512          59 :             loopEdges.push_back(e);
    1513          59 :             const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
    1514          59 :             EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
    1515          83 :                 return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
    1516             :             });
    1517          59 :             if (me == outgoingEdges.end()) { // no closed loop
    1518             :                 doLoop = false;
    1519             :             } else {
    1520          59 :                 e = *me;
    1521             :             }
    1522             :         } while (doLoop);
    1523             :     }
    1524         172 :     return extracted;
    1525             : }
    1526             : 
    1527             : 
    1528             : double
    1529         106 : NBEdgeCont::formFactor(const EdgeVector& loopEdges) {
    1530             :     // A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
    1531         106 :     PositionVector points;
    1532         675 :     for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
    1533         569 :         points.append((*it)->getGeometry());
    1534             :     }
    1535         106 :     double circumference = points.length2D();
    1536         212 :     return 4 * M_PI * points.area() / (circumference * circumference);
    1537         106 : }
    1538             : 
    1539             : 
    1540             : const std::set<EdgeSet>
    1541        7485 : NBEdgeCont::getRoundabouts() const {
    1542             :     std::set<EdgeSet> result = myRoundabouts;
    1543        7485 :     result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
    1544        7485 :     return result;
    1545             : }
    1546             : 
    1547             : 
    1548             : void
    1549          89 : NBEdgeCont::addRoundabout(const EdgeSet& roundabout) {
    1550          89 :     if (roundabout.size() > 0) {
    1551          87 :         if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
    1552           0 :             WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
    1553             :         } else {
    1554             :             myRoundabouts.insert(roundabout);
    1555             :         }
    1556             :     }
    1557          89 : }
    1558             : 
    1559             : void
    1560           4 : NBEdgeCont::removeRoundabout(const NBNode* node) {
    1561           4 :     for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
    1562           2 :         for (NBEdge* e : *it) {
    1563           2 :             if (e->getToNode() == node) {
    1564             :                 myRoundabouts.erase(it);
    1565             :                 return;
    1566             :             }
    1567             :         }
    1568             :     }
    1569             : }
    1570             : 
    1571             : void
    1572          23 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove) {
    1573          23 :     removeRoundaboutEdges(toRemove, myRoundabouts);
    1574          23 :     removeRoundaboutEdges(toRemove, myGuessedRoundabouts);
    1575          23 : }
    1576             : 
    1577             : void
    1578          46 : NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
    1579             :     // members of a set are constant so we have to do some tricks
    1580             :     std::vector<EdgeSet> rList;
    1581          48 :     for (const EdgeSet& r : roundabouts) {
    1582             :         EdgeSet r2;
    1583           2 :         std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
    1584           2 :         rList.push_back(r2);
    1585             :     }
    1586             :     roundabouts.clear();
    1587             :     roundabouts.insert(rList.begin(), rList.end());
    1588          46 : }
    1589             : 
    1590             : 
    1591             : void
    1592        1837 : NBEdgeCont::markRoundabouts() {
    1593        1935 :     for (const EdgeSet& roundaboutSet : getRoundabouts()) {
    1594         568 :         for (NBEdge* const edge : roundaboutSet) {
    1595             :             // disable turnarounds on incoming edges
    1596             :             NBNode* const node = edge->getToNode();
    1597        1267 :             for (NBEdge* const inEdge : node->getIncomingEdges()) {
    1598         470 :                 if (roundaboutSet.count(inEdge) > 0) {
    1599         470 :                     continue;
    1600             :                 }
    1601         327 :                 if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
    1602         234 :                     continue;
    1603             :                 }
    1604          93 :                 if (inEdge->getTurnDestination() != nullptr) {
    1605          38 :                     inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
    1606             :                 } else {
    1607             :                     // also remove connections that are effecively a turnaround but
    1608             :                     // where not correctly detector due to geometrical quirks
    1609          55 :                     const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
    1610         120 :                     for (const NBEdge::Connection& con : cons) {
    1611          65 :                         if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
    1612          10 :                             const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
    1613          10 :                             if (angle > 160) {
    1614           1 :                                 inEdge->removeFromConnections(con.toEdge, -1);
    1615             :                             }
    1616             :                         }
    1617             :                     }
    1618          55 :                 }
    1619             : 
    1620             :             }
    1621             :             // let the connections to succeeding roundabout edge have a higher priority
    1622         470 :             edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
    1623         470 :             edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
    1624         470 :             node->setRoundabout();
    1625             :         }
    1626             :     }
    1627        1837 : }
    1628             : 
    1629             : 
    1630             : void
    1631           1 : NBEdgeCont::generateStreetSigns() {
    1632          23 :     for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
    1633          22 :         NBEdge* e = i->second;
    1634          22 :         const double offset = MAX2(0., e->getLength() - 3);
    1635          22 :         if (e->getToNode()->isSimpleContinuation(false)) {
    1636             :             // not a "real" junction?
    1637          10 :             continue;
    1638             :         }
    1639             :         const SumoXMLNodeType nodeType = e->getToNode()->getType();
    1640          12 :         switch (nodeType) {
    1641           4 :             case SumoXMLNodeType::PRIORITY:
    1642             :                 // yield or major?
    1643           4 :                 if (e->getJunctionPriority(e->getToNode()) > 0) {
    1644           4 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
    1645             :                 } else {
    1646           4 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_YIELD, offset));
    1647             :                 }
    1648             :                 break;
    1649           0 :             case SumoXMLNodeType::PRIORITY_STOP:
    1650             :                 // yield or major?
    1651           0 :                 if (e->getJunctionPriority(e->getToNode()) > 0) {
    1652           0 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
    1653             :                 } else {
    1654           0 :                     e->addSign(NBSign(NBSign::SIGN_TYPE_STOP, offset));
    1655             :                 }
    1656             :                 break;
    1657           0 :             case SumoXMLNodeType::ALLWAY_STOP:
    1658           0 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_ALLWAY_STOP, offset));
    1659           0 :                 break;
    1660           4 :             case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
    1661           8 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_RIGHT_BEFORE_LEFT, offset));
    1662           4 :                 break;
    1663           0 :             case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
    1664           0 :                 e->addSign(NBSign(NBSign::SIGN_TYPE_LEFT_BEFORE_RIGHT, offset));
    1665           0 :                 break;
    1666             :             default:
    1667             :                 break;
    1668             :         }
    1669             :     }
    1670           1 : }
    1671             : 
    1672             : 
    1673             : int
    1674          19 : NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
    1675             :                               NBTrafficLightLogicCont& tlc) {
    1676             :     int lanesCreated = 0;
    1677             :     std::vector<std::string> edges;
    1678          19 :     if (excludeOpt != "") {
    1679          19 :         edges = OptionsCont::getOptions().getStringVector(excludeOpt);
    1680             :     }
    1681          19 :     std::set<std::string> exclude(edges.begin(), edges.end());
    1682         429 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1683         410 :         NBEdge* edge = it->second;
    1684             :         if (// not excluded
    1685         410 :             exclude.count(edge->getID()) == 0
    1686             :             // does not yet have a sidewalk
    1687         407 :             && !edge->hasRestrictedLane(svc)
    1688         289 :             && (
    1689             :                 // guess.from-permissions
    1690         139 :                 (fromPermissions && (edge->getPermissions() & svc) != 0)
    1691             :                 // guess from speed
    1692         160 :                 || (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
    1693             :             )) {
    1694         225 :             edge->addRestrictedLane(width, svc);
    1695         225 :             lanesCreated += 1;
    1696         225 :             if (svc != SVC_PEDESTRIAN) {
    1697          34 :                 edge->invalidateConnections(true);
    1698          34 :                 edge->getFromNode()->invalidateOutgoingConnections(true);
    1699          34 :                 edge->getFromNode()->invalidateTLS(tlc, true, true);
    1700          34 :                 edge->getToNode()->invalidateTLS(tlc, true, true);
    1701             :             }
    1702             :         }
    1703             :     }
    1704          19 :     return lanesCreated;
    1705          19 : }
    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           0 :     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        1837 : NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBPTStopCont& sc) {
    1730        1837 :     bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
    1731        3638 :     if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
    1732             :         return 0;
    1733             :     }
    1734             :     std::vector<std::string> avoid;
    1735          46 :     if (startGiven) {
    1736           9 :         avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
    1737             :     } else {
    1738          43 :         avoid = getAllNames();
    1739             :     }
    1740             :     std::set<std::string> reserve;
    1741          46 :     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          92 :     IDSupplier idSupplier("", avoid);
    1746             :     std::set<NBEdge*, ComparatorIdLess> toChange;
    1747       29422 :     for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1748       29376 :         if (startGiven) {
    1749          78 :             toChange.insert(it->second);
    1750          78 :             continue;
    1751             :         }
    1752       29298 :         if (numericaIDs) {
    1753             :             try {
    1754       29124 :                 StringUtils::toLong(it->first);
    1755       11156 :             } catch (NumberFormatException&) {
    1756       11156 :                 toChange.insert(it->second);
    1757       11156 :             }
    1758             :         }
    1759       29298 :         if (reservedIDs && reserve.count(it->first) > 0) {
    1760           2 :             toChange.insert(it->second);
    1761             :         }
    1762             :     }
    1763             : 
    1764             :     std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
    1765          70 :     for (const auto& item : sc.getStops()) {
    1766          24 :         stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
    1767             :     }
    1768             : 
    1769          92 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    1770       11282 :     for (NBEdge* edge : toChange) {
    1771       11236 :         myEdges.erase(edge->getID());
    1772             :     }
    1773       11282 :     for (NBEdge* edge : toChange) {
    1774       11236 :         const std::string origID = edge->getID();
    1775       11236 :         if (origNames) {
    1776       21272 :             edge->setOrigID(origID, false);
    1777             :         }
    1778       11236 :         edge->setID(idSupplier.getNext());
    1779       11236 :         myEdges[edge->getID()] = edge;
    1780       11255 :         for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
    1781          38 :             stop->setEdgeId(prefix + edge->getID(), *this);
    1782             :         }
    1783             :     }
    1784          46 :     if (prefix.empty()) {
    1785          38 :         return (int)toChange.size();
    1786             :     } else {
    1787             :         int renamed = 0;
    1788             :         // make a copy because we will modify the map
    1789             :         auto oldEdges = myEdges;
    1790         190 :         for (auto item : oldEdges) {
    1791         364 :             if (!StringUtils::startsWith(item.first, prefix)) {
    1792         181 :                 rename(item.second, prefix + item.first);
    1793         181 :                 renamed++;
    1794             :             }
    1795             :         }
    1796             :         return renamed;
    1797             :     }
    1798          92 : }
    1799             : 
    1800             : 
    1801             : void
    1802           0 : NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
    1803           0 :     for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1804           0 :         const NBEdge* e1 = it->second;
    1805           0 :         Boundary b1 = e1->getGeometry().getBoxBoundary();
    1806           0 :         b1.grow(e1->getTotalWidth());
    1807           0 :         PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
    1808           0 :         outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
    1809             :         // check is symmetric. only check once per pair
    1810           0 :         for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
    1811           0 :             const NBEdge* e2 = it2->second;
    1812           0 :             if (e1 == e2) {
    1813           0 :                 continue;
    1814             :             }
    1815           0 :             Boundary b2 = e2->getGeometry().getBoxBoundary();
    1816           0 :             b2.grow(e2->getTotalWidth());
    1817           0 :             if (b1.overlapsWith(b2)) {
    1818           0 :                 PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
    1819           0 :                 outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
    1820           0 :                 const double overlap = outline1.getOverlapWith(outline2, zThreshold);
    1821           0 :                 if (overlap > threshold) {
    1822           0 :                     WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
    1823             :                 }
    1824           0 :             }
    1825           0 :         }
    1826           0 :     }
    1827           0 : }
    1828             : 
    1829             : 
    1830             : void
    1831          23 : NBEdgeCont::checkGrade(double threshold) const {
    1832        2830 :     for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
    1833        2807 :         const NBEdge* edge = it->second;
    1834        5402 :         for (int i = 0; i < (int)edge->getNumLanes(); i++) {
    1835        2890 :             double maxJump = 0;
    1836        2890 :             const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
    1837        2890 :             if (maxJump > 0.01) {
    1838           0 :                 WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
    1839        2890 :             } else if (grade > threshold) {
    1840         885 :                 WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
    1841         295 :                 break;
    1842             :             }
    1843             :         }
    1844             :         const std::vector<NBEdge::Connection>& connections = edge->getConnections();
    1845        3863 :         for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
    1846             :             const NBEdge::Connection& c = *it_con;
    1847        1060 :             double maxJump = 0;
    1848        1060 :             const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
    1849        1060 :             if (maxJump > 0.01) {
    1850           0 :                 WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
    1851        1060 :             } else if (grade > threshold) {
    1852           8 :                 WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
    1853           4 :                 break;
    1854             :             }
    1855             :         }
    1856             :     }
    1857          23 : }
    1858             : 
    1859             : 
    1860             : int
    1861           2 : NBEdgeCont::joinLanes(SVCPermissions perms) {
    1862             :     int affectedEdges = 0;
    1863          38 :     for (auto item : myEdges) {
    1864          36 :         if (item.second->joinLanes(perms)) {
    1865          18 :             affectedEdges++;
    1866             :         }
    1867             :     }
    1868           2 :     return affectedEdges;
    1869             : }
    1870             : 
    1871             : 
    1872             : bool
    1873        2898 : NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
    1874        2898 :     if (a.first->getID() == b.first->getID()) {
    1875          44 :         return a.second < b.second;
    1876             :     }
    1877        2854 :     return a.first->getID() < b.first->getID();
    1878             : }
    1879             : 
    1880             : int
    1881           6 : NBEdgeCont::joinTramEdges(NBDistrictCont& dc, NBPTStopCont& sc, NBPTLineCont& lc, double maxDist) {
    1882             :     // this is different from joinSimilarEdges because there don't need to be
    1883             :     // shared nodes and tram edges may be split
    1884             :     std::vector<NBEdge*> tramEdges;
    1885             :     std::vector<NBEdge*> targetEdges;
    1886        2225 :     for (auto item : myEdges) {
    1887        2219 :         SVCPermissions permissions = item.second->getPermissions();
    1888        2219 :         if (isTram(permissions)) {
    1889         486 :             if (item.second->getNumLanes() == 1) {
    1890         458 :                 tramEdges.push_back(item.second);
    1891             :             } else {
    1892          84 :                 WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
    1893             :             }
    1894        1733 :         } else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
    1895        1165 :             targetEdges.push_back(item.second);
    1896             :         }
    1897             :     }
    1898           6 :     if (tramEdges.empty() || targetEdges.empty()) {
    1899             :         return 0;
    1900             :     }
    1901             :     int numJoined = 0;
    1902             :     NamedRTree tramTree;
    1903         464 :     for (NBEdge* const edge : tramEdges) {
    1904         458 :         const Boundary& bound = edge->getGeometry().getBoxBoundary();
    1905         458 :         float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
    1906         458 :         float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
    1907         458 :         tramTree.Insert(min, max, edge);
    1908         458 :     }
    1909             :     // {targetEdge, laneIndex : tramEdge}
    1910             :     std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
    1911             : 
    1912        1171 :     for (NBEdge* const edge : targetEdges) {
    1913        1165 :         Boundary bound = edge->getGeometry().getBoxBoundary();
    1914        1165 :         bound.grow(maxDist + edge->getTotalWidth());
    1915        1165 :         float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
    1916        1165 :         float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
    1917             :         std::set<const Named*> near;
    1918             :         Named::StoringVisitor visitor(near);
    1919             :         tramTree.Search(min, max, visitor);
    1920             :         // the nearby set is actually just re-sorting according to the id to make the tests comparable
    1921             :         std::set<NBEdge*, ComparatorIdLess> nearby;
    1922        4721 :         for (const Named* namedEdge : near) {
    1923        3556 :             nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
    1924             :         }
    1925        4721 :         for (NBEdge* const tramEdge : nearby) {
    1926             :             // find a continous stretch of tramEdge that runs along one of the
    1927             :             // lanes of the road edge
    1928             :             const PositionVector& tramShape = tramEdge->getGeometry();
    1929        3556 :             double minEdgeDist = maxDist + 1;
    1930             :             int minLane = -1;
    1931             :             // find the lane where the maximum distance from the tram geometry
    1932             :             // is minimal and within maxDist
    1933       10405 :             for (int i = 0; i < edge->getNumLanes(); i++) {
    1934             :                 double maxLaneDist = -1;
    1935        6849 :                 if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
    1936        6327 :                     const PositionVector& laneShape = edge->getLaneShape(i);
    1937        7373 :                     for (Position pos : laneShape) {
    1938        7119 :                         const double dist = tramShape.distance2D(pos, false);
    1939             : #ifdef DEBUG_JOIN_TRAM
    1940             :                         //if (edge->getID() == "106838214#1") {
    1941             :                         //    std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
    1942             :                         //}
    1943             : #endif
    1944        7119 :                         if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
    1945             :                             maxLaneDist = -1;
    1946             :                             break;
    1947             :                         }
    1948             :                         maxLaneDist = MAX2(maxLaneDist, dist);
    1949             :                     }
    1950        6327 :                     if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
    1951             :                         minEdgeDist = maxLaneDist;
    1952             :                         minLane = i;
    1953             :                     }
    1954             :                 }
    1955             :             }
    1956        3556 :             if (minLane >= 0) {
    1957             :                 // edge could run in the wrong direction and still fit the threshold we check the angle as well
    1958         248 :                 const PositionVector& laneShape = edge->getLaneShape(minLane);
    1959         248 :                 const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
    1960         248 :                 const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
    1961         248 :                 Position p1 = tramShape.positionAtOffset2D(offset1);
    1962         248 :                 Position p2 = tramShape.positionAtOffset2D(offset2);
    1963         248 :                 double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
    1964         248 :                 bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
    1965         248 :                 if (angleOK && offset2 > offset1) {
    1966         198 :                     std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
    1967             :                     if (matches.count(key) == 0) {
    1968         188 :                         matches[key] = tramEdge;
    1969             :                     } else {
    1970          40 :                         WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
    1971             :                     }
    1972             : #ifdef DEBUG_JOIN_TRAM
    1973             :                     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";
    1974             : #endif
    1975             :                 }
    1976             :             }
    1977             :         }
    1978        1165 :     }
    1979           6 :     if (matches.size() == 0) {
    1980             :         return 0;
    1981             :     }
    1982          12 :     const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
    1983             :     // find continous runs of matched edges for each tramEdge
    1984         464 :     for (NBEdge* tramEdge : tramEdges) {
    1985             :         std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
    1986       14710 :         for (auto item : matches) {
    1987       14252 :             if (item.second == tramEdge) {
    1988             :                 NBEdge* road = item.first.first;
    1989             :                 int laneIndex = item.first.second;
    1990         188 :                 const PositionVector& laneShape = road->getLaneShape(laneIndex);
    1991         188 :                 double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
    1992         188 :                 roads.push_back(std::make_pair(tramPos, item.first));
    1993             :             }
    1994             :         }
    1995         458 :         if (roads.size() != 0) {
    1996             : 
    1997          55 :             sort(roads.begin(), roads.end());
    1998             : #ifdef DEBUG_JOIN_TRAM
    1999             :             std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
    2000             :             for (auto item : roads) {
    2001             :                 std::cout << item.second.first->getLaneID(item.second.second) << ",";
    2002             :             }
    2003             :             std::cout << " offsets=";
    2004             :             for (auto item : roads) {
    2005             :                 std::cout << item.first << ",";
    2006             :             }
    2007             :             std::cout << "\n";
    2008             : #endif
    2009             :             // merge tramEdge into road lanes
    2010             :             EdgeVector replacement;
    2011             :             double pos = 0;
    2012             :             int tramPart = 0;
    2013          55 :             std::string tramEdgeID = tramEdge->getID();
    2014             :             NBNode* tramFrom = tramEdge->getFromNode();
    2015             :             PositionVector tramShape = tramEdge->getGeometry();
    2016          55 :             const double tramLength = tramShape.length();
    2017          55 :             EdgeVector incoming = tramFrom->getIncomingEdges();
    2018             :             bool erasedLast = false;
    2019         243 :             for (const auto& item : roads) {
    2020         188 :                 const double gap = item.first - pos;
    2021         188 :                 NBEdge* road = item.second.first;
    2022         188 :                 int laneIndex = item.second.second;
    2023         188 :                 if (gap >= JOIN_TRAM_MIN_LENGTH) {
    2024             : #ifdef DEBUG_JOIN_TRAM
    2025             :                     std::cout << "    splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
    2026             : #endif
    2027          72 :                     const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
    2028          36 :                     splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
    2029          36 :                     tramEdge = retrieve(tramEdgeID); // second part;
    2030          36 :                     NBEdge* firstPart = retrieve(firstPartID);
    2031          36 :                     firstPart->invalidateConnections(true);
    2032             :                     incoming.clear();
    2033          36 :                     incoming.push_back(firstPart);
    2034          36 :                     replacement.push_back(firstPart);
    2035             :                 }
    2036         188 :                 pos = item.first + road->getGeometry().length();
    2037         188 :                 numJoined++;
    2038         188 :                 replacement.push_back(road);
    2039             :                 // merge section of tramEdge into road lane
    2040         188 :                 if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
    2041         164 :                     tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
    2042         164 :                     tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
    2043             :                     erasedLast = false;
    2044             : #ifdef DEBUG_JOIN_TRAM
    2045             :                     std::cout << "    shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
    2046             : #endif
    2047             :                 } else {
    2048             : #ifdef DEBUG_JOIN_TRAM
    2049             :                     std::cout << "    erased tramEdge=" << tramEdge->getID() << "\n";
    2050             : #endif
    2051          24 :                     extract(dc, tramEdge, true);
    2052             :                     erasedLast = true;
    2053             :                 }
    2054         188 :                 road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
    2055         188 :                 if (origNames) {
    2056         114 :                     road->setOrigID(tramEdgeID, true, laneIndex);
    2057             :                 }
    2058         263 :                 for (NBEdge* in : incoming) {
    2059          75 :                     if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
    2060          47 :                         if (in->getFromNode() != road->getFromNode()) {
    2061          44 :                             in->reinitNodes(in->getFromNode(), road->getFromNode());
    2062             :                         } else {
    2063           3 :                             extract(dc, in, true);
    2064             : #ifdef DEBUG_JOIN_TRAM
    2065             :                             std::cout << "    erased incoming tramEdge=" << in->getID() << "\n";
    2066             : #endif
    2067             :                         }
    2068             :                     }
    2069             :                 }
    2070             :                 incoming.clear();
    2071             :             }
    2072          55 :             NBEdge* lastRoad = roads.back().second.first;
    2073          55 :             if (erasedLast) {
    2074             :                 // copy to avoid concurrent modification
    2075          22 :                 auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
    2076          68 :                 for (NBEdge* out : outEdges) {
    2077          46 :                     if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
    2078          25 :                         if (lastRoad->getToNode() != out->getToNode()) {
    2079          24 :                             out->reinitNodes(lastRoad->getToNode(), out->getToNode());
    2080             :                         } else {
    2081           1 :                             extract(dc, out, true);
    2082             : #ifdef DEBUG_JOIN_TRAM
    2083             :                             std::cout << "    erased outgoing tramEdge=" << out->getID() << "\n";
    2084             : #endif
    2085             : 
    2086             :                         }
    2087             :                     }
    2088             :                 }
    2089             :             } else {
    2090          33 :                 replacement.push_back(tramEdge);
    2091             :             }
    2092             :             // update ptstops and ptlines
    2093          55 :             sc.replaceEdge(tramEdgeID, replacement);
    2094          55 :             lc.replaceEdge(tramEdgeID, replacement);
    2095          55 :         }
    2096             :     }
    2097             : 
    2098           6 :     return numJoined;
    2099             : }
    2100             : 
    2101             : 
    2102             : EdgeVector
    2103         165 : NBEdgeCont::getAllEdges() const {
    2104             :     EdgeVector result;
    2105       85914 :     for (auto item : myEdges) {
    2106       85749 :         item.second->setNumericalID((int)result.size());
    2107       85749 :         result.push_back(item.second);
    2108             :     }
    2109         165 :     return result;
    2110             : }
    2111             : 
    2112             : RouterEdgeVector
    2113          69 : NBEdgeCont::getAllRouterEdges() const {
    2114          69 :     EdgeVector all = getAllEdges();
    2115         138 :     return RouterEdgeVector(all.begin(), all.end());
    2116             : }
    2117             : 
    2118             : bool
    2119        2046 : NBEdgeCont::checkConsistency(const NBNodeCont& nc) {
    2120             :     bool ok = true;
    2121      127520 :     for (const auto& item : myEdges) {
    2122      125474 :         NBEdge* e = item.second;
    2123      125474 :         if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
    2124           4 :             WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
    2125             :             ok = false;
    2126             :         }
    2127      125474 :         if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
    2128           4 :             WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
    2129             :             ok = false;
    2130             :         }
    2131             : 
    2132             :     }
    2133        2046 :     return ok;
    2134             : }
    2135             : 
    2136             : 
    2137             : void
    2138          79 : NBEdgeCont::fixSplitCustomLength() {
    2139        2344 :     for (auto item : myEdges) {
    2140             :         NBEdge* e = item.second;
    2141        2265 :         if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
    2142             :             // subtract half the length of the longest incoming / outgoing connection
    2143             :             double maxLengthOut = 0;
    2144           5 :             for (const NBEdge::Connection& c : e->getConnections()) {
    2145           3 :                 maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
    2146             :             }
    2147             :             double maxLengthIn = 0;
    2148           4 :             for (const NBEdge* in : e->getIncomingEdges()) {
    2149           7 :                 for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
    2150           5 :                     maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
    2151           2 :                 }
    2152             :             }
    2153           4 :             e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
    2154             :         }
    2155             :     }
    2156          79 : }
    2157             : 
    2158             : void
    2159           0 : NBEdgeCont::computeAngles() {
    2160           0 :     for (auto item : myEdges) {
    2161           0 :         item.second->computeAngle();
    2162             :     }
    2163           0 : }
    2164             : 
    2165             : 
    2166             : std::set<std::string>
    2167        1744 : NBEdgeCont::getUsedTypes() const {
    2168             :     std::set<std::string> result;
    2169      127646 :     for (auto item : myEdges) {
    2170      125902 :         if (item.second->getTypeID() != "") {
    2171       89004 :             result.insert(item.second->getTypeID());
    2172             :         }
    2173             :     }
    2174        1744 :     return result;
    2175             : }
    2176             : 
    2177             : 
    2178             : int
    2179           4 : NBEdgeCont::removeEdgesBySpeed(NBDistrictCont& dc) {
    2180             :     EdgeSet toRemove;
    2181          84 :     for (auto item : myEdges) {
    2182          80 :         NBEdge* edge = item.second;
    2183             :         // remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
    2184          80 :         if (edge->getSpeed() < myEdgesMinSpeed) {
    2185             :             toRemove.insert(edge);
    2186             :         }
    2187             :     }
    2188             :     int numRemoved = 0;
    2189           4 :     for (NBEdge* edge : toRemove) {
    2190             :         // explicit whitelist overrides removal
    2191           0 :         if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
    2192           0 :             extract(dc, edge);
    2193           0 :             numRemoved++;
    2194             :         }
    2195             :     }
    2196           4 :     return numRemoved;
    2197             : }
    2198             : 
    2199             : int
    2200           4 : NBEdgeCont::removeEdgesByPermissions(NBDistrictCont& dc) {
    2201             :     EdgeSet toRemove;
    2202          84 :     for (auto item : myEdges) {
    2203          80 :         NBEdge* edge = item.second;
    2204             :         // check whether the edge shall be removed because it does not allow any of the wished classes
    2205          80 :         if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
    2206             :             toRemove.insert(edge);
    2207             :         }
    2208             :         // check whether the edge shall be removed due to allowing unwished classes only
    2209          80 :         if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
    2210             :             toRemove.insert(edge);
    2211             :         }
    2212             :     }
    2213             :     int numRemoved = 0;
    2214          29 :     for (NBEdge* edge : toRemove) {
    2215             :         // explicit whitelist overrides removal
    2216          25 :         if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
    2217          25 :             extract(dc, edge);
    2218          25 :             numRemoved++;
    2219             :         }
    2220             :     }
    2221           4 :     return numRemoved;
    2222             : }
    2223             : /****************************************************************************/

Generated by: LCOV version 1.14