LCOV - code coverage report
Current view: top level - src/netimport - NIXMLNodesHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.7 % 179 166
Test Date: 2026-03-26 16:31:35 Functions: 90.9 % 11 10

            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    NIXMLNodesHandler.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Sascha Krieg
      18              : /// @author  Michael Behrisch
      19              : /// @date    Tue, 20 Nov 2001
      20              : ///
      21              : // Importer for network nodes stored in XML
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <iostream>
      27              : #include <utils/xml/SUMOSAXHandler.h>
      28              : #include <utils/xml/SUMOXMLDefinitions.h>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/common/StringUtils.h>
      31              : #include <utils/common/ToString.h>
      32              : #include <utils/common/StringTokenizer.h>
      33              : #include <utils/options/OptionsCont.h>
      34              : #include <utils/geom/GeoConvHelper.h>
      35              : #include <netbuild/NBNodeCont.h>
      36              : #include <netbuild/NBTrafficLightLogicCont.h>
      37              : #include <netbuild/NBOwnTLDef.h>
      38              : #include <netbuild/NBNetBuilder.h>
      39              : #include "NIXMLNodesHandler.h"
      40              : #include "NIImporter_SUMO.h"
      41              : 
      42              : 
      43              : // ===========================================================================
      44              : // method definitions
      45              : // ===========================================================================
      46         2373 : NIXMLNodesHandler::NIXMLNodesHandler(NBNodeCont& nc, NBEdgeCont& ec,
      47              :                                      NBTrafficLightLogicCont& tlc,
      48         2373 :                                      OptionsCont& options) :
      49              :     SUMOSAXHandler("xml-nodes - file"),
      50         2373 :     myOptions(options),
      51         2373 :     myNodeCont(nc),
      52         2373 :     myEdgeCont(ec),
      53         2373 :     myTLLogicCont(tlc),
      54         2373 :     myLocation(nullptr),
      55         2373 :     myDefaultNodeType(SUMOXMLDefinitions::NodeTypes.hasString(options.getString("default.junctions.type"))
      56         2375 :             ? SUMOXMLDefinitions::NodeTypes.get(options.getString("default.junctions.type")) : SumoXMLNodeType::UNKNOWN),
      57         4746 :     myLastParameterised(nullptr) {
      58         2373 : }
      59              : 
      60              : 
      61         2373 : NIXMLNodesHandler::~NIXMLNodesHandler() {
      62         2373 :     delete myLocation;
      63         2373 : }
      64              : 
      65              : 
      66              : void
      67        13714 : NIXMLNodesHandler::myStartElement(int element,
      68              :                                   const SUMOSAXAttributes& attrs) {
      69        13714 :     switch (element) {
      70          569 :         case SUMO_TAG_LOCATION:
      71          569 :             delete myLocation;
      72          569 :             myLocation = NIImporter_SUMO::loadLocation(attrs);
      73          569 :             if (myLocation) {
      74          569 :                 GeoConvHelper::setLoadedPlain(getFileName(), *myLocation);
      75              :             }
      76              :             break;
      77        11866 :         case SUMO_TAG_NODE:
      78        11866 :             addNode(attrs);
      79        11865 :             break;
      80           11 :         case SUMO_TAG_JOIN:
      81           11 :             addJoinCluster(attrs);
      82           11 :             break;
      83            1 :         case SUMO_TAG_JOINEXCLUDE:
      84            1 :             addJoinExclusion(attrs);
      85            1 :             break;
      86           11 :         case SUMO_TAG_DEL:
      87           11 :             deleteNode(attrs);
      88           11 :             break;
      89          149 :         case SUMO_TAG_PARAM:
      90          149 :             if (myLastParameterised != nullptr) {
      91          149 :                 bool ok = true;
      92          149 :                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
      93              :                 // circumventing empty string test
      94          149 :                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
      95          149 :                 myLastParameterised->setParameter(key, val);
      96              :             }
      97              :             break;
      98              :         default:
      99              :             break;
     100              :     }
     101        13713 : }
     102              : 
     103              : 
     104              : void
     105        13712 : NIXMLNodesHandler::myEndElement(int element) {
     106        13712 :     switch (element) {
     107        11865 :         case SUMO_TAG_NODE:
     108        11865 :             myLastParameterised = nullptr;
     109        11865 :             break;
     110              :         default:
     111              :             break;
     112              :     }
     113        13712 : }
     114              : 
     115              : 
     116              : void
     117        11866 : NIXMLNodesHandler::addNode(const SUMOSAXAttributes& attrs) {
     118        11866 :     bool ok = true;
     119              :     // get the id, report a warning if not given or empty...
     120        11866 :     myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     121        11866 :     if (!ok) {
     122            0 :         return;
     123              :     }
     124        11866 :     NBNode* node = myNodeCont.retrieve(myID);
     125              :     // retrieve the position of the node
     126              :     bool xOk = false;
     127              :     bool yOk = false;
     128              :     bool needConversion = true;
     129        11866 :     if (node != nullptr) {
     130         1470 :         myPosition = node->getPosition();
     131              :         xOk = yOk = true;
     132              :         needConversion = false;
     133              :     } else {
     134              :         myPosition.set(0, 0, 0); // better to reset than to reuse the previous (z)-value
     135              :     }
     136        11866 :     if (attrs.hasAttribute(SUMO_ATTR_X)) {
     137        11827 :         myPosition.set(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok), myPosition.y());
     138              :         xOk = true;
     139              :         needConversion = true;
     140              :     }
     141        11866 :     if (attrs.hasAttribute(SUMO_ATTR_Y)) {
     142        11828 :         myPosition.set(myPosition.x(), attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
     143              :         yOk = true;
     144              :         needConversion = true;
     145              :     }
     146        11866 :     if (attrs.hasAttribute(SUMO_ATTR_Z)) {
     147           57 :         myPosition.set(myPosition.x(), myPosition.y(), attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
     148              :     }
     149        11866 :     if (xOk && yOk) {
     150        11866 :         if (needConversion && !NBNetBuilder::transformCoordinate(myPosition, true, myLocation)) {
     151            0 :             WRITE_ERRORF(TL("Unable to project coordinates for node '%'."), myID);
     152              :         }
     153              :     } else {
     154            0 :         WRITE_ERRORF(TL("Missing position (at node ID='%')."), myID);
     155              :     }
     156        11866 :     bool updateEdgeGeometries = node != nullptr && myPosition != node->getPosition();
     157        11866 :     node = processNodeType(attrs, node, myID, myPosition, updateEdgeGeometries, myDefaultNodeType, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
     158        11865 :     myLastParameterised = node;
     159              : }
     160              : 
     161              : 
     162              : NBNode*
     163        12095 : NIXMLNodesHandler::processNodeType(const SUMOSAXAttributes& attrs, NBNode* node, const std::string& nodeID, const Position& position,
     164              :                                    bool updateEdgeGeometries,
     165              :                                    SumoXMLNodeType type,
     166              :                                    NBNodeCont& nc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc,
     167              :                                    GeoConvHelper* from_srs) {
     168        12095 :     bool ok = true;
     169              :     // get the type
     170        12095 :     if (node != nullptr) {
     171              :         type = node->getType();
     172              :     }
     173        24190 :     std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nodeID.c_str(), ok, "");
     174              :     if (SUMOXMLDefinitions::NodeTypes.hasString(typeS)) {
     175         6242 :         type = SUMOXMLDefinitions::NodeTypes.get(typeS);
     176         6242 :         if (type == SumoXMLNodeType::DEAD_END_DEPRECATED || type == SumoXMLNodeType::DEAD_END) {
     177              :             // dead end is a computed status. Reset this to unknown so it will
     178              :             // be corrected if additional connections are loaded
     179              :             type = SumoXMLNodeType::UNKNOWN;
     180              :         }
     181              :     }
     182              :     std::set<NBTrafficLightDefinition*> oldTLS;
     183              :     // check whether a prior node shall be modified
     184              :     const bool isPatch = node != nullptr;
     185        12095 :     if (node == nullptr) {
     186        10407 :         node = new NBNode(nodeID, position, type);
     187        10406 :         if (!nc.insert(node)) {
     188            0 :             throw ProcessError(TLF("Could not insert node though checked this before (id='%').", nodeID));
     189              :         }
     190              :     } else {
     191              :         // patch information
     192              :         oldTLS = node->getControllingTLS();
     193              :         if (node->getType() == SumoXMLNodeType::PRIORITY
     194         1688 :                 && (type == SumoXMLNodeType::RIGHT_BEFORE_LEFT || type == SumoXMLNodeType::LEFT_BEFORE_RIGHT)) {
     195            4 :             ec.removeRoundabout(node);
     196              :         }
     197         1688 :         node->reinit(position, type, updateEdgeGeometries);
     198              :     }
     199              :     // process traffic light definition
     200        12094 :     if (NBNode::isTrafficLight(type)) {
     201          604 :         processTrafficLightDefinitions(attrs, node, tlc);
     202        11490 :     } else if (isPatch && typeS != "") {
     203           37 :         nc.markAsNotTLS(node);
     204              :     }
     205              :     // remove previously set tls if this node is not controlled by them
     206        12126 :     for (std::set<NBTrafficLightDefinition*>::iterator i = oldTLS.begin(); i != oldTLS.end(); ++i) {
     207           32 :         if ((*i)->getNodes().size() == 0) {
     208           31 :             tlc.removeFully((*i)->getID());
     209              :         }
     210              :     }
     211              : 
     212              :     // set optional shape
     213        12094 :     PositionVector shape;
     214        12094 :     if (attrs.hasAttribute(SUMO_ATTR_SHAPE)) {
     215           78 :         shape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nodeID.c_str(), ok, PositionVector());
     216           39 :         if (!NBNetBuilder::transformCoordinates(shape, true, from_srs)) {
     217            0 :             WRITE_ERRORF(TL("Unable to project node shape at node '%'."), node->getID());
     218              :         }
     219           39 :         if (shape.size() > 2) {
     220           38 :             shape.closePolygon();
     221              :         }
     222           39 :         node->setCustomShape(shape);
     223              :     }
     224              :     // set optional radius
     225        12094 :     if (attrs.hasAttribute(SUMO_ATTR_RADIUS)) {
     226           61 :         node->setRadius(attrs.get<double>(SUMO_ATTR_RADIUS, nodeID.c_str(), ok));
     227              :     }
     228              :     // set optional keepClear flag
     229        12094 :     if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
     230            1 :         node->setKeepClear(attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nodeID.c_str(), ok));
     231              :     }
     232        12094 :     node->setRightOfWay(attrs.getOpt<RightOfWay>(SUMO_ATTR_RIGHT_OF_WAY, nodeID.c_str(), ok, node->getRightOfWay()));
     233        12094 :     node->setFringeType(attrs.getOpt<FringeType>(SUMO_ATTR_FRINGE, nodeID.c_str(), ok, node->getFringeType()));
     234        12094 :     node->setRoundaboutType(attrs.getOpt<RoundaboutType>(SUMO_ATTR_ROUNDABOUT, nodeID.c_str(), ok, node->getRoundaboutType()));
     235              :     // set optional name
     236        12094 :     if (attrs.hasAttribute(SUMO_ATTR_NAME)) {
     237            4 :         node->setName(attrs.get<std::string>(SUMO_ATTR_NAME, nodeID.c_str(), ok));
     238              :     }
     239        12094 :     return node;
     240        12094 : }
     241              : 
     242              : 
     243              : void
     244           11 : NIXMLNodesHandler::deleteNode(const SUMOSAXAttributes& attrs) {
     245           11 :     bool ok = true;
     246              :     // get the id, report a warning if not given or empty...
     247           11 :     myID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     248           11 :     if (!ok) {
     249            0 :         return;
     250              :     }
     251           11 :     NBNode* node = myNodeCont.retrieve(myID);
     252           11 :     if (node == nullptr) {
     253            0 :         WRITE_WARNING("Ignoring tag '" + toString(SUMO_TAG_DEL) + "' for unknown node '" +
     254              :                       myID + "'");
     255            0 :         return;
     256              :     } else {
     257           11 :         myNodeCont.extract(node, true);
     258              :     }
     259              : }
     260              : 
     261              : 
     262              : void
     263           11 : NIXMLNodesHandler::addJoinCluster(const SUMOSAXAttributes& attrs) {
     264           11 :     bool ok = true;
     265           11 :     const std::string clusterString = attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok);
     266           11 :     const std::set<std::string>& cluster = StringTokenizer(clusterString).getSet();
     267              : 
     268           11 :     myID = attrs.getOpt<std::string>(SUMO_ATTR_ID, nullptr, ok, myNodeCont.createClusterId(cluster));
     269              : 
     270           11 :     Position pos = Position::INVALID;
     271           11 :     if (attrs.hasAttribute(SUMO_ATTR_X)) {
     272            1 :         pos.setx(attrs.get<double>(SUMO_ATTR_X, myID.c_str(), ok));
     273              :     }
     274           11 :     if (attrs.hasAttribute(SUMO_ATTR_Y)) {
     275            1 :         pos.sety(attrs.get<double>(SUMO_ATTR_Y, myID.c_str(), ok));
     276              :     }
     277           11 :     if (attrs.hasAttribute(SUMO_ATTR_Z)) {
     278            1 :         pos.setz(attrs.get<double>(SUMO_ATTR_Z, myID.c_str(), ok));
     279              :     }
     280           11 :     const bool reset = attrs.getOpt<bool>(SUMO_ATTR_RESET, myID.c_str(), ok, OptionsCont::getOptions().getBool("junctions.join-reset"));
     281              : 
     282           11 :     NBNode* node = processNodeType(attrs, nullptr, myID, pos, false, myDefaultNodeType, myNodeCont, myEdgeCont, myTLLogicCont, myLocation);
     283           11 :     if (ok) {
     284           11 :         myNodeCont.addCluster2Join(cluster, node, reset);
     285              :     }
     286           11 : }
     287              : 
     288              : 
     289              : void
     290            1 : NIXMLNodesHandler::addJoinExclusion(const SUMOSAXAttributes& attrs) {
     291            1 :     bool ok = true;
     292            2 :     const std::vector<std::string> ids = StringTokenizer(
     293            2 :             attrs.get<std::string>(SUMO_ATTR_NODES, nullptr, ok)).getVector();
     294            1 :     if (ok) {
     295            1 :         myNodeCont.addJoinExclusion(ids);
     296              :     }
     297            1 : }
     298              : 
     299              : 
     300              : void
     301          604 : NIXMLNodesHandler::processTrafficLightDefinitions(const SUMOSAXAttributes& attrs,
     302              :         NBNode* currentNode, NBTrafficLightLogicCont& tlc) {
     303              :     // try to get the tl-id
     304              :     // if a tl-id is given, we will look whether this tl already exists
     305              :     //  if so, we will add the node to it (and to all programs with this id), otherwise allocate a new one with this id
     306              :     // if no tl-id exists, we will build a tl with the node's id
     307              :     std::set<NBTrafficLightDefinition*> tlDefs;
     308          604 :     bool ok = true;
     309              : 
     310          604 :     std::string oldTlID = "";
     311         1208 :     std::string oldTypeS = OptionsCont::getOptions().getString("tls.default-type");
     312              : 
     313          604 :     if (currentNode->isTLControlled()) {
     314           30 :         NBTrafficLightDefinition* oldDef = *(currentNode->getControllingTLS().begin());
     315              :         oldTlID = oldDef->getID();
     316           60 :         oldTypeS = toString(oldDef->getType());
     317              :     }
     318         1208 :     std::string tlID = attrs.getOpt<std::string>(SUMO_ATTR_TLID, nullptr, ok, oldTlID);
     319         1208 :     std::string typeS = attrs.getOpt<std::string>(SUMO_ATTR_TLTYPE, nullptr, ok, oldTypeS);
     320          604 :     if (tlID != oldTlID || typeS != oldTypeS) {
     321          379 :         currentNode->removeTrafficLights();
     322              :     }
     323              :     TrafficLightType type;
     324              :     if (SUMOXMLDefinitions::TrafficLightTypes.hasString(typeS)) {
     325          604 :         type = SUMOXMLDefinitions::TrafficLightTypes.get(typeS);
     326              :     } else {
     327            0 :         WRITE_ERRORF(TL("Unknown traffic light type '%' for node '%'."), typeS, currentNode->getID());
     328            0 :         return;
     329              :     }
     330              :     TrafficLightLayout layout = TrafficLightLayout::DEFAULT;
     331          604 :     if (attrs.hasAttribute(SUMO_ATTR_TLLAYOUT)) {
     332           19 :         std::string layoutS = attrs.get<std::string>(SUMO_ATTR_TLLAYOUT, nullptr, ok);
     333              :         if (SUMOXMLDefinitions::TrafficLightLayouts.hasString(layoutS)) {
     334           19 :             layout = SUMOXMLDefinitions::TrafficLightLayouts.get(layoutS);
     335              :         } else {
     336            0 :             WRITE_ERRORF(TL("Unknown traffic light layout '%' for node '%'."), typeS, currentNode->getID());
     337              :             return;
     338              :         }
     339              :     }
     340          604 :     if (tlID != "" && tlc.getPrograms(tlID).size() > 0) {
     341              :         // we already have definitions for this tlID
     342          136 :         for (auto item : tlc.getPrograms(tlID)) {
     343           68 :             NBTrafficLightDefinition* def = item.second;
     344              :             tlDefs.insert(def);
     345           68 :             def->addNode(currentNode);
     346           68 :             if (def->getType() != type && attrs.hasAttribute(SUMO_ATTR_TLTYPE)) {
     347            3 :                 WRITE_WARNINGF(TL("Changing traffic light type '%' to '%' for tl '%'."), toString(def->getType()), typeS, tlID);
     348            1 :                 def->setType(type);
     349            1 :                 if (type != TrafficLightType::STATIC && dynamic_cast<NBLoadedSUMOTLDef*>(def) != nullptr) {
     350            1 :                     dynamic_cast<NBLoadedSUMOTLDef*>(def)->guessMinMaxDuration();
     351              :                 }
     352              :             }
     353           68 :             if (layout != TrafficLightLayout::DEFAULT && dynamic_cast<NBOwnTLDef*>(def) != nullptr) {
     354              :                 dynamic_cast<NBOwnTLDef*>(def)->setLayout(layout);
     355              :             }
     356              :         }
     357              :     } else {
     358              :         // we need to add a new defition
     359          536 :         tlID = (tlID == "" ? currentNode->getID() : tlID);
     360          536 :         NBOwnTLDef* tlDef = new NBOwnTLDef(tlID, currentNode, 0, type);
     361          536 :         if (!tlc.insert(tlDef)) {
     362              :             // actually, nothing should fail here
     363            0 :             delete tlDef;
     364            0 :             throw ProcessError(TLF("Could not allocate tls '%'.", currentNode->getID()));
     365              :         }
     366              :         tlDef->setLayout(layout);
     367          536 :         tlDefs.insert(tlDef);
     368              :     }
     369              :     // process inner edges which shall be controlled
     370          604 :     const std::vector<std::string>& controlledInner = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_CONTROLLED_INNER, nullptr, ok);
     371          604 :     if (controlledInner.size() != 0) {
     372           48 :         for (std::set<NBTrafficLightDefinition*>::iterator it = tlDefs.begin(); it != tlDefs.end(); it++) {
     373           24 :             (*it)->addControlledInnerEdges(controlledInner);
     374              :         }
     375              :     }
     376          604 : }
     377              : 
     378              : 
     379              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1