LCOV - code coverage report
Current view: top level - src/netimport - NIXMLConnectionsHandler.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 195 258 75.6 %
Date: 2024-05-02 15:31:40 Functions: 9 11 81.8 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
       4             : // This program and the accompanying materials are made available under the
       5             : // terms of the Eclipse Public License 2.0 which is available at
       6             : // https://www.eclipse.org/legal/epl-2.0/
       7             : // This Source Code may also be made available under the following Secondary
       8             : // Licenses when the conditions for such availability set forth in the Eclipse
       9             : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10             : // or later which is available at
      11             : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12             : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13             : /****************************************************************************/
      14             : /// @file    NIXMLConnectionsHandler.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Jakob Erdmann
      17             : /// @author  Michael Behrisch
      18             : /// @author  Laura Bieker
      19             : /// @date    Thu, 17 Oct 2002
      20             : ///
      21             : // Importer for edge connections stored in XML
      22             : /****************************************************************************/
      23             : #include <config.h>
      24             : 
      25             : #include <string>
      26             : #include <iostream>
      27             : #include "NIXMLConnectionsHandler.h"
      28             : #include <netbuild/NBEdge.h>
      29             : #include <netbuild/NBEdgeCont.h>
      30             : #include <netbuild/NBNodeCont.h>
      31             : #include <netbuild/NBTrafficLightLogicCont.h>
      32             : #include <netbuild/NBNode.h>
      33             : #include <netbuild/NBNetBuilder.h>
      34             : #include <utils/common/StringTokenizer.h>
      35             : #include <utils/xml/SUMOSAXHandler.h>
      36             : #include <utils/xml/SUMOXMLDefinitions.h>
      37             : #include <utils/common/ToString.h>
      38             : #include <utils/common/StringUtils.h>
      39             : #include <utils/common/UtilExceptions.h>
      40             : #include <utils/common/MsgHandler.h>
      41             : #include <utils/options/OptionsCont.h>
      42             : 
      43             : 
      44             : // ===========================================================================
      45             : // method definitions
      46             : // ===========================================================================
      47        2168 : NIXMLConnectionsHandler::NIXMLConnectionsHandler(NBEdgeCont& ec, NBNodeCont& nc, NBTrafficLightLogicCont& tlc) :
      48             :     SUMOSAXHandler("xml-connection-description"),
      49        2168 :     myEdgeCont(ec),
      50        2168 :     myNodeCont(nc),
      51        2168 :     myTLLogicCont(tlc),
      52        2168 :     myHaveWarnedAboutDeprecatedLanes(false),
      53        4336 :     myErrorMsgHandler(OptionsCont::getOptions().getBool("ignore-errors.connections") ?
      54        8672 :                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()) {}
      55             : 
      56             : 
      57        2168 : NIXMLConnectionsHandler::~NIXMLConnectionsHandler() {}
      58             : 
      59             : 
      60             : void
      61        2441 : NIXMLConnectionsHandler::myStartElement(int element,
      62             :                                         const SUMOSAXAttributes& attrs) {
      63        2441 :     if (element == SUMO_TAG_DEL) {
      64          69 :         bool ok = true;
      65          69 :         std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
      66          69 :         std::string to = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
      67          69 :         if (!ok) {
      68             :             return;
      69             :         }
      70             :         // these connections were removed when the edge was deleted
      71         214 :         if (myEdgeCont.wasRemoved(from) || myEdgeCont.wasRemoved(to)) {
      72             :             return;
      73             :         }
      74          27 :         NBEdge* fromEdge = myEdgeCont.retrieve(from);
      75          27 :         NBEdge* toEdge = myEdgeCont.retrieve(to);
      76          27 :         if (fromEdge == nullptr) {
      77           0 :             myErrorMsgHandler->informf("The connection-source edge '%' to reset is not known.", from);
      78           0 :             return;
      79             :         }
      80          27 :         if (toEdge == nullptr) {
      81           0 :             myErrorMsgHandler->informf("The connection-destination edge '%' to reset is not known.", to);
      82           0 :             return;
      83             :         }
      84          27 :         if (!fromEdge->isConnectedTo(toEdge) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::EDGE2EDGES) {
      85           8 :             WRITE_WARNINGF(TL("Target edge '%' is not connected with '%'; the connection cannot be reset."), toEdge->getID(), fromEdge->getID());
      86           2 :             return;
      87             :         }
      88          25 :         int fromLane = -1; // Assume all lanes are to be reset.
      89          25 :         int toLane = -1;
      90          25 :         if (attrs.hasAttribute(SUMO_ATTR_LANE)
      91          25 :                 || attrs.hasAttribute(SUMO_ATTR_FROM_LANE)
      92          32 :                 || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
      93          18 :             if (!parseLaneInfo(attrs, fromEdge, toEdge, &fromLane, &toLane)) {
      94             :                 return;
      95             :             }
      96             :             // we could be trying to reset a connection loaded from a sumo net and which has become obsolete.
      97             :             // In this case it's ok to encounter invalid lance indices
      98          18 :             if (!fromEdge->hasConnectionTo(toEdge, toLane) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2EDGES) {
      99           0 :                 WRITE_WARNINGF(TL("Edge '%' has no connection to lane '%'; the connection cannot be reset."), fromEdge->getID(), toEdge->getLaneID(toLane));
     100             :             }
     101             :         }
     102          25 :         fromEdge->removeFromConnections(toEdge, fromLane, toLane, true);
     103             :     }
     104             : 
     105        2397 :     if (element == SUMO_TAG_CONNECTION) {
     106        1838 :         bool ok = true;
     107        1838 :         std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, "connection", ok);
     108        1838 :         std::string to = attrs.getOpt<std::string>(SUMO_ATTR_TO, "connection", ok, "");
     109        9191 :         if (!ok || myEdgeCont.wasIgnored(from) || myEdgeCont.wasIgnored(to)) {
     110             :             return;
     111             :         }
     112             :         // extract edges
     113        1837 :         NBEdge* fromEdge = myEdgeCont.retrieve(from);
     114        1837 :         NBEdge* toEdge = to.length() != 0 ? myEdgeCont.retrieve(to) : nullptr;
     115             :         // check whether they are valid
     116        1837 :         if (fromEdge == nullptr) {
     117           0 :             myErrorMsgHandler->inform("The connection-source edge '" + from + "' is not known.");
     118           0 :             return;
     119             :         }
     120        1837 :         if (toEdge == nullptr && to.length() != 0) {
     121           0 :             myErrorMsgHandler->inform("The connection-destination edge '" + to + "' is not known.");
     122           0 :             return;
     123             :         }
     124             :         // parse optional lane information
     125        1837 :         if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_FROM_LANE) || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
     126        1589 :             parseLaneBound(attrs, fromEdge, toEdge);
     127             :         } else {
     128         248 :             fromEdge->addEdge2EdgeConnection(toEdge);
     129         248 :             fromEdge->getToNode()->invalidateTLS(myTLLogicCont, true, false);
     130         248 :             if (attrs.hasAttribute(SUMO_ATTR_PASS)
     131         247 :                     || attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)
     132         247 :                     || attrs.hasAttribute(SUMO_ATTR_CONTPOS)
     133         247 :                     || attrs.hasAttribute(SUMO_ATTR_VISIBILITY_DISTANCE)
     134         247 :                     || attrs.hasAttribute(SUMO_ATTR_SPEED)
     135         246 :                     || attrs.hasAttribute(SUMO_ATTR_LENGTH)
     136         246 :                     || attrs.hasAttribute(SUMO_ATTR_UNCONTROLLED)
     137         246 :                     || attrs.hasAttribute(SUMO_ATTR_SHAPE)
     138         246 :                     || attrs.hasAttribute(SUMO_ATTR_ALLOW)
     139         494 :                     || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     140           4 :                 WRITE_ERROR("No additional connection attributes are permitted in connection from edge '" + fromEdge->getID() + "' unless '"
     141             :                             + toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) + "' are set.");
     142             :             }
     143             :         }
     144             :     }
     145        2396 :     if (element == SUMO_TAG_PROHIBITION) {
     146          14 :         bool ok = true;
     147          14 :         std::string prohibitor = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITOR, nullptr, ok, "");
     148          14 :         std::string prohibited = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITED, nullptr, ok, "");
     149          14 :         if (!ok) {
     150             :             return;
     151             :         }
     152          14 :         NBConnection prohibitorC = parseConnection("prohibitor", prohibitor);
     153          14 :         NBConnection prohibitedC = parseConnection("prohibited", prohibited);
     154          14 :         if (prohibitorC == NBConnection::InvalidConnection || prohibitedC == NBConnection::InvalidConnection) {
     155             :             // something failed
     156             :             return;
     157             :         }
     158          14 :         NBNode* n = prohibitorC.getFrom()->getToNode();
     159          14 :         n->addSortedLinkFoes(prohibitorC, prohibitedC);
     160          14 :     }
     161        2396 :     if (element == SUMO_TAG_CROSSING) {
     162         276 :         addCrossing(attrs);
     163             :     }
     164        2396 :     if (element == SUMO_TAG_WALKINGAREA) {
     165           8 :         addWalkingArea(attrs);
     166             :     }
     167             : }
     168             : 
     169             : 
     170             : NBConnection
     171          28 : NIXMLConnectionsHandler::parseConnection(const std::string& defRole, const std::string& def) {
     172             :     // split from/to
     173             :     const std::string::size_type div = def.find("->");
     174          28 :     if (div == std::string::npos) {
     175           0 :         myErrorMsgHandler->inform("Missing connection divider in " + defRole + " '" + def + "'");
     176           0 :         return NBConnection::InvalidConnection;
     177             :     }
     178          28 :     std::string fromDef = def.substr(0, div);
     179          28 :     std::string toDef = def.substr(div + 2);
     180             : 
     181             :     // retrieve them now
     182          28 :     NBEdge* fromE = myEdgeCont.retrieve(fromDef);
     183          28 :     NBEdge* toE = myEdgeCont.retrieve(toDef);
     184             :     // check
     185          28 :     if (fromE == nullptr) {
     186           0 :         myErrorMsgHandler->inform("Could not find edge '" + fromDef + "' in " + defRole + " '" + def + "'");
     187           0 :         return NBConnection::InvalidConnection;
     188             :     }
     189          28 :     if (toE == nullptr) {
     190           0 :         myErrorMsgHandler->inform("Could not find edge '" + toDef + "' in " + defRole + " '" + def + "'");
     191           0 :         return NBConnection::InvalidConnection;
     192             :     }
     193          28 :     return NBConnection(fromE, toE);
     194             : }
     195             : 
     196             : 
     197             : void
     198        1589 : NIXMLConnectionsHandler::parseLaneBound(const SUMOSAXAttributes& attrs, NBEdge* from, NBEdge* to) {
     199        1589 :     if (to == nullptr) {
     200             :         // do nothing if it's a dead end
     201           0 :         return;
     202             :     }
     203        1589 :     bool ok = true;
     204             :     // get the begin and the end lane
     205             :     int fromLane;
     206             :     int toLane;
     207             :     try {
     208        1589 :         if (!parseLaneInfo(attrs, from, to, &fromLane, &toLane)) {
     209           0 :             return;
     210             :         }
     211        1589 :         if (fromLane < 0) {
     212           0 :             myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_FROM_LANE) +
     213           0 :                                        " in connection from '%' to '%'.", fromLane, from->getID(), to->getID());
     214           0 :             return;
     215             :         }
     216        1589 :         if (toLane < 0) {
     217           0 :             myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_TO_LANE) +
     218           0 :                                        " in connection from '%' to '%'.", toLane, from->getID(), to->getID());
     219           0 :             return;
     220             :         }
     221             : 
     222        1589 :         NBEdge::Connection defaultCon(fromLane, to, toLane);
     223        1589 :         if (from->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     224             :             // maybe we are patching an existing connection
     225        1028 :             std::vector<NBEdge::Connection> existing = from->getConnectionsFromLane(fromLane, to, toLane);
     226        1028 :             if (existing.size() > 0) {
     227             :                 assert(existing.size() == 1);
     228          10 :                 defaultCon = existing.front();
     229             :                 // remove the original so we can insert the replacement
     230          10 :                 from->removeFromConnections(defaultCon);
     231             :             } else {
     232        1018 :                 from->getToNode()->invalidateTLS(myTLLogicCont, true, false);
     233             :             }
     234        1028 :         }
     235        1589 :         const bool mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, defaultCon.mayDefinitelyPass);
     236        1589 :         KeepClear keepClear = defaultCon.keepClear;
     237        1589 :         if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
     238           7 :             keepClear = attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok) ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE;
     239             :         }
     240        1589 :         const double contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, defaultCon.contPos);
     241        1589 :         const double visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, defaultCon.visibility);
     242        1589 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, defaultCon.speed);
     243        1589 :         const double friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, defaultCon.friction);
     244        1589 :         const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, defaultCon.customLength);
     245        1589 :         const bool uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, defaultCon.uncontrolled);
     246        1589 :         const bool indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
     247        3178 :         const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
     248        1589 :         PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, defaultCon.customShape);
     249        1589 :         std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
     250        3178 :         std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
     251             :         SVCPermissions permissions;
     252        3176 :         if (allow == "" && disallow == "") {
     253        1586 :             permissions = SVC_UNSPECIFIED;
     254             :         } else {
     255           6 :             permissions = parseVehicleClasses(attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, ""), attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, ""));
     256             :         }
     257        1589 :         SVCPermissions changeLeft = SVC_UNSPECIFIED;
     258             :         SVCPermissions changeRight = SVC_UNSPECIFIED;
     259        1589 :         if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT)) {
     260           4 :             changeLeft = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok), "");
     261             :         }
     262        1589 :         if (attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
     263           4 :             changeRight = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok), "");
     264             :         }
     265             : 
     266        1589 :         if (attrs.hasAttribute(SUMO_ATTR_SHAPE) && !NBNetBuilder::transformCoordinates(customShape)) {
     267           0 :             WRITE_ERRORF(TL("Unable to project shape for connection from edge '%' to edge '%'."), from->getID(), to->getID());
     268             :         }
     269        1589 :         if (!ok) {
     270             :             return;
     271             :         }
     272        1589 :         if (!from->addLane2LaneConnection(fromLane, to, toLane, NBEdge::Lane2LaneInfoType::USER, true, mayDefinitelyPass,
     273             :                                           keepClear, contPos, visibility, speed, friction, length, customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight)) {
     274          36 :             if (OptionsCont::getOptions().getBool("show-errors.connections-first-try")) {
     275           6 :                 WRITE_WARNINGF(TL("Could not set loaded connection from lane '%' to lane '%'."), from->getLaneID(fromLane), to->getLaneID(toLane));
     276             :             }
     277             :             // set as to be re-applied after network processing
     278          18 :             myEdgeCont.addPostProcessConnection(from->getID(), fromLane, to->getID(), toLane, mayDefinitelyPass, keepClear, contPos, visibility,
     279             :                                                 speed, friction, length, customShape, uncontrolled, false, permissions, indirectLeft, edgeType, changeLeft, changeRight);
     280             :         }
     281        3178 :     } catch (NumberFormatException&) {
     282           0 :         myErrorMsgHandler->inform("At least one of the defined lanes was not numeric");
     283           0 :     }
     284             : }
     285             : 
     286             : bool
     287        1607 : NIXMLConnectionsHandler::parseLaneInfo(const SUMOSAXAttributes& attributes, NBEdge* fromEdge, NBEdge* toEdge,
     288             :                                        int* fromLane, int* toLane) {
     289        1607 :     if (attributes.hasAttribute(SUMO_ATTR_LANE)) {
     290           0 :         return parseDeprecatedLaneDefinition(attributes, fromEdge, toEdge, fromLane, toLane);
     291             :     } else {
     292        1607 :         return parseLaneDefinition(attributes, fromLane, toLane);
     293             :     }
     294             : }
     295             : 
     296             : 
     297             : inline bool
     298           0 : NIXMLConnectionsHandler::parseDeprecatedLaneDefinition(const SUMOSAXAttributes& attributes,
     299             :         NBEdge* from, NBEdge* to,
     300             :         int* fromLane, int* toLane) {
     301           0 :     bool ok = true;
     302           0 :     if (!myHaveWarnedAboutDeprecatedLanes) {
     303           0 :         myHaveWarnedAboutDeprecatedLanes = true;
     304           0 :         WRITE_WARNING("'" + toString(SUMO_ATTR_LANE) + "' is deprecated, please use '" +
     305             :                       toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) +
     306             :                       "' instead.");
     307             :     }
     308             : 
     309           0 :     std::string laneConn = attributes.get<std::string>(SUMO_ATTR_LANE, nullptr, ok);
     310           0 :     StringTokenizer st(laneConn, ':');
     311           0 :     if (!ok || st.size() != 2) {
     312           0 :         myErrorMsgHandler->inform("Invalid lane to lane connection from '" +
     313           0 :                                   from->getID() + "' to '" + to->getID() + "'.");
     314           0 :         return false; // There was an error.
     315             :     }
     316             : 
     317           0 :     *fromLane = StringUtils::toIntSecure(st.next(), -1);
     318           0 :     *toLane = StringUtils::toIntSecure(st.next(), -1);
     319             : 
     320           0 :     return true; // We succeeded.
     321           0 : }
     322             : 
     323             : 
     324             : inline bool
     325        1607 : NIXMLConnectionsHandler::parseLaneDefinition(const SUMOSAXAttributes& attributes,
     326             :         int* fromLane,
     327             :         int* toLane) {
     328        1607 :     bool ok = true;
     329        1607 :     *fromLane = attributes.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
     330        1607 :     *toLane = attributes.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
     331        1607 :     return ok;
     332             : }
     333             : 
     334             : 
     335             : void
     336         276 : NIXMLConnectionsHandler::addCrossing(const SUMOSAXAttributes& attrs) {
     337         276 :     bool ok = true;
     338             :     EdgeVector edges;
     339         276 :     const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
     340         276 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nodeID.c_str(), ok, NBEdge::UNSPECIFIED_WIDTH, true);
     341         276 :     const bool discard = attrs.getOpt<bool>(SUMO_ATTR_DISCARD, nodeID.c_str(), ok, false, true);
     342         276 :     int tlIndex = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
     343         276 :     int tlIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
     344         276 :     NBNode* node = myNodeCont.retrieve(nodeID);
     345         276 :     if (node == nullptr) {
     346           0 :         if (!discard && myNodeCont.wasRemoved(nodeID)) {
     347           0 :             WRITE_ERRORF(TL("Node '%' in crossing is not known."), nodeID);
     348             :         }
     349           0 :         return;
     350             :     }
     351         276 :     if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     352           1 :         if (discard) {
     353           1 :             node->discardAllCrossings(true);
     354             :             return;
     355             :         } else {
     356           0 :             WRITE_ERRORF(TL("No edges specified for crossing at node '%'."), nodeID);
     357           0 :             return;
     358             :         }
     359             :     }
     360         766 :     for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
     361         491 :         NBEdge* edge = myEdgeCont.retrieve(id);
     362         491 :         if (edge == nullptr) {
     363           3 :             if (!(discard && myEdgeCont.wasRemoved(id))) {
     364           0 :                 WRITE_ERRORF(TL("Edge '%' for crossing at node '%' is not known."), id, nodeID);
     365           0 :                 return;
     366             :             } else {
     367           1 :                 edge = myEdgeCont.retrieve(id, true);
     368             :             }
     369             :         } else {
     370         490 :             if (edge->getToNode() != node && edge->getFromNode() != node) {
     371           0 :                 if (!discard) {
     372           0 :                     WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     373           0 :                     return;
     374             :                 }
     375             :             }
     376             :         }
     377         491 :         edges.push_back(edge);
     378         275 :     }
     379         275 :     if (!ok) {
     380             :         return;
     381             :     }
     382         274 :     bool priority = attrs.getOpt<bool>(SUMO_ATTR_PRIORITY, nodeID.c_str(), ok, node->isTLControlled(), true);
     383         274 :     if (node->isTLControlled() && !priority) {
     384             :         // traffic_light nodes should always have priority crossings
     385           6 :         WRITE_WARNINGF(TL("Crossing at controlled node '%' must be prioritized"), nodeID);
     386             :         priority = true;
     387             :     }
     388         274 :     PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     389         274 :     if (!NBNetBuilder::transformCoordinates(customShape)) {
     390           0 :         WRITE_ERRORF(TL("Unable to project shape for crossing at node '%'."), node->getID());
     391             :     }
     392         274 :     if (discard) {
     393           2 :         node->removeCrossing(edges);
     394             :     } else {
     395         544 :         if (node->checkCrossingDuplicated(edges)) {
     396             :             // possibly a diff
     397           3 :             NBNode::Crossing* existing =  node->getCrossing(edges);
     398           5 :             if (!(
     399           6 :                         (attrs.hasAttribute(SUMO_ATTR_WIDTH) && width != existing->width)
     400           2 :                         || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX) && tlIndex != existing->customTLIndex)
     401           2 :                         || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2) && tlIndex2 != existing->customTLIndex2)
     402           2 :                         || (attrs.hasAttribute(SUMO_ATTR_PRIORITY) && priority != existing->priority))) {
     403           6 :                 WRITE_ERRORF(TL("Crossing with edges '%' already exists at node '%'."), toString(edges), node->getID());
     404             :                 return;
     405             :             } else {
     406             :                 // replace existing, keep old attributes
     407           1 :                 if (!attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     408           0 :                     width = existing->width;
     409             :                 }
     410           1 :                 if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX)) {
     411           1 :                     tlIndex = existing->customTLIndex;
     412             :                 }
     413           1 :                 if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2)) {
     414           1 :                     tlIndex2 = existing->customTLIndex2;
     415             :                 }
     416           1 :                 if (!attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
     417           1 :                     priority = existing->priority;
     418             :                 }
     419           1 :                 node->removeCrossing(edges);
     420             :             }
     421             :         }
     422         540 :         node->addCrossing(edges, width, priority, tlIndex, tlIndex2, customShape);
     423             :     }
     424         274 : }
     425             : 
     426             : 
     427             : void
     428           8 : NIXMLConnectionsHandler::addWalkingArea(const SUMOSAXAttributes& attrs) {
     429           8 :     bool ok = true;
     430             :     NBNode* node = nullptr;
     431             :     EdgeVector edges;
     432           8 :     const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
     433             :     std::vector<std::string> edgeIDs;
     434           8 :     if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     435           0 :         WRITE_ERRORF(TL("No edges specified for walkingArea at node '%'."), nodeID);
     436           0 :         return;
     437             :     }
     438          20 :     for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
     439          12 :         NBEdge* edge = myEdgeCont.retrieve(id);
     440          12 :         if (edge == nullptr) {
     441           0 :             WRITE_ERRORF(TL("Edge '%' for walkingArea at node '%' is not known."), id, nodeID);
     442           0 :             return;
     443             :         }
     444          12 :         if (node == nullptr) {
     445           8 :             if (edge->getToNode()->getID() == nodeID) {
     446             :                 node = edge->getToNode();
     447           2 :             } else if (edge->getFromNode()->getID() == nodeID) {
     448             :                 node = edge->getFromNode();
     449             :             } else {
     450           0 :                 WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     451           0 :                 return;
     452             :             }
     453             :         } else {
     454           4 :             if (edge->getToNode() != node && edge->getFromNode() != node) {
     455           0 :                 WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     456           0 :                 return;
     457             :             }
     458             :         }
     459          12 :         edges.push_back(edge);
     460           8 :     }
     461           8 :     if (!ok) {
     462             :         return;
     463             :     }
     464           8 :     PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     465           8 :     double customWidth = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nullptr, ok, NBEdge::UNSPECIFIED_WIDTH);
     466           8 :     if (!NBNetBuilder::transformCoordinates(customShape)) {
     467           0 :         WRITE_ERRORF(TL("Unable to project shape for walkingArea at node '%'."), node->getID());
     468             :     }
     469           8 :     node->addWalkingAreaShape(edges, customShape, customWidth);
     470           8 : }
     471             : 
     472             : 
     473             : /****************************************************************************/

Generated by: LCOV version 1.14