LCOV - code coverage report
Current view: top level - src/netimport - NIXMLEdgesHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.8 % 413 379
Test Date: 2026-03-26 16:31:35 Functions: 92.3 % 13 12

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    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         2371 : NIXMLEdgesHandler::NIXMLEdgesHandler(NBNodeCont& nc,
      52              :                                      NBEdgeCont& ec,
      53              :                                      NBTypeCont& tc,
      54              :                                      NBDistrictCont& dc,
      55              :                                      NBTrafficLightLogicCont& tlc,
      56         2371 :                                      OptionsCont& options) :
      57              :     SUMOSAXHandler("xml-edges - file"),
      58         2371 :     myOptions(options),
      59         2371 :     myNodeCont(nc),
      60         2371 :     myEdgeCont(ec),
      61         2371 :     myTypeCont(tc),
      62         2371 :     myDistrictCont(dc),
      63         2371 :     myTLLogicCont(tlc),
      64         2371 :     myCurrentEdge(nullptr),
      65         2371 :     myCurrentLaneIndex(-1),
      66         2371 :     myHaveReportedAboutOverwriting(false),
      67         2371 :     myHaveReportedAboutTypeOverride(false),
      68         2371 :     myHaveWarnedAboutDeprecatedLaneId(false),
      69         2371 :     myDefaultNodeType(SUMOXMLDefinitions::NodeTypes.hasString(options.getString("default.junctions.type"))
      70         2373 :             ? SUMOXMLDefinitions::NodeTypes.get(options.getString("default.junctions.type")) : SumoXMLNodeType::UNKNOWN),
      71         7113 :     myKeepEdgeShape(!options.getBool("plain.extend-edge-shape")) {
      72         2371 : }
      73              : 
      74              : 
      75         2371 : NIXMLEdgesHandler::~NIXMLEdgesHandler() {
      76         2371 :     delete myLocation;
      77         2371 : }
      78              : 
      79              : 
      80              : void
      81        28530 : NIXMLEdgesHandler::myStartElement(int element,
      82              :                                   const SUMOSAXAttributes& attrs) {
      83        28530 :     switch (element) {
      84              :         case SUMO_TAG_VIEWSETTINGS_EDGES:
      85              :             // infer location for legacy networks that don't have location information
      86         1154 :             myLocation = GeoConvHelper::getLoadedPlain(getFileName());
      87         1154 :             break;
      88           27 :         case SUMO_TAG_LOCATION:
      89           27 :             delete myLocation;
      90           27 :             myLocation = NIImporter_SUMO::loadLocation(attrs, false);
      91           27 :             break;
      92        19024 :         case SUMO_TAG_EDGE:
      93        19024 :             addEdge(attrs);
      94        19023 :             break;
      95         5888 :         case SUMO_TAG_LANE:
      96         5888 :             addLane(attrs);
      97         5888 :             break;
      98           16 :         case SUMO_TAG_NEIGH:
      99           16 :             myCurrentEdge->getLaneStruct((int)myCurrentEdge->getNumLanes() - 1).oppositeID = attrs.getString(SUMO_ATTR_LANE);
     100           16 :             break;
     101          222 :         case SUMO_TAG_SPLIT:
     102          222 :             addSplit(attrs);
     103          222 :             break;
     104           31 :         case SUMO_TAG_DEL:
     105           31 :             deleteEdge(attrs);
     106           31 :             break;
     107           11 :         case SUMO_TAG_ROUNDABOUT:
     108           11 :             addRoundabout(attrs);
     109           11 :             break;
     110         2132 :         case SUMO_TAG_PARAM:
     111         2132 :             if (myLastParameterised.size() != 0 && myCurrentEdge != nullptr) {
     112         2090 :                 bool ok = true;
     113         2090 :                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
     114              :                 // circumventing empty string test
     115         2090 :                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     116         2090 :                 myLastParameterised.back()->setParameter(key, val);
     117              :             }
     118              :             break;
     119           25 :         case SUMO_TAG_STOPOFFSET: {
     120           25 :             bool ok = true;
     121           25 :             const StopOffset stopOffset(attrs, ok);
     122           25 :             if (!ok) {
     123            1 :                 std::stringstream ss;
     124            2 :                 ss << "(Error encountered at lane " << myCurrentLaneIndex << " of edge '" << myCurrentID << "' while parsing stopOffsets.)";
     125            1 :                 WRITE_ERROR(ss.str());
     126            1 :             } else {
     127           24 :                 if (myCurrentEdge->getLaneStopOffset(myCurrentLaneIndex).isDefined()) {
     128            3 :                     std::stringstream ss;
     129            3 :                     ss << "Duplicate definition of stopOffset for ";
     130            3 :                     if (myCurrentLaneIndex != -1) {
     131            1 :                         ss << "lane " << myCurrentLaneIndex << " on ";
     132              :                     }
     133            6 :                     ss << "edge " << myCurrentEdge->getID() << ". Ignoring duplicate specification.";
     134            3 :                     WRITE_WARNING(ss.str());
     135           24 :                 } else if ((stopOffset.getOffset() > myCurrentEdge->getLength()) || (stopOffset.getOffset() < 0)) {
     136            4 :                     std::stringstream ss;
     137            4 :                     ss << "Ignoring invalid stopOffset for ";
     138            4 :                     if (myCurrentLaneIndex != -1) {
     139            2 :                         ss << "lane " << myCurrentLaneIndex << " on ";
     140              :                     }
     141            4 :                     ss << "edge " << myCurrentEdge->getID();
     142            4 :                     if (stopOffset.getOffset() > myCurrentEdge->getLength()) {
     143            2 :                         ss << " (offset larger than the edge length).";
     144              :                     } else {
     145            2 :                         ss << " (negative offset).";
     146              :                     }
     147            4 :                     WRITE_WARNING(ss.str());
     148            4 :                 } else {
     149           17 :                     myCurrentEdge->setEdgeStopOffset(myCurrentLaneIndex, stopOffset);
     150              :                 }
     151              :             }
     152              :         }
     153           25 :         break;
     154              :         default:
     155              :             break;
     156              :     }
     157        28529 : }
     158              : 
     159              : 
     160              : void
     161        19024 : NIXMLEdgesHandler::addEdge(const SUMOSAXAttributes& attrs) {
     162        19024 :     myIsUpdate = false;
     163        19024 :     bool ok = true;
     164              :     // initialise the edge
     165        19024 :     myCurrentEdge = nullptr;
     166              :     mySplits.clear();
     167              :     // get the id, report an error if not given or empty...
     168        19024 :     myCurrentID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     169        19024 :     if (!ok) {
     170           59 :         return;
     171              :     }
     172        19020 :     myCurrentEdge = myEdgeCont.retrieve(myCurrentID);
     173              :     // check deprecated (unused) attributes
     174              :     // use default values, first
     175        40168 :     myCurrentType = myCurrentEdge == nullptr ? myOptions.getString("default.type") : myCurrentEdge->getTypeID();
     176              :     // check whether a type's values shall be used
     177        19020 :     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
     178         3195 :         myCurrentType = attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok);
     179         3195 :         if (!ok) {
     180              :             return;
     181              :         }
     182         8720 :         if (!myTypeCont.knows(myCurrentType) && !myOptions.getBool("ignore-errors.edge-type")) {
     183           81 :             WRITE_ERRORF("Type '%' used by edge '%' was not defined (ignore with option --ignore-errors.edge-type).", myCurrentType, myCurrentID);
     184           27 :             return;
     185              :         }
     186              :     }
     187        18991 :     myCurrentEndOffset = NBEdge::UNSPECIFIED_OFFSET;
     188        18991 :     if (myCurrentEdge != nullptr) {
     189              :         // update existing edge. only update lane-specific settings when explicitly requested
     190         2128 :         myIsUpdate = true;
     191         2128 :         myCurrentSpeed = NBEdge::UNSPECIFIED_SPEED;
     192         2128 :         myCurrentFriction = NBEdge::UNSPECIFIED_FRICTION;
     193         2128 :         myCurrentPriority = myCurrentEdge->getPriority();
     194         2128 :         myCurrentLaneNo = myCurrentEdge->getNumLanes();
     195         2128 :         myPermissions = SVC_UNSPECIFIED;
     196         2128 :         myCurrentWidth = NBEdge::UNSPECIFIED_WIDTH;
     197         2128 :         myLanesSpread = myCurrentEdge->getLaneSpreadFunction();
     198         2128 :         mySidewalkWidth = NBEdge::UNSPECIFIED_WIDTH;
     199         2128 :         myBikeLaneWidth = NBEdge::UNSPECIFIED_WIDTH;
     200              :     } else {
     201              :         // this is a completely new edge. get the type specific defaults
     202        16863 :         myCurrentSpeed = myTypeCont.getEdgeTypeSpeed(myCurrentType);
     203        16863 :         myCurrentFriction = myTypeCont.getEdgeTypeFriction(myCurrentType);
     204        16863 :         myCurrentPriority = myTypeCont.getEdgeTypePriority(myCurrentType);
     205        16863 :         myCurrentLaneNo = myTypeCont.getEdgeTypeNumLanes(myCurrentType);
     206        16863 :         myPermissions = myTypeCont.getEdgeTypePermissions(myCurrentType);
     207        16863 :         myCurrentWidth = myTypeCont.getEdgeTypeWidth(myCurrentType);
     208        16863 :         myLanesSpread = myTypeCont.getEdgeTypeSpreadType(myCurrentType);
     209        16863 :         if (myLanesSpread == LaneSpreadFunction::SPREAD_UNKNOWN) {
     210          824 :             myLanesSpread = SUMOXMLDefinitions::LaneSpreadFunctions.get(myOptions.getString("default.spreadtype"));
     211              :         }
     212        16863 :         mySidewalkWidth = myTypeCont.getEdgeTypeSidewalkWidth(myCurrentType);
     213        16863 :         myBikeLaneWidth = myTypeCont.getEdgeTypeBikeLaneWidth(myCurrentType);
     214              :     }
     215        37982 :     myShape = PositionVector();
     216        18991 :     myLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
     217        18991 :     myCurrentStreetName = "";
     218        18991 :     myReinitKeepEdgeShape = false;
     219              : 
     220              :     // use values from the edge to overwrite if existing, then
     221        18991 :     if (myIsUpdate) {
     222         2128 :         if (!myHaveReportedAboutOverwriting) {
     223          279 :             WRITE_MESSAGEF(TL("Duplicate edge id occurred ('%'); assuming overwriting is wished."), myCurrentID);
     224           93 :             myHaveReportedAboutOverwriting = true;
     225              :         }
     226         2128 :         if (attrs.hasAttribute(SUMO_ATTR_TYPE) && myCurrentType != myCurrentEdge->getTypeID()) {
     227            3 :             if (!myHaveReportedAboutTypeOverride) {
     228            6 :                 WRITE_MESSAGEF(TL("Edge '%' changed its type; assuming type override is wished."), myCurrentID);
     229            2 :                 myHaveReportedAboutTypeOverride = true;
     230              :             }
     231              :         }
     232         2128 :         if (attrs.getOpt<bool>(SUMO_ATTR_REMOVE, myCurrentID.c_str(), ok, false)) {
     233            0 :             myEdgeCont.erase(myDistrictCont, myCurrentEdge);
     234            0 :             myCurrentEdge = nullptr;
     235            0 :             return;
     236              :         }
     237         2128 :         if (!myCurrentEdge->hasDefaultGeometry()) {
     238         1654 :             myShape = myCurrentEdge->getGeometry();
     239         1654 :             myReinitKeepEdgeShape = true;
     240              :         }
     241         2128 :         if (myCurrentEdge->hasLoadedLength()) {
     242            1 :             myLength = myCurrentEdge->getLoadedLength();
     243              :         }
     244              :         myCurrentStreetName = myCurrentEdge->getStreetName();
     245              :     }
     246              :     // speed, priority and the number of lanes have now default values;
     247              :     // try to read the real values from the file
     248        18991 :     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
     249        12528 :         myCurrentSpeed = attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok);
     250              :     }
     251        37982 :     if (myOptions.getBool("speed-in-kmh") && myCurrentSpeed != NBEdge::UNSPECIFIED_SPEED) {
     252          689 :         myCurrentSpeed = myCurrentSpeed / (double) 3.6;
     253              :     }
     254              :     // try to read the friction value from file
     255        18991 :     if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
     256            3 :         myCurrentFriction = attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok);
     257              :     }
     258              :     // try to get the number of lanes
     259        18991 :     if (attrs.hasAttribute(SUMO_ATTR_NUMLANES)) {
     260        12977 :         myCurrentLaneNo = attrs.get<int>(SUMO_ATTR_NUMLANES, myCurrentID.c_str(), ok);
     261              :     }
     262              :     // try to get the priority
     263        18991 :     if (attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
     264        10658 :         myCurrentPriority = attrs.get<int>(SUMO_ATTR_PRIORITY, myCurrentID.c_str(), ok);
     265              :     }
     266              :     // try to get the width
     267        18991 :     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     268          655 :         myCurrentWidth = attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok);
     269              :     }
     270              :     // try to get the offset of the stop line from the intersection
     271        18991 :     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
     272           11 :         myCurrentEndOffset = attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok);
     273              :     }
     274              :     // try to get the street name
     275        18991 :     if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
     276         1938 :         myCurrentStreetName = attrs.get<std::string>(SUMO_ATTR_NAME, myCurrentID.c_str(), ok);
     277         1938 :         if (myCurrentStreetName != "" && myOptions.isDefault("output.street-names")) {
     278          106 :             myOptions.set("output.street-names", "true");
     279              :         }
     280              :     }
     281              : 
     282              :     // try to get the allowed/disallowed classes
     283        18991 :     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     284         4770 :         std::string allowS = attrs.hasAttribute(SUMO_ATTR_ALLOW) ? attrs.getStringSecure(SUMO_ATTR_ALLOW, "") : "";
     285         4770 :         std::string disallowS = attrs.hasAttribute(SUMO_ATTR_DISALLOW) ? attrs.getStringSecure(SUMO_ATTR_DISALLOW, "") : "";
     286              :         // XXX matter of interpretation: should updated permissions replace or extend previously set permissions?
     287         4770 :         myPermissions = parseVehicleClasses(allowS, disallowS);
     288              :     }
     289              :     // try to set the nodes
     290        18991 :     if (!setNodes(attrs)) {
     291              :         // return if this failed
     292           13 :         myCurrentEdge = nullptr;
     293           13 :         return;
     294              :     }
     295              :     // try to get the shape
     296        37956 :     myShape = tryGetShape(attrs);
     297              :     // try to get the spread type
     298        18978 :     myLanesSpread = tryGetLaneSpread(attrs);
     299              :     // try to get the length
     300        18978 :     myLength = attrs.getOpt<double>(SUMO_ATTR_LENGTH, myCurrentID.c_str(), ok, myLength);
     301              :     // try to get the sidewalkWidth
     302        18978 :     mySidewalkWidth = attrs.getOpt<double>(SUMO_ATTR_SIDEWALKWIDTH, myCurrentID.c_str(), ok, mySidewalkWidth);
     303              :     // try to get the bikeLaneWidth
     304        18978 :     myBikeLaneWidth = attrs.getOpt<double>(SUMO_ATTR_BIKELANEWIDTH, myCurrentID.c_str(), ok, myBikeLaneWidth);
     305              :     // insert the parsed edge into the edges map
     306        18978 :     if (!ok) {
     307           10 :         myCurrentEdge = nullptr;
     308           10 :         return;
     309              :     }
     310        18968 :     if (myFromNode == myToNode) {
     311              :         // this might as well be an error. We make this a warning mostly for
     312              :         // backward compatibility
     313            9 :         WRITE_WARNINGF(TL("Ignoring self-looped edge '%' at junction '%'"), myCurrentID, myFromNode->getID());
     314            3 :         myCurrentEdge = nullptr;
     315            3 :         return;
     316              :     }
     317              :     // check whether a previously defined edge shall be overwritten
     318        18965 :     const bool applyLaneType = myCurrentEdge == nullptr;
     319        18965 :     if (myCurrentEdge != nullptr) {
     320         2127 :         myCurrentEdge->reinit(myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     321              :                               myCurrentLaneNo, myCurrentPriority, myShape,
     322              :                               myCurrentWidth, myCurrentEndOffset,
     323              :                               myCurrentStreetName, myLanesSpread,
     324         2127 :                               myReinitKeepEdgeShape);
     325              :     } else {
     326              :         // the edge must be allocated in dependence to whether a shape is given
     327        16838 :         if (myShape.size() == 0) {
     328        14254 :             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     329              :                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
     330        42762 :                                        myLanesSpread, myCurrentStreetName);
     331              :         } else {
     332         2583 :             myCurrentEdge = new NBEdge(myCurrentID, myFromNode, myToNode, myCurrentType, myCurrentSpeed, myCurrentFriction,
     333              :                                        myCurrentLaneNo, myCurrentPriority, myCurrentWidth, myCurrentEndOffset,
     334              :                                        myShape, myLanesSpread, myCurrentStreetName, "",
     335         7754 :                                        myKeepEdgeShape);
     336              :         }
     337              :     }
     338        18964 :     myCurrentEdge->setLoadedLength(myLength);
     339        18964 :     if (myPermissions != SVC_UNSPECIFIED) {
     340        16857 :         myCurrentEdge->setPermissions(myPermissions);
     341              :     }
     342              :     // apply laneType if given
     343        18964 :     if (applyLaneType && myCurrentType != "" && myTypeCont.knows(myCurrentType)) {
     344          432 :         const NBTypeCont::EdgeTypeDefinition* eType = myTypeCont.getEdgeType(myCurrentType);
     345          432 :         if (eType->needsLaneType()) {
     346              :             int lane = 0;
     347          340 :             for (const NBTypeCont::LaneTypeDefinition& laneType : eType->laneTypeDefinitions) {
     348          236 :                 if (lane >= myCurrentLaneNo) {
     349              :                     break;
     350              :                 }
     351              :                 if (laneType.attrs.count(SUMO_ATTR_SPEED) > 0) {
     352          139 :                     myCurrentEdge->setSpeed(lane, laneType.speed);
     353              :                 }
     354              :                 if (laneType.attrs.count(SUMO_ATTR_FRICTION) > 0) {
     355            0 :                     myCurrentEdge->setFriction(lane, laneType.friction);
     356              :                 }
     357              :                 if (laneType.attrs.count(SUMO_ATTR_DISALLOW) > 0 || laneType.attrs.count(SUMO_ATTR_ALLOW) > 0) {
     358           54 :                     myCurrentEdge->setPermissions(laneType.permissions, lane);
     359              :                 }
     360              :                 if (laneType.attrs.count(SUMO_ATTR_WIDTH) > 0) {
     361          169 :                     myCurrentEdge->setLaneWidth(lane, laneType.width);
     362              :                 }
     363          236 :                 lane++;
     364              :             }
     365              :         }
     366              :     }
     367              :     // try to get the kilometrage/mileage
     368        18964 :     myCurrentEdge->setDistance(attrs.getOpt<double>(SUMO_ATTR_DISTANCE, myCurrentID.c_str(), ok, myCurrentEdge->getDistance()));
     369        18964 :     myCurrentEdge->setRoutingType(attrs.getOpt<std::string>(SUMO_ATTR_ROUTINGTYPE, myCurrentID.c_str(), ok, myCurrentEdge->getRoutingType()));
     370              :     // preserve bidi edge (only as bool, the actual edge will be recomputed)
     371        18964 :     const std::string bidi = attrs.getOpt<std::string>(SUMO_ATTR_BIDI, myCurrentID.c_str(), ok, "");
     372        18964 :     myCurrentEdge->setBidi(myCurrentEdge->getBidiEdge() != nullptr || myCurrentEdge->isBidi() || bidi != "");
     373              : 
     374        18964 :     myLastParameterised.push_back(myCurrentEdge);
     375              : }
     376              : 
     377              : 
     378              : void
     379         5888 : NIXMLEdgesHandler::addLane(const SUMOSAXAttributes& attrs) {
     380         5888 :     if (myCurrentEdge == nullptr) {
     381           58 :         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
     382           87 :             WRITE_ERRORF("Additional lane information could not be set - the edge with id '%s' is not known.", myCurrentID);
     383              :         }
     384           30 :         return;
     385              :     }
     386         5859 :     bool ok = true;
     387              :     int lane;
     388         5859 :     if (attrs.hasAttribute(SUMO_ATTR_ID)) {
     389            0 :         lane = attrs.get<int>(SUMO_ATTR_ID, myCurrentID.c_str(), ok);
     390            0 :         if (!myHaveWarnedAboutDeprecatedLaneId) {
     391            0 :             myHaveWarnedAboutDeprecatedLaneId = true;
     392            0 :             WRITE_WARNINGF(TL("'%' is deprecated, please use '%' instead."), toString(SUMO_ATTR_ID), toString(SUMO_ATTR_INDEX));
     393              :         }
     394              :     } else {
     395         5859 :         lane = attrs.get<int>(SUMO_ATTR_INDEX, myCurrentID.c_str(), ok);
     396              :     }
     397         5859 :     if (!ok) {
     398              :         return;
     399              :     }
     400              :     // check whether this lane exists
     401         5859 :     if (lane >= myCurrentEdge->getNumLanes()) {
     402            3 :         WRITE_ERRORF(TL("Lane index is larger than number of lanes (edge '%')."), myCurrentID);
     403            1 :         return;
     404              :     }
     405         5858 :     myCurrentLaneIndex = lane;
     406              :     // set information about allowed / disallowed vehicle classes (if specified)
     407         5858 :     if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     408         4763 :         const std::string allowed = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
     409         4763 :         const std::string disallowed = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
     410         4763 :         myCurrentEdge->setPermissions(parseVehicleClasses(allowed, disallowed), lane);
     411              :     }
     412         5858 :     if (attrs.hasAttribute(SUMO_ATTR_PREFER)) {
     413            0 :         const std::string preferred  = attrs.get<std::string>(SUMO_ATTR_PREFER, nullptr, ok);
     414            0 :         myCurrentEdge->setPreferredVehicleClass(parseVehicleClasses(preferred), lane);
     415              :     }
     416         5858 :     if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT) || attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
     417           47 :         const std::string changeLeft = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok, "");
     418           47 :         const std::string changeRight = attrs.getOpt<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok, "");
     419           94 :         myCurrentEdge->setPermittedChanging(lane, parseVehicleClasses(changeLeft, ""), parseVehicleClasses(changeRight, ""));
     420              :     }
     421              :     // try to get the width
     422         5858 :     if (attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     423         1947 :         myCurrentEdge->setLaneWidth(lane, attrs.get<double>(SUMO_ATTR_WIDTH, myCurrentID.c_str(), ok));
     424              :     }
     425              :     // try to get the end-offset (lane shortened due to pedestrian crossing etc..)
     426         5858 :     if (attrs.hasAttribute(SUMO_ATTR_ENDOFFSET)) {
     427           18 :         myCurrentEdge->setEndOffset(lane, attrs.get<double>(SUMO_ATTR_ENDOFFSET, myCurrentID.c_str(), ok));
     428              :     }
     429              :     // try to get lane specific speed (should not occur for german networks)
     430         5858 :     if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
     431            9 :         myCurrentEdge->setSpeed(lane, attrs.get<double>(SUMO_ATTR_SPEED, myCurrentID.c_str(), ok));
     432              :     }
     433              :     // try to get lane specific friction
     434         5858 :     if (attrs.hasAttribute(SUMO_ATTR_FRICTION)) {
     435            4 :         myCurrentEdge->setFriction(lane, attrs.get<double>(SUMO_ATTR_FRICTION, myCurrentID.c_str(), ok));
     436              :     }
     437              :     // check whether this is an acceleration lane
     438         5858 :     if (attrs.hasAttribute(SUMO_ATTR_ACCELERATION)) {
     439            4 :         myCurrentEdge->setAcceleration(lane, attrs.get<bool>(SUMO_ATTR_ACCELERATION, myCurrentID.c_str(), ok));
     440              :     }
     441              :     // check whether this lane has a custom shape
     442         5858 :     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     443           72 :         PositionVector shape = attrs.get<PositionVector>(SUMO_ATTR_SHAPE, myCurrentID.c_str(), ok);
     444           72 :         if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
     445            0 :             const std::string laneID = myCurrentID + "_" + toString(lane);
     446            0 :             WRITE_ERRORF(TL("Unable to project coordinates for lane '%'."), laneID);
     447              :         }
     448           72 :         if (shape.size() == 1) {
     449              :             // lane shape of length 1 is not permitted
     450            1 :             shape.push_front(myCurrentEdge->getFromNode()->getPosition());
     451            1 :             shape.push_back(myCurrentEdge->getToNode()->getPosition());
     452              :         }
     453           72 :         shape.removeDoublePoints();
     454           72 :         if (shape.size() < 2) {
     455              :             // ignore lane shape for very short lanes
     456              :             shape.clear();
     457              :         }
     458           72 :         myCurrentEdge->setLaneShape(lane, shape);
     459           72 :     }
     460              :     // set custom lane type
     461         5858 :     if (attrs.hasAttribute(SUMO_ATTR_TYPE)) {
     462           16 :         myCurrentEdge->setLaneType(lane, attrs.get<std::string>(SUMO_ATTR_TYPE, myCurrentID.c_str(), ok));
     463              :     }
     464         5858 :     myLastParameterised.push_back(&myCurrentEdge->getLaneStruct(lane));
     465              : }
     466              : 
     467              : 
     468          222 : void NIXMLEdgesHandler::addSplit(const SUMOSAXAttributes& attrs) {
     469          222 :     if (myCurrentEdge == nullptr) {
     470            0 :         if (!OptionsCont::getOptions().isInStringVector("remove-edges.explicit", myCurrentID)) {
     471            0 :             WRITE_WARNING(TL("Ignoring 'split' because it cannot be assigned to an edge"));
     472              :         }
     473            4 :         return;
     474              :     }
     475          222 :     bool ok = true;
     476              :     NBEdgeCont::Split e;
     477          222 :     e.pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
     478          222 :     if (ok) {
     479          437 :         if (fabs(e.pos) > myCurrentEdge->getLoadedLength()) {
     480            3 :             WRITE_ERRORF(TL("Edge '%' has a split at invalid position %."), myCurrentID, toString(e.pos));
     481            1 :             return;
     482              :         }
     483          221 :         std::vector<NBEdgeCont::Split>::iterator i = find_if(mySplits.begin(), mySplits.end(), split_by_pos_finder(e.pos));
     484          221 :         if (i != mySplits.end()) {
     485            6 :             WRITE_ERRORF(TL("Edge '%' has already a split at position %."), myCurrentID, toString(e.pos));
     486            2 :             return;
     487              :         }
     488              :         // XXX rounding to int may duplicate the id of another split
     489          438 :         e.nameID = myCurrentID + "." + toString((int)e.pos);
     490          219 :         if (e.pos < 0) {
     491           75 :             e.pos += myCurrentEdge->getLoadedLength();
     492              :         }
     493          535 :         for (const std::string& id : attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LANES, myCurrentID.c_str(), ok)) {
     494              :             try {
     495          316 :                 int lane = StringUtils::toInt(id);
     496          314 :                 e.lanes.push_back(lane);
     497            2 :             } catch (NumberFormatException&) {
     498            6 :                 WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
     499            2 :             } catch (EmptyData&) {
     500            0 :                 WRITE_ERRORF(TL("Error on parsing a split (edge '%')."), myCurrentID);
     501            0 :             }
     502          219 :         }
     503          219 :         if (e.lanes.empty()) {
     504          195 :             for (int l = 0; l < myCurrentEdge->getNumLanes(); ++l) {
     505          122 :                 e.lanes.push_back(l);
     506              :             }
     507              :         }
     508          219 :         e.speed = attrs.getOpt(SUMO_ATTR_SPEED, nullptr, ok, myCurrentEdge->getSpeed());
     509          223 :         if (attrs.hasAttribute(SUMO_ATTR_SPEED) && myOptions.getBool("speed-in-kmh")) {
     510            0 :             e.speed /= 3.6;
     511              :         }
     512          219 :         e.idBefore = attrs.getOpt(SUMO_ATTR_ID_BEFORE, nullptr, ok, std::string(""));
     513          219 :         e.idAfter = attrs.getOpt(SUMO_ATTR_ID_AFTER, nullptr, ok, std::string(""));
     514          219 :         if (!ok) {
     515              :             return;
     516              :         }
     517          219 :         const std::string nodeID = attrs.getOpt(SUMO_ATTR_ID, nullptr, ok, e.nameID);
     518          219 :         if (nodeID == myCurrentEdge->getFromNode()->getID() || nodeID == myCurrentEdge->getToNode()->getID()) {
     519            3 :             WRITE_ERRORF(TL("Invalid split node id for edge '%' (from- and to-node are forbidden)"), myCurrentEdge->getID());
     520              :             return;
     521              :         }
     522          218 :         e.node = e.pos != 0 ? myNodeCont.retrieve(nodeID) : myCurrentEdge->getFromNode();
     523          218 :         e.offset = attrs.getOpt(SUMO_ATTR_OFFSET, nullptr, ok, 0.0);
     524          435 :         e.offsetFactor = OptionsCont::getOptions().getBool("lefthand") ? -1 : 1;
     525          218 :         if (e.node == nullptr) {
     526          163 :             double geomPos = e.pos;
     527          163 :             if (myCurrentEdge->hasLoadedLength()) {
     528            5 :                 geomPos *= myCurrentEdge->getGeometry().length() / myCurrentEdge->getLoadedLength();
     529              :             }
     530          163 :             e.node = new NBNode(nodeID, myCurrentEdge->getGeometry().positionAtOffset(geomPos));
     531          163 :             myNodeCont.insert(e.node);
     532              :         }
     533          218 :         NIXMLNodesHandler::processNodeType(attrs, e.node, e.node->getID(), e.node->getPosition(), false,
     534              :                                            myDefaultNodeType, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
     535          218 :         mySplits.push_back(e);
     536              :     }
     537          222 : }
     538              : 
     539              : 
     540              : bool
     541        18991 : NIXMLEdgesHandler::setNodes(const SUMOSAXAttributes& attrs) {
     542              :     // the names and the coordinates of the beginning and the end node
     543              :     // may be found, try
     544        18991 :     bool ok = true;
     545        18991 :     if (myIsUpdate) {
     546         2128 :         myFromNode = myCurrentEdge->getFromNode();
     547         2128 :         myToNode = myCurrentEdge->getToNode();
     548              :     }
     549        18991 :     if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
     550        16891 :         const std::string begNodeID = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
     551        16891 :         if (begNodeID != "") {
     552        16889 :             myFromNode = myNodeCont.retrieve(begNodeID);
     553        16889 :             if (myFromNode == nullptr) {
     554           12 :                 WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), myCurrentID, begNodeID);
     555              :             }
     556              :         }
     557         2100 :     } else if (!myIsUpdate) {
     558            9 :         WRITE_ERRORF(TL("The from-node is not given for edge '%'."), myCurrentID);
     559            3 :         ok = false;
     560              :     }
     561        18991 :     if (attrs.hasAttribute(SUMO_ATTR_TO)) {
     562        16890 :         const std::string endNodeID = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     563        16890 :         if (endNodeID != "") {
     564        16888 :             myToNode = myNodeCont.retrieve(endNodeID);
     565        16888 :             if (myToNode == nullptr) {
     566           12 :                 WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), myCurrentID, endNodeID);
     567              :             }
     568              :         }
     569         2101 :     } else if (!myIsUpdate) {
     570            9 :         WRITE_ERRORF(TL("The to-node is not given for edge '%'."), myCurrentID);
     571            3 :         ok = false;
     572              :     }
     573        19004 :     return ok && myFromNode != nullptr && myToNode != nullptr;
     574              : }
     575              : 
     576              : 
     577              : PositionVector
     578        18978 : NIXMLEdgesHandler::tryGetShape(const SUMOSAXAttributes& attrs) {
     579        18978 :     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE) && myShape.size() > 0) {
     580              :         return myShape;
     581              :     }
     582              :     // try to build shape
     583        18964 :     bool ok = true;
     584        18964 :     if (!attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     585        14333 :         const double maxSegmentLength = OptionsCont::getOptions().getFloat("geometry.max-segment-length");
     586        14333 :         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        14332 :             myReinitKeepEdgeShape = false;
     595        14332 :             return PositionVector();
     596              :         }
     597              :     }
     598         4631 :     PositionVector shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector());
     599         4631 :     if (!NBNetBuilder::transformCoordinates(shape, true, myLocation)) {
     600            0 :         WRITE_ERRORF(TL("Unable to project coordinates for edge '%'."), myCurrentID);
     601              :     }
     602         4631 :     myReinitKeepEdgeShape = myKeepEdgeShape;
     603              :     return shape;
     604         4631 : }
     605              : 
     606              : 
     607              : LaneSpreadFunction
     608        18978 : NIXMLEdgesHandler::tryGetLaneSpread(const SUMOSAXAttributes& attrs) {
     609        18978 :     bool ok = true;
     610        18978 :     LaneSpreadFunction result = myLanesSpread;
     611        18978 :     std::string lsfS = toString(result);
     612        37956 :     lsfS = attrs.getOpt<std::string>(SUMO_ATTR_SPREADTYPE, myCurrentID.c_str(), ok, lsfS);
     613              :     if (SUMOXMLDefinitions::LaneSpreadFunctions.hasString(lsfS)) {
     614        18978 :         result = SUMOXMLDefinitions::LaneSpreadFunctions.get(lsfS);
     615              :     } else {
     616            0 :         WRITE_WARNINGF(TL("Ignoring unknown spreadType '%' for edge '%'."), lsfS, myCurrentID);
     617              :     }
     618        18978 :     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        28528 : NIXMLEdgesHandler::myEndElement(int element) {
     646        28528 :     if (element == SUMO_TAG_VIEWSETTINGS_EDGES) {
     647         1153 :         delete myLocation;
     648         1153 :         myLocation = nullptr;
     649         1153 :         return;
     650              :     }
     651        27375 :     if (myCurrentEdge == nullptr) {
     652              :         return;
     653              :     }
     654        27176 :     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        18964 :         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        18964 :         if (mySidewalkWidth != NBEdge::UNSPECIFIED_WIDTH) {
     662           44 :             myCurrentEdge->addSidewalk(mySidewalkWidth);
     663              :         }
     664              :         // apply default stopOffsets of edge to all lanes without specified stopOffset.
     665        18964 :         const StopOffset stopOffsets = myCurrentEdge->getEdgeStopOffset();
     666        18964 :         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        18964 :         if (!myIsUpdate) {
     672              :             try {
     673        16837 :                 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        18964 :         myEdgeCont.processSplits(myCurrentEdge, mySplits, myNodeCont, myDistrictCont, myTLLogicCont);
     687        18964 :         myCurrentEdge = nullptr;
     688         8212 :     } else if (element == SUMO_TAG_LANE && myCurrentLaneIndex != -1) {
     689              :         myLastParameterised.pop_back();
     690         5858 :         myCurrentLaneIndex = -1;
     691              :     }
     692              : }
     693              : 
     694              : 
     695              : void
     696           11 : NIXMLEdgesHandler::addRoundabout(const SUMOSAXAttributes& attrs) {
     697           11 :     bool ok = true;
     698           11 :     const std::vector<std::string>& edgeIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nullptr, ok);
     699           11 :     if (ok) {
     700              :         EdgeSet roundabout;
     701           77 :         for (const std::string& eID : edgeIDs) {
     702           66 :             NBEdge* edge = myEdgeCont.retrieve(eID);
     703           66 :             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           11 :         myEdgeCont.addRoundabout(roundabout);
     712              :     }
     713           11 : }
     714              : 
     715              : 
     716              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1