LCOV - code coverage report
Current view: top level - src/netimport - NIXMLEdgesHandler.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 382 416 91.8 %
Date: 2024-05-02 15:31:40 Functions: 12 13 92.3 %

          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    NIXMLEdgesHandler.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Jakob Erdmann
      17             : /// @author  Michael Behrisch
      18             : /// @author  Walter Bamberger
      19             : /// @author  Laura Bieker
      20             : /// @author  Leonhard Luecken
      21             : /// @date    Tue, 20 Nov 2001
      22             : ///
      23             : // Importer for network edges stored in XML
      24             : /****************************************************************************/
      25             : #include <config.h>
      26             : 
      27             : #include <string>
      28             : #include <iostream>
      29             : #include <map>
      30             : #include <cmath>
      31             : #include <utils/xml/SUMOSAXHandler.h>
      32             : #include <netbuild/NBNodeCont.h>
      33             : #include <netbuild/NBTypeCont.h>
      34             : #include <netbuild/NBNetBuilder.h>
      35             : #include <utils/xml/SUMOXMLDefinitions.h>
      36             : #include <utils/common/MsgHandler.h>
      37             : #include <utils/common/StringUtils.h>
      38             : #include <utils/common/StringTokenizer.h>
      39             : #include <utils/geom/GeomConvHelper.h>
      40             : #include <utils/common/ToString.h>
      41             : #include <utils/options/OptionsCont.h>
      42             : #include <utils/geom/GeoConvHelper.h>
      43             : #include "NIXMLNodesHandler.h"
      44             : #include "NIXMLEdgesHandler.h"
      45             : #include "NIImporter_SUMO.h"
      46             : 
      47             : 
      48             : // ===========================================================================
      49             : // method definitions
      50             : // ===========================================================================
      51        2222 : NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
      52             :                                      NBEdgeCont& ec,
      53             :                                      NBTypeCont& tc,
      54             :                                      NBDistrictCont& dc,
      55             :                                      NBTrafficLightLogicCont& tlc,
      56        2222 :                                      OptionsCont& options) :
      57             :     SUMOSAXHandler("xml-edges - file"),
      58        2222 :     myOptions(options),
      59        2222 :     myNodeCont(nc),
      60        2222 :     myEdgeCont(ec),
      61        2222 :     myTypeCont(tc),
      62        2222 :     myDistrictCont(dc),
      63        2222 :     myTLLogicCont(tlc),
      64        2222 :     myCurrentEdge(nullptr),
      65        2222 :     myCurrentLaneIndex(-1),
      66        2222 :     myHaveReportedAboutOverwriting(false),
      67        2222 :     myHaveReportedAboutTypeOverride(false),
      68        2222 :     myHaveWarnedAboutDeprecatedLaneId(false),
      69        6666 :     myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
      70        2222 : }
      71             : 
      72             : 
      73        2222 : NIXMLEdgesHandler::~NIXMLEdgesHandler() {
      74        2222 :     delete myLocation;
      75        4444 : }
      76             : 
      77             : 
      78             : void
      79       31662 : NIXMLEdgesHandler::myStartElement(int element,
      80             :                                   const SUMOSAXAttributes& attrs) {
      81       31662 :     switch (element) {
      82        1092 :         case SUMO_TAG_VIEWSETTINGS_EDGES:
      83             :             // infer location for legacy networks that don't have location information
      84        1092 :             myLocation = GeoConvHelper::getLoadedPlain(getFileName());
      85        1092 :             break;
      86          14 :         case SUMO_TAG_LOCATION:
      87          14 :             delete myLocation;
      88          14 :             myLocation = NIImporter_SUMO::loadLocation(attrs, false);
      89          14 :             break;
      90       22973 :         case SUMO_TAG_EDGE:
      91       22973 :             addEdge(attrs);
      92       22972 :             break;
      93        5544 :         case SUMO_TAG_LANE:
      94        5544 :             addLane(attrs);
      95        5544 :             break;
      96          13 :         case SUMO_TAG_NEIGH:
      97          13 :             myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
      98          13 :             break;
      99         246 :         case SUMO_TAG_SPLIT:
     100         246 :             addSplit(attrs);
     101         246 :             break;
     102          31 :         case SUMO_TAG_DEL:
     103          31 :             deleteEdge(attrs);
     104          31 :             break;
     105           9 :         case SUMO_TAG_ROUNDABOUT:
     106           9 :             addRoundabout(attrs);
     107           9 :             break;
     108        1715 :         case SUMO_TAG_PARAM:
     109        1715 :             if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
     110        1673 :                 bool ok = true;
     111        1673 :                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
     112             :                 // circumventing empty string test
     113        1673 :                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     114        1673 :                 myLastParameterised.back()->setParameter(key, val);
     115             :             }
     116             :             break;
     117          25 :         case SUMO_TAG_STOPOFFSET: {
     118          25 :             bool ok = true;
     119          25 :             const StopOffset stopOffset(attrs, ok);
     120          25 :             if (!ok) {
     121           1 :                 std::stringstream ss;
     122           2 :                 ss << "(Error encountered at lane " << myCurrentLaneIndex << " of edge '" << myCurrentID << "' while parsing stopOffsets.)";
     123           1 :                 WRITE_ERROR(ss.str());
     124           1 :             } else {
     125          24 :                 if (myCurrentEdge->getLaneStopOffset(myCurrentLaneIndex).isDefined()) {
     126           3 :                     std::stringstream ss;
     127           3 :                     ss << "Duplicate definition of stopOffset for ";
     128           3 :                     if (myCurrentLaneIndex != -1) {
     129           1 :                         ss << "lane " << myCurrentLaneIndex << " on ";
     130             :                     }
     131           6 :                     ss << "edge " << myCurrentEdge->getID() << ". Ignoring duplicate specification.";
     132           3 :                     WRITE_WARNING(ss.str());
     133          24 :                 } else if ((stopOffset.getOffset() > myCurrentEdge->getLength()) || (stopOffset.getOffset() < 0)) {
     134           4 :                     std::stringstream ss;
     135           4 :                     ss << "Ignoring invalid stopOffset for ";
     136           4 :                     if (myCurrentLaneIndex != -1) {
     137           2 :                         ss << "lane " << myCurrentLaneIndex << " on ";
     138             :                     }
     139           4 :                     ss << "edge " << myCurrentEdge->getID();
     140           4 :                     if (stopOffset.getOffset() > myCurrentEdge->getLength()) {
     141           2 :                         ss << " (offset larger than the edge length).";
     142             :                     } else {
     143           2 :                         ss << " (negative offset).";
     144             :                     }
     145           4 :                     WRITE_WARNING(ss.str());
     146           4 :                 } else {
     147          17 :                     myCurrentEdge->setEdgeStopOffset(myCurrentLaneIndex, stopOffset);
     148             :                 }
     149             :             }
     150             :         }
     151          25 :         break;
     152             :         default:
     153             :             break;
     154             :     }
     155       31661 : }
     156             : 
     157             : 
     158             : void
     159       22973 : NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
     160       22973 :     myIsUpdate = false;
     161       22973 :     bool ok = true;
     162             :     // initialise the edge
     163       22973 :     myCurrentEdge = nullptr;
     164             :     mySplits.clear();
     165             :     // get the id, report an error if not given or empty...
     166       22973 :     myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     167       22973 :     if (!ok) {
     168          59 :         return;
     169             :     }
     170       22969 :     myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
     171             :     // check deprecated (unused) attributes
     172             :     // use default values, first
     173       45938 :     myCurrentType = myOptions.getString("default.type");
     174       22969 :     myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
     175       22969 :     myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
     176       22969 :     myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
     177       22969 :     if (myCurrentEdge != nullptr) {
     178             :         // update existing edge. only update lane-specific settings when explicitly requested
     179        1879 :         myIsUpdate = true;
     180        1879 :         myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
     181        1879 :         myCurrentFriction = NBEdge::UNSPECIFIED_FRICTION;
     182        1879 :         myPermissions = SVC_UNSPECIFIED;
     183        1879 :         myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
     184             :         myCurrentType = myCurrentEdge->getTypeID();
     185        3758 :         myLanesSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(myOptions.getString("default.spreadtype"));
     186             :     } else {
     187             :         // this is a completely new edge. get the type specific defaults
     188       21090 :         myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
     189       21090 :         myCurrentFriction = myTypeCont.getEdgeTypeFriction(myCurrentType);
     190       21090 :         myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
     191       21090 :         myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
     192       21090 :         myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
     193             :     }
     194       45938 :     myShape = PositionVector();
     195       22969 :     myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
     196       22969 :     myCurrentStreetName = "";
     197       22969 :     myReinitKeepEdgeShape = false;
     198       22969 :     mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
     199       22969 :     myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
     200             :     // check whether a type's values shall be used
     201       22969 :     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
     202        3074 :         myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
     203        3074 :         if (!ok) {
     204             :             return;
     205             :         }
     206        8317 :         if (!myTypeCont.knows(myCurrentType) && !myOptions.getBool("ignore-errors.edge-type")) {
     207          92 :             WRITE_ERRORF("Type '%' used by edge '%' was not defined (ignore with option --ignore-errors.edge-type).", myCurrentType, myCurrentID);
     208          27 :             return;
     209             :         }
     210        3045 :         myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
     211        3045 :         myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
     212        3045 :         myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
     213        3045 :         myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
     214        3045 :         myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
     215        3045 :         myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
     216        3045 :         mySidewalkWidth = myTypeCont.getEdgeTypeSidewalkWidth(myCurrentType);
     217        3045 :         myBikeLaneWidth = myTypeCont.getEdgeTypeBikeLaneWidth(myCurrentType);
     218             :     }
     219             :     // use values from the edge to overwrite if existing, then
     220       22940 :     if (myIsUpdate) {
     221        1879 :         if (!myHaveReportedAboutOverwriting) {
     222         249 :             WRITE_MESSAGEF(TL("Duplicate edge id occurred ('%'); assuming overwriting is wished."), myCurrentID);
     223          83 :             myHaveReportedAboutOverwriting = true;
     224             :         }
     225        1879 :         if (attrs.hasAttribute(SUMO_ATTR_TYPE) && myCurrentType != myCurrentEdge->getTypeID()) {
     226           3 :             if (!myHaveReportedAboutTypeOverride) {
     227           6 :                 WRITE_MESSAGEF(TL("Edge '%' changed its type; assuming type override is wished."), myCurrentID);
     228           2 :                 myHaveReportedAboutTypeOverride = true;
     229             :             }
     230             :         }
     231        1879 :         if (attrs.getOpt<bool>(SUMO_ATTR_REMOVE, myCurrentID.c_str(), ok, false)) {
     232           0 :             myEdgeCont.erase(myDistrictCont, myCurrentEdge);
     233           0 :             myCurrentEdge = nullptr;
     234           0 :             return;
     235             :         }
     236        1879 :         myCurrentPriority = myCurrentEdge->getPriority();
     237        1879 :         myCurrentLaneNo = myCurrentEdge->getNumLanes();
     238        1879 :         if (!myCurrentEdge->hasDefaultGeometry()) {
     239        1444 :             myShape = myCurrentEdge->getGeometry();
     240        1444 :             myReinitKeepEdgeShape = true;
     241             :         }
     242        1879 :         myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
     243        1879 :         if (myCurrentEdge->hasLoadedLength()) {
     244           1 :             myLength = myCurrentEdge->getLoadedLength();
     245             :         }
     246             :         myCurrentStreetName = myCurrentEdge->getStreetName();
     247             :     }
     248             :     // speed, priority and the number of lanes have now default values;
     249             :     // try to read the real values from the file
     250       22940 :     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
     251       13134 :         myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
     252             :     }
     253       45880 :     if (myOptions.getBool("speed-in-kmh") && myCurrentSpeed != NBEdge::UNSPECIFIED_SPEED) {
     254         689 :         myCurrentSpeed = myCurrentSpeed / (double) 3.6;
     255             :     }
     256             :     // try to read the friction value from file
     257       22940 :     if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
     258           3 :         myCurrentFriction = attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok);
     259             :     }
     260             :     // try to get the number of lanes
     261       22940 :     if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
     262       13826 :         myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
     263             :     }
     264             :     // try to get the priority
     265       22940 :     if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
     266       10097 :         myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
     267             :     }
     268             :     // try to get the width
     269       22940 :     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     270         595 :         myCurrentWidth = attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok);
     271             :     }
     272             :     // try to get the offset of the stop line from the intersection
     273       22940 :     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
     274           9 :         myCurrentEndOffset = attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok);
     275             :     }
     276             :     // try to get the street name
     277       22940 :     if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
     278        1750 :         myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
     279        2580 :         if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
     280          90 :             myOptions.set("output.street-names", "true");
     281             :         }
     282             :     }
     283             : 
     284             :     // try to get the allowed/disallowed classes
     285       22940 :     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     286        4542 :         std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
     287        4542 :         std::string disallowS = attrs.hasAttribute(SUMO_ATTR_DISALLOW) ? attrs.getStringSecure(SUMO_ATTR_DISALLOW, "") : "";
     288             :         // XXX matter of interpretation: should updated permissions replace or extend previously set permissions?
     289        4542 :         myPermissions = parseVehicleClasses(allowS, disallowS);
     290             :     }
     291             :     // try to set the nodes
     292       22940 :     if (!setNodes(attrs)) {
     293             :         // return if this failed
     294          13 :         myCurrentEdge = nullptr;
     295          13 :         return;
     296             :     }
     297             :     // try to get the shape
     298       45854 :     myShape = tryGetShape(attrs);
     299             :     // try to get the spread type
     300       22927 :     myLanesSpread = tryGetLaneSpread(attrs);
     301             :     // try to get the length
     302       22927 :     myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
     303             :     // try to get the sidewalkWidth
     304       22927 :     mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
     305             :     // try to get the bikeLaneWidth
     306       22927 :     myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
     307             :     // insert the parsed edge into the edges map
     308       22927 :     if (!ok) {
     309          10 :         myCurrentEdge = nullptr;
     310          10 :         return;
     311             :     }
     312       22917 :     if (myFromNode == myToNode) {
     313             :         // this might as well be an error. We make this a warning mostly for
     314             :         // backward compatibility
     315           9 :         WRITE_WARNINGF(TL("Ignoring self-looped edge '%' at junction '%'"), myCurrentID, myFromNode->getID());
     316           3 :         myCurrentEdge = nullptr;
     317           3 :         return;
     318             :     }
     319             :     // check whether a previously defined edge shall be overwritten
     320       22914 :     const bool applyLaneType = myCurrentEdge == nullptr;
     321       22914 :     if (myCurrentEdge != nullptr) {
     322        1878 :         myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     323             :                               myCurrentLaneNo, myCurrentPriority, myShape,
     324             :                               myCurrentWidth, myCurrentEndOffset,
     325             :                               myCurrentStreetName, myLanesSpread,
     326        1878 :                               myReinitKeepEdgeShape);
     327             :     } else {
     328             :         // the edge must be allocated in dependence to whether a shape is given
     329       21036 :         if (myShape.size() == 0) {
     330       19126 :             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     331             :                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
     332       18631 :                                        myLanesSpread, myCurrentStreetName);
     333             :         } else {
     334        5710 :             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     335             :                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
     336             :                                        myShape, myLanesSpread, myCurrentStreetName, "",
     337        2405 :                                        myKeepEdgeShape);
     338             :         }
     339             :     }
     340       22913 :     myCurrentEdge->setLoadedLength(myLength);
     341       22913 :     if (myPermissions != SVC_UNSPECIFIED) {
     342       21058 :         myCurrentEdge->setPermissions(myPermissions);
     343             :     }
     344             :     // apply laneType if given
     345       43948 :     if (applyLaneType && myCurrentType != "" && myTypeCont.knows(myCurrentType)) {
     346         452 :         const NBTypeCont::EdgeTypeDefinition* eType = myTypeCont.getEdgeType(myCurrentType);
     347         452 :         if (eType->needsLaneType()) {
     348             :             int lane = 0;
     349         340 :             for (const NBTypeCont::LaneTypeDefinition& laneType : eType->laneTypeDefinitions) {
     350         236 :                 if (lane >= myCurrentLaneNo) {
     351             :                     break;
     352             :                 }
     353             :                 if (laneType.attrs.count(SUMO_ATTR_SPEED) > 0) {
     354         139 :                     myCurrentEdge->setSpeed(lane, laneType.speed);
     355             :                 }
     356             :                 if (laneType.attrs.count(SUMO_ATTR_FRICTION) > 0) {
     357           0 :                     myCurrentEdge->setFriction(lane, laneType.friction);
     358             :                 }
     359             :                 if (laneType.attrs.count(SUMO_ATTR_DISALLOW) > 0 || laneType.attrs.count(SUMO_ATTR_ALLOW) > 0) {
     360          54 :                     myCurrentEdge->setPermissions(laneType.permissions, lane);
     361             :                 }
     362             :                 if (laneType.attrs.count(SUMO_ATTR_WIDTH) > 0) {
     363         169 :                     myCurrentEdge->setLaneWidth(lane, laneType.width);
     364             :                 }
     365         236 :                 lane++;
     366             :             }
     367             :         }
     368             :     }
     369             :     // try to get the kilometrage/mileage
     370       22913 :     myCurrentEdge->setDistance(attrs.getOpt<double>(SUMO_ATTR_DISTANCE, myCurrentID.c_str(), ok, myCurrentEdge->getDistance()));
     371             :     // preserve bidi edge (only as boo, the actual edge will be recomputed)
     372       22913 :     const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
     373       45826 :     myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || bidi != "");
     374             : 
     375       22913 :     myLastParameterised.push_back(myCurrentEdge);
     376             : }
     377             : 
     378             : 
     379             : void
     380        5544 : NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
     381        5544 :     if (myCurrentEdge == nullptr) {
     382          58 :         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
     383          87 :             WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
     384             :         }
     385          30 :         return;
     386             :     }
     387        5515 :     bool ok = true;
     388             :     int lane;
     389        5515 :     if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     390           0 :         lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
     391           0 :         if (!myHaveWarnedAboutDeprecatedLaneId) {
     392           0 :             myHaveWarnedAboutDeprecatedLaneId = true;
     393           0 :             WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
     394             :         }
     395             :     } else {
     396        5515 :         lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
     397             :     }
     398        5515 :     if (!ok) {
     399             :         return;
     400             :     }
     401             :     // check whether this lane exists
     402        5515 :     if (lane >= myCurrentEdge->getNumLanes()) {
     403           3 :         WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
     404           1 :         return;
     405             :     }
     406        5514 :     myCurrentLaneIndex = lane;
     407             :     // set information about allowed / disallowed vehicle classes (if specified)
     408        5514 :     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     409        4598 :         const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
     410        4598 :         const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
     411        4598 :         myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
     412             :     }
     413        5514 :     if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
     414           0 :         const std::string preferred  = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
     415           0 :         myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
     416             :     }
     417        5514 :     if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
     418          41 :         const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
     419          41 :         const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
     420          82 :         myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
     421             :     }
     422             :     // try to get the width
     423        5514 :     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     424        1830 :         myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
     425             :     }
     426             :     // try to get the end-offset (lane shortened due to pedestrian crossing etc..)
     427        5514 :     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
     428          18 :         myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
     429             :     }
     430             :     // try to get lane specific speed (should not occur for german networks)
     431        5514 :     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
     432           9 :         myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
     433             :     }
     434             :     // try to get lane specific friction
     435        5514 :     if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
     436           4 :         myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
     437             :     }
     438             :     // check whether this is an acceleration lane
     439        5514 :     if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
     440           2 :         myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
     441             :     }
     442             :     // check whether this lane has a custom shape
     443        5514 :     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     444          68 :         PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
     445          68 :         if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
     446           0 :             const std::string laneID = myCurrentID + "_" + toString(lane);
     447           0 :             WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
     448             :         }
     449          68 :         if (shape.size() == 1) {
     450             :             // lane shape of length 1 is not permitted
     451           1 :             shape.push_front(myCurrentEdge->getFromNode()->getPosition());
     452           1 :             shape.push_back(myCurrentEdge->getToNode()->getPosition());
     453             :         }
     454          68 :         shape.removeDoublePoints();
     455          68 :         if (shape.size() < 2) {
     456             :             // ignore lane shape for very short lanes
     457             :             shape.clear();
     458             :         }
     459          68 :         myCurrentEdge->setLaneShape(lane, shape);
     460          68 :     }
     461             :     // set custom lane type
     462        5514 :     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
     463          16 :         myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
     464             :     }
     465        5514 :     myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
     466             : }
     467             : 
     468             : 
     469         246 : void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
     470         246 :     if (myCurrentEdge == nullptr) {
     471           0 :         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
     472           0 :             WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
     473             :         }
     474           4 :         return;
     475             :     }
     476         246 :     bool ok = true;
     477             :     NBEdgeCont::Split e;
     478         246 :     e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
     479         246 :     if (ok) {
     480         488 :         if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
     481           3 :             WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
     482           1 :             return;
     483             :         }
     484         245 :         std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
     485         245 :         if (i != mySplits.end()) {
     486           6 :             WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
     487           2 :             return;
     488             :         }
     489             :         // XXX rounding to int may duplicate the id of another split
     490         486 :         e.nameID = myCurrentID + "." + toString((int)e.pos);
     491         243 :         if (e.pos < 0) {
     492          57 :             e.pos += myCurrentEdge->getGeometry().length();
     493             :         }
     494         613 :         for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
     495             :             try {
     496         370 :                 int lane = StringUtils::toInt(id);
     497         368 :                 e.lanes.push_back(lane);
     498           2 :             } catch (NumberFormatException&) {
     499           6 :                 WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
     500           2 :             } catch (EmptyData&) {
     501           0 :                 WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
     502           0 :             }
     503         243 :         }
     504         243 :         if (e.lanes.empty()) {
     505         159 :             for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
     506         101 :                 e.lanes.push_back(l);
     507             :             }
     508             :         }
     509         243 :         e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
     510         251 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
     511           0 :             e.speed /= 3.6;
     512             :         }
     513         486 :         e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
     514         486 :         e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
     515         243 :         if (!ok) {
     516             :             return;
     517             :         }
     518         243 :         const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
     519         243 :         if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
     520           3 :             WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
     521             :             return;
     522             :         }
     523         242 :         e.node = myNodeCont.retrieve(nodeID);
     524         483 :         e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
     525         242 :         if (e.node == nullptr) {
     526         235 :             double geomPos = e.pos;
     527         235 :             if (myCurrentEdge->hasLoadedLength()) {
     528           4 :                 geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
     529             :             }
     530         235 :             e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
     531         235 :             myNodeCont.insert(e.node);
     532             :         }
     533         242 :         NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
     534             :                                            myNodeCont, myEdgeCont, myTLLogicCont);
     535         242 :         mySplits.push_back(e);
     536             :     }
     537         246 : }
     538             : 
     539             : 
     540             : bool
     541       22940 : NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
     542             :     // the names and the coordinates of the beginning and the end node
     543             :     // may be found, try
     544       22940 :     bool ok = true;
     545       22940 :     if (myIsUpdate) {
     546        1879 :         myFromNode = myCurrentEdge->getFromNode();
     547        1879 :         myToNode = myCurrentEdge->getToNode();
     548             :     }
     549       22940 :     if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
     550       21089 :         const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
     551       21089 :         if (begNodeID != "") {
     552       21087 :             myFromNode = myNodeCont.retrieve(begNodeID);
     553       21087 :             if (myFromNode == nullptr) {
     554          12 :                 WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
     555             :             }
     556             :         }
     557        1851 :     } else if (!myIsUpdate) {
     558           9 :         WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
     559           3 :         ok = false;
     560             :     }
     561       22940 :     if (attrs.hasAttribute(SUMO_ATTR_TO)) {
     562       21088 :         const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     563       21088 :         if (endNodeID != "") {
     564       21086 :             myToNode = myNodeCont.retrieve(endNodeID);
     565       21086 :             if (myToNode == nullptr) {
     566          12 :                 WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
     567             :             }
     568             :         }
     569        1852 :     } else if (!myIsUpdate) {
     570           9 :         WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
     571           3 :         ok = false;
     572             :     }
     573       22953 :     return ok && myFromNode != nullptr && myToNode != nullptr;
     574             : }
     575             : 
     576             : 
     577             : PositionVector
     578       22927 : NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
     579       22927 :     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
     580             :         return myShape;
     581             :     }
     582             :     // try to build shape
     583       22915 :     bool ok = true;
     584       22915 :     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     585       18705 :         const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
     586       18705 :         if (maxSegmentLength > 0) {
     587           1 :             PositionVector shape;
     588           1 :             shape.push_back(myFromNode->getPosition());
     589           1 :             shape.push_back(myToNode->getPosition());
     590             :             // shape is already cartesian but we must use a copy because the original will be modified
     591           1 :             NBNetBuilder::addGeometrySegments(shape, PositionVector(shape), maxSegmentLength);
     592             :             return shape;
     593           1 :         } else {
     594       18704 :             myReinitKeepEdgeShape = false;
     595       18704 :             return PositionVector();
     596             :         }
     597             :     }
     598        4210 :     PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
     599        4210 :     if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
     600           0 :         WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
     601             :     }
     602        4210 :     myReinitKeepEdgeShape = myKeepEdgeShape;
     603             :     return shape;
     604        4210 : }
     605             : 
     606             : 
     607             : LaneSpreadFunction
     608       22927 : NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
     609       22927 :     bool ok = true;
     610       22927 :     LaneSpreadFunction result = myLanesSpread;
     611       22927 :     std::string lsfS = toString(result);
     612       45854 :     lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
     613       22927 :     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
     614       22927 :         result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
     615             :     } else {
     616           0 :         WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
     617             :     }
     618       22927 :     return result;
     619             : }
     620             : 
     621             : 
     622             : void
     623          31 : NIXMLEdgesHandler::deleteEdge(const SUMOSAXAttributes& attrs) {
     624          31 :     bool ok = true;
     625          31 :     myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     626          31 :     if (!ok) {
     627           0 :         return;
     628             :     }
     629          31 :     NBEdge* edge = myEdgeCont.retrieve(myCurrentID);
     630          31 :     if (edge == nullptr) {
     631           0 :         WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown edge '" +
     632             :                       myCurrentID + "'");
     633           0 :         return;
     634             :     }
     635          31 :     const int lane = attrs.getOpt<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok, -1);
     636          31 :     if (lane < 0) {
     637          28 :         myEdgeCont.extract(myDistrictCont, edge, true);
     638             :     } else {
     639           3 :         edge->deleteLane(lane, false, true);
     640             :     }
     641             : }
     642             : 
     643             : 
     644             : void
     645       31660 : NIXMLEdgesHandler::myEndElement(int element) {
     646       31660 :     if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
     647        1091 :         delete myLocation;
     648        1091 :         myLocation = nullptr;
     649        1091 :         return;
     650             :     }
     651       30569 :     if (myCurrentEdge == nullptr) {
     652             :         return;
     653             :     }
     654       30385 :     if (element == SUMO_TAG_EDGE) {
     655             :         myLastParameterised.pop_back();
     656             :         // add bike lane, wait until lanes are loaded to avoid building if it already exists
     657       22913 :         if (myBikeLaneWidth != NBEdge::UNSPECIFIED_WIDTH) {
     658           7 :             myCurrentEdge->addBikeLane(myBikeLaneWidth);
     659             :         }
     660             :         // add sidewalk, wait until lanes are loaded to avoid building if it already exists
     661       22913 :         if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
     662          44 :             myCurrentEdge->addSidewalk(mySidewalkWidth);
     663             :         }
     664             :         // apply default stopOffsets of edge to all lanes without specified stopOffset.
     665       22913 :         const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
     666       22913 :         if (stopOffsets.isDefined()) {
     667          32 :             for (int i = 0; i < (int)myCurrentEdge->getLanes().size(); i++) {
     668          24 :                 myCurrentEdge->setEdgeStopOffset(i, stopOffsets, false);
     669             :             }
     670             :         }
     671       22913 :         if (!myIsUpdate) {
     672             :             try {
     673       21035 :                 if (!myEdgeCont.insert(myCurrentEdge)) {
     674           0 :                     WRITE_ERRORF(TL("Duplicate edge '%' occurred."), myCurrentID);
     675           0 :                     delete myCurrentEdge;
     676           0 :                     myCurrentEdge = nullptr;
     677           0 :                     return;
     678             :                 }
     679           0 :             } catch (InvalidArgument& e) {
     680           0 :                 WRITE_ERROR(e.what());
     681           0 :                 throw;
     682           0 :             } catch (...) {
     683           0 :                 WRITE_ERRORF(TL("An important information is missing in edge '%'."), myCurrentID);
     684           0 :             }
     685             :         }
     686       22913 :         myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
     687       22913 :         myCurrentEdge = nullptr;
     688        7472 :     } else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
     689             :         myLastParameterised.pop_back();
     690        5514 :         myCurrentLaneIndex = -1;
     691             :     }
     692             : }
     693             : 
     694             : 
     695             : void
     696           9 : NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
     697           9 :     bool ok = true;
     698           9 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
     699           9 :     if (ok) {
     700             :         EdgeSet roundabout;
     701          67 :         for (const std::string& eID : edgeIDs) {
     702          58 :             NBEdge* edge = myEdgeCont.retrieve(eID);
     703          58 :             if (edge == nullptr) {
     704           0 :                 if (!myEdgeCont.wasIgnored(eID)) {
     705           0 :                     WRITE_ERRORF(TL("Unknown edge '%' in roundabout."), eID);
     706             :                 }
     707             :             } else {
     708             :                 roundabout.insert(edge);
     709             :             }
     710             :         }
     711           9 :         myEdgeCont.addRoundabout(roundabout);
     712             :     }
     713           9 : }
     714             : 
     715             : 
     716             : /****************************************************************************/

Generated by: LCOV version 1.14