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

Generated by: LCOV version 2.0-1