LCOV - code coverage report
Current view: top level - src/netimport - NIXMLConnectionsHandler.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 77.4 % 297 230
Test Date: 2024-12-21 15:45:41 Functions: 86.7 % 15 13

            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 <netbuild/NBEdge.h>
      28              : #include <netbuild/NBEdgeCont.h>
      29              : #include <netbuild/NBNodeCont.h>
      30              : #include <netbuild/NBTrafficLightLogicCont.h>
      31              : #include <netbuild/NBNode.h>
      32              : #include <netbuild/NBNetBuilder.h>
      33              : #include <utils/geom/GeoConvHelper.h>
      34              : #include <utils/xml/SUMOSAXHandler.h>
      35              : #include <utils/xml/SUMOXMLDefinitions.h>
      36              : #include <utils/common/StringTokenizer.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              : #include "NIImporter_SUMO.h"
      43              : #include "NIXMLConnectionsHandler.h"
      44              : 
      45              : 
      46              : // ===========================================================================
      47              : // method definitions
      48              : // ===========================================================================
      49         1837 : NIXMLConnectionsHandler::NIXMLConnectionsHandler(NBEdgeCont& ec, NBNodeCont& nc, NBTrafficLightLogicCont& tlc) :
      50              :     SUMOSAXHandler("xml-connection-description"),
      51         1837 :     myEdgeCont(ec),
      52         1837 :     myNodeCont(nc),
      53         1837 :     myTLLogicCont(tlc),
      54         1837 :     myHaveWarnedAboutDeprecatedLanes(false),
      55         3674 :     myErrorMsgHandler(OptionsCont::getOptions().getBool("ignore-errors.connections") ?
      56         1837 :                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      57         1837 :     myLocation(nullptr),
      58         3674 :     myLastParameterised(nullptr) {
      59         1837 : }
      60              : 
      61              : 
      62         1837 : NIXMLConnectionsHandler::~NIXMLConnectionsHandler() {
      63         1837 :     delete myLocation;
      64         1837 : }
      65              : 
      66              : 
      67              : void
      68         2391 : NIXMLConnectionsHandler::myStartElement(int element,
      69              :                                         const SUMOSAXAttributes& attrs) {
      70         2391 :     switch (element) {
      71              :         case SUMO_TAG_CONNECTIONS:
      72              :             // infer location for legacy networks that don't have location information
      73          221 :             myLocation = GeoConvHelper::getLoadedPlain(getFileName(), ".con.xml");
      74          221 :             break;
      75            0 :         case SUMO_TAG_LOCATION:
      76            0 :             delete myLocation;
      77            0 :             myLocation = NIImporter_SUMO::loadLocation(attrs, false);
      78            0 :             break;
      79           69 :         case SUMO_TAG_DEL:
      80           69 :             delConnection(attrs);
      81           69 :             break;
      82         1792 :         case SUMO_TAG_CONNECTION:
      83         1792 :             parseConnection(attrs);
      84         1792 :             break;
      85           14 :         case SUMO_TAG_PROHIBITION:
      86           14 :             addProhibition(attrs);
      87           14 :             break;
      88          285 :         case SUMO_TAG_CROSSING:
      89          285 :             addCrossing(attrs);
      90          285 :             break;
      91            8 :         case SUMO_TAG_WALKINGAREA:
      92            8 :             addWalkingArea(attrs);
      93            8 :             break;
      94            1 :         case SUMO_TAG_PARAM:
      95            1 :             if (myLastParameterised != nullptr) {
      96            1 :                 bool ok = true;
      97            1 :                 const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
      98              :                 // circumventing empty string test
      99            1 :                 const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
     100            1 :                 myLastParameterised->setParameter(key, val);
     101              :             }
     102              :             break;
     103              :         default:
     104              :             break;
     105              :     }
     106         2391 : }
     107              : 
     108              : 
     109              : void
     110         2391 : NIXMLConnectionsHandler::myEndElement(int element) {
     111         2391 :     switch (element) {
     112         2077 :         case SUMO_TAG_CONNECTION:
     113              :         case SUMO_TAG_CROSSING:
     114         2077 :             myLastParameterised = nullptr;
     115         2077 :             break;
     116              :         default:
     117              :             break;
     118              :     }
     119         2391 : }
     120              : 
     121              : 
     122              : 
     123              : NBConnection
     124           28 : NIXMLConnectionsHandler::parseConnectionDef(const std::string& defRole, const std::string& def) {
     125              :     // split from/to
     126              :     const std::string::size_type div = def.find("->");
     127           28 :     if (div == std::string::npos) {
     128            0 :         myErrorMsgHandler->inform("Missing connection divider in " + defRole + " '" + def + "'");
     129            0 :         return NBConnection::InvalidConnection;
     130              :     }
     131           28 :     std::string fromDef = def.substr(0, div);
     132           28 :     std::string toDef = def.substr(div + 2);
     133              : 
     134              :     // retrieve them now
     135           28 :     NBEdge* fromE = myEdgeCont.retrieve(fromDef);
     136           28 :     NBEdge* toE = myEdgeCont.retrieve(toDef);
     137              :     // check
     138           28 :     if (fromE == nullptr) {
     139            0 :         myErrorMsgHandler->inform("Could not find edge '" + fromDef + "' in " + defRole + " '" + def + "'");
     140            0 :         return NBConnection::InvalidConnection;
     141              :     }
     142           28 :     if (toE == nullptr) {
     143            0 :         myErrorMsgHandler->inform("Could not find edge '" + toDef + "' in " + defRole + " '" + def + "'");
     144            0 :         return NBConnection::InvalidConnection;
     145              :     }
     146           28 :     return NBConnection(fromE, toE);
     147              : }
     148              : 
     149              : 
     150              : void
     151         1594 : NIXMLConnectionsHandler::parseLaneBound(const SUMOSAXAttributes& attrs, NBEdge* from, NBEdge* to) {
     152         1594 :     if (to == nullptr) {
     153              :         // do nothing if it's a dead end
     154            0 :         return;
     155              :     }
     156         1594 :     bool ok = true;
     157              :     // get the begin and the end lane
     158              :     int fromLane;
     159              :     int toLane;
     160              :     try {
     161         1594 :         if (!parseLaneInfo(attrs, from, to, &fromLane, &toLane)) {
     162            0 :             return;
     163              :         }
     164         1594 :         if (fromLane < 0) {
     165            0 :             myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_FROM_LANE) +
     166            0 :                                        " in connection from '%' to '%'.", fromLane, from->getID(), to->getID());
     167            0 :             return;
     168              :         }
     169         1594 :         if (toLane < 0) {
     170            0 :             myErrorMsgHandler->informf("Invalid value '%' for " + toString(SUMO_ATTR_TO_LANE) +
     171            0 :                                        " in connection from '%' to '%'.", toLane, from->getID(), to->getID());
     172            0 :             return;
     173              :         }
     174              : 
     175         1594 :         NBEdge::Connection defaultCon(fromLane, to, toLane);
     176         1594 :         if (from->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     177              :             // maybe we are patching an existing connection
     178         1006 :             std::vector<NBEdge::Connection> existing = from->getConnectionsFromLane(fromLane, to, toLane);
     179         1006 :             if (existing.size() > 0) {
     180              :                 assert(existing.size() == 1);
     181           10 :                 defaultCon = existing.front();
     182              :                 // remove the original so we can insert the replacement
     183           10 :                 from->removeFromConnections(defaultCon);
     184              :             } else {
     185          996 :                 from->getToNode()->invalidateTLS(myTLLogicCont, true, false);
     186              :             }
     187         1006 :         }
     188         3188 :         if (OptionsCont::getOptions().isSet("default.connection.cont-pos")) {
     189         1594 :             defaultCon.contPos = OptionsCont::getOptions().getFloat("default.connection.cont-pos");
     190              :         }
     191         1594 :         const bool mayDefinitelyPass = attrs.getOpt<bool>(SUMO_ATTR_PASS, nullptr, ok, defaultCon.mayDefinitelyPass);
     192         1594 :         KeepClear keepClear = defaultCon.keepClear;
     193         1594 :         if (attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)) {
     194           19 :             keepClear = attrs.get<bool>(SUMO_ATTR_KEEP_CLEAR, nullptr, ok) ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE;
     195              :         }
     196         1594 :         const double contPos = attrs.getOpt<double>(SUMO_ATTR_CONTPOS, nullptr, ok, defaultCon.contPos);
     197         1594 :         const double visibility = attrs.getOpt<double>(SUMO_ATTR_VISIBILITY_DISTANCE, nullptr, ok, defaultCon.visibility);
     198         1594 :         const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, defaultCon.speed);
     199         1594 :         const double friction = attrs.getOpt<double>(SUMO_ATTR_FRICTION, nullptr, ok, defaultCon.friction);
     200         1594 :         const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, nullptr, ok, defaultCon.customLength);
     201         1594 :         const bool uncontrolled = attrs.getOpt<bool>(SUMO_ATTR_UNCONTROLLED, nullptr, ok, defaultCon.uncontrolled);
     202         1594 :         const bool indirectLeft = attrs.getOpt<bool>(SUMO_ATTR_INDIRECT, nullptr, ok, false);
     203         3188 :         const std::string edgeType = attrs.getOpt<std::string>(SUMO_ATTR_TYPE, nullptr, ok, "");
     204         1594 :         PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, defaultCon.customShape);
     205         1594 :         std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, nullptr, ok, "");
     206         1594 :         std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, nullptr, ok, "");
     207              :         SVCPermissions permissions;
     208         1594 :         if (allow == "" && disallow == "") {
     209         1591 :             permissions = SVC_UNSPECIFIED;
     210              :         } else {
     211            3 :             permissions = parseVehicleClasses(allow, disallow);
     212              :         }
     213         1594 :         SVCPermissions changeLeft = SVC_UNSPECIFIED;
     214              :         SVCPermissions changeRight = SVC_UNSPECIFIED;
     215         1594 :         if (attrs.hasAttribute(SUMO_ATTR_CHANGE_LEFT)) {
     216            4 :             changeLeft = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_LEFT, nullptr, ok), "");
     217              :         }
     218         1594 :         if (attrs.hasAttribute(SUMO_ATTR_CHANGE_RIGHT)) {
     219            4 :             changeRight = parseVehicleClasses(attrs.get<std::string>(SUMO_ATTR_CHANGE_RIGHT, nullptr, ok), "");
     220              :         }
     221         1594 :         if (attrs.hasAttribute(SUMO_ATTR_SHAPE) && !NBNetBuilder::transformCoordinates(customShape, true, myLocation)) {
     222            0 :             WRITE_ERRORF(TL("Unable to project shape for connection from edge '%' to edge '%'."), from->getID(), to->getID());
     223              :         }
     224         1594 :         if (!ok) {
     225              :             return;
     226              :         }
     227         1594 :         if (!from->addLane2LaneConnection(fromLane, to, toLane, NBEdge::Lane2LaneInfoType::USER, true, mayDefinitelyPass,
     228              :                                           keepClear, contPos, visibility, speed, friction, length, customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight)) {
     229           36 :             if (OptionsCont::getOptions().getBool("show-errors.connections-first-try")) {
     230            6 :                 WRITE_WARNINGF(TL("Could not set loaded connection from lane '%' to lane '%'."), from->getLaneID(fromLane), to->getLaneID(toLane));
     231              :             }
     232              :             // set as to be re-applied after network processing
     233           18 :             myEdgeCont.addPostProcessConnection(from->getID(), fromLane, to->getID(), toLane, mayDefinitelyPass, keepClear, contPos, visibility,
     234              :                                                 speed, friction, length, customShape, uncontrolled, false, permissions, indirectLeft, edgeType, changeLeft, changeRight);
     235              :         }
     236         3188 :     } catch (NumberFormatException&) {
     237            0 :         myErrorMsgHandler->inform("At least one of the defined lanes was not numeric");
     238            0 :     }
     239              : }
     240              : 
     241              : bool
     242         1612 : NIXMLConnectionsHandler::parseLaneInfo(const SUMOSAXAttributes& attributes, NBEdge* fromEdge, NBEdge* toEdge,
     243              :                                        int* fromLane, int* toLane) {
     244         1612 :     if (attributes.hasAttribute(SUMO_ATTR_LANE)) {
     245            0 :         return parseDeprecatedLaneDefinition(attributes, fromEdge, toEdge, fromLane, toLane);
     246              :     } else {
     247         1612 :         return parseLaneDefinition(attributes, fromLane, toLane);
     248              :     }
     249              : }
     250              : 
     251              : 
     252              : inline bool
     253            0 : NIXMLConnectionsHandler::parseDeprecatedLaneDefinition(const SUMOSAXAttributes& attributes,
     254              :         NBEdge* from, NBEdge* to,
     255              :         int* fromLane, int* toLane) {
     256            0 :     bool ok = true;
     257            0 :     if (!myHaveWarnedAboutDeprecatedLanes) {
     258            0 :         myHaveWarnedAboutDeprecatedLanes = true;
     259            0 :         WRITE_WARNING("'" + toString(SUMO_ATTR_LANE) + "' is deprecated, please use '" +
     260              :                       toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) +
     261              :                       "' instead.");
     262              :     }
     263              : 
     264            0 :     std::string laneConn = attributes.get<std::string>(SUMO_ATTR_LANE, nullptr, ok);
     265            0 :     StringTokenizer st(laneConn, ':');
     266            0 :     if (!ok || st.size() != 2) {
     267            0 :         myErrorMsgHandler->inform("Invalid lane to lane connection from '" +
     268            0 :                                   from->getID() + "' to '" + to->getID() + "'.");
     269            0 :         return false; // There was an error.
     270              :     }
     271              : 
     272            0 :     *fromLane = StringUtils::toIntSecure(st.next(), -1);
     273            0 :     *toLane = StringUtils::toIntSecure(st.next(), -1);
     274              : 
     275            0 :     return true; // We succeeded.
     276            0 : }
     277              : 
     278              : 
     279              : inline bool
     280         1612 : NIXMLConnectionsHandler::parseLaneDefinition(const SUMOSAXAttributes& attributes,
     281              :         int* fromLane,
     282              :         int* toLane) {
     283         1612 :     bool ok = true;
     284         1612 :     *fromLane = attributes.get<int>(SUMO_ATTR_FROM_LANE, nullptr, ok);
     285         1612 :     *toLane = attributes.get<int>(SUMO_ATTR_TO_LANE, nullptr, ok);
     286         1612 :     return ok;
     287              : }
     288              : 
     289              : void
     290           69 : NIXMLConnectionsHandler::delConnection(const SUMOSAXAttributes& attrs) {
     291           69 :     bool ok = true;
     292           69 :     std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, nullptr, ok);
     293           69 :     std::string to = attrs.get<std::string>(SUMO_ATTR_TO, nullptr, ok);
     294           69 :     if (!ok) {
     295              :         return;
     296              :     }
     297              :     // these connections were removed when the edge was deleted
     298          176 :     if (myEdgeCont.wasRemoved(from) || myEdgeCont.wasRemoved(to)) {
     299              :         return;
     300              :     }
     301           27 :     NBEdge* fromEdge = myEdgeCont.retrieve(from);
     302           27 :     NBEdge* toEdge = myEdgeCont.retrieve(to);
     303           27 :     if (fromEdge == nullptr) {
     304            0 :         myErrorMsgHandler->informf("The connection-source edge '%' to reset is not known.", from);
     305            0 :         return;
     306              :     }
     307           27 :     if (toEdge == nullptr) {
     308            0 :         myErrorMsgHandler->informf("The connection-destination edge '%' to reset is not known.", to);
     309            0 :         return;
     310              :     }
     311           27 :     if (!fromEdge->isConnectedTo(toEdge) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::EDGE2EDGES) {
     312            8 :         WRITE_WARNINGF(TL("Target edge '%' is not connected with '%'; the connection cannot be reset."), toEdge->getID(), fromEdge->getID());
     313            2 :         return;
     314              :     }
     315           25 :     int fromLane = -1; // Assume all lanes are to be reset.
     316           25 :     int toLane = -1;
     317           25 :     if (attrs.hasAttribute(SUMO_ATTR_LANE)
     318           25 :             || attrs.hasAttribute(SUMO_ATTR_FROM_LANE)
     319           32 :             || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
     320           18 :         if (!parseLaneInfo(attrs, fromEdge, toEdge, &fromLane, &toLane)) {
     321              :             return;
     322              :         }
     323              :         // we could be trying to reset a connection loaded from a sumo net and which has become obsolete.
     324              :         // In this case it's ok to encounter invalid lance indices
     325           18 :         if (!fromEdge->hasConnectionTo(toEdge, toLane) && fromEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2EDGES) {
     326            0 :             WRITE_WARNINGF(TL("Edge '%' has no connection to lane '%'; the connection cannot be reset."), fromEdge->getID(), toEdge->getLaneID(toLane));
     327              :         }
     328              :     }
     329           25 :     fromEdge->removeFromConnections(toEdge, fromLane, toLane, true);
     330              : }
     331              : 
     332              : void
     333         1792 : NIXMLConnectionsHandler::parseConnection(const SUMOSAXAttributes& attrs) {
     334         1792 :     bool ok = true;
     335         1792 :     std::string from = attrs.get<std::string>(SUMO_ATTR_FROM, "connection", ok);
     336         1792 :     std::string to = attrs.getOpt<std::string>(SUMO_ATTR_TO, "connection", ok, "");
     337         8960 :     if (!ok || myEdgeCont.wasIgnored(from) || myEdgeCont.wasIgnored(to)) {
     338              :         return;
     339              :     }
     340              :     // extract edges
     341         1791 :     NBEdge* fromEdge = myEdgeCont.retrieve(from);
     342         1791 :     NBEdge* toEdge = to.length() != 0 ? myEdgeCont.retrieve(to) : nullptr;
     343              :     // check whether they are valid
     344         1791 :     if (fromEdge == nullptr) {
     345            0 :         myErrorMsgHandler->inform("The connection-source edge '" + from + "' is not known.");
     346            0 :         return;
     347              :     }
     348         1791 :     if (toEdge == nullptr && to.length() != 0) {
     349            0 :         myErrorMsgHandler->inform("The connection-destination edge '" + to + "' is not known.");
     350            0 :         return;
     351              :     }
     352              :     // parse optional lane information
     353         1791 :     if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_FROM_LANE) || attrs.hasAttribute(SUMO_ATTR_TO_LANE)) {
     354         1594 :         parseLaneBound(attrs, fromEdge, toEdge);
     355              :     } else {
     356          197 :         fromEdge->addEdge2EdgeConnection(toEdge);
     357          197 :         fromEdge->getToNode()->invalidateTLS(myTLLogicCont, true, false);
     358          197 :         if (attrs.hasAttribute(SUMO_ATTR_PASS)
     359          196 :                 || attrs.hasAttribute(SUMO_ATTR_KEEP_CLEAR)
     360          196 :                 || attrs.hasAttribute(SUMO_ATTR_CONTPOS)
     361          196 :                 || attrs.hasAttribute(SUMO_ATTR_VISIBILITY_DISTANCE)
     362          196 :                 || attrs.hasAttribute(SUMO_ATTR_SPEED)
     363          195 :                 || attrs.hasAttribute(SUMO_ATTR_LENGTH)
     364          195 :                 || attrs.hasAttribute(SUMO_ATTR_UNCONTROLLED)
     365          195 :                 || attrs.hasAttribute(SUMO_ATTR_SHAPE)
     366          195 :                 || attrs.hasAttribute(SUMO_ATTR_ALLOW)
     367          392 :                 || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
     368           10 :             WRITE_ERROR("No additional connection attributes are permitted in connection from edge '" + fromEdge->getID() + "' unless '"
     369              :                         + toString(SUMO_ATTR_FROM_LANE) + "' and '" + toString(SUMO_ATTR_TO_LANE) + "' are set.");
     370              :         }
     371              :     }
     372              : }
     373              : 
     374              : void
     375          285 : NIXMLConnectionsHandler::addCrossing(const SUMOSAXAttributes& attrs) {
     376          285 :     bool ok = true;
     377              :     EdgeVector edges;
     378          285 :     const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
     379          285 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nodeID.c_str(), ok, NBEdge::UNSPECIFIED_WIDTH, true);
     380          285 :     const bool discard = attrs.getOpt<bool>(SUMO_ATTR_DISCARD, nodeID.c_str(), ok, false, true);
     381          285 :     int tlIndex = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX, nullptr, ok, -1);
     382          285 :     int tlIndex2 = attrs.getOpt<int>(SUMO_ATTR_TLLINKINDEX2, nullptr, ok, -1);
     383          285 :     NBNode* node = myNodeCont.retrieve(nodeID);
     384          285 :     if (node == nullptr) {
     385            0 :         if (!discard && myNodeCont.wasRemoved(nodeID)) {
     386            0 :             WRITE_ERRORF(TL("Node '%' in crossing is not known."), nodeID);
     387              :         }
     388            0 :         return;
     389              :     }
     390          285 :     if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     391            1 :         if (discard) {
     392            1 :             node->discardAllCrossings(true);
     393              :             return;
     394              :         } else {
     395            0 :             WRITE_ERRORF(TL("No edges specified for crossing at node '%'."), nodeID);
     396            0 :             return;
     397              :         }
     398              :     }
     399          793 :     for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
     400          509 :         NBEdge* edge = myEdgeCont.retrieve(id);
     401          509 :         if (edge == nullptr) {
     402            3 :             if (!(discard && myEdgeCont.wasRemoved(id))) {
     403            0 :                 WRITE_ERRORF(TL("Edge '%' for crossing at node '%' is not known."), id, nodeID);
     404            0 :                 return;
     405              :             } else {
     406            1 :                 edge = myEdgeCont.retrieve(id, true);
     407              :             }
     408              :         } else {
     409          508 :             if (edge->getToNode() != node && edge->getFromNode() != node) {
     410            0 :                 if (!discard) {
     411            0 :                     WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     412            0 :                     return;
     413              :                 }
     414              :             }
     415              :         }
     416          509 :         edges.push_back(edge);
     417          284 :     }
     418          284 :     if (!ok) {
     419              :         return;
     420              :     }
     421          283 :     bool priority = attrs.getOpt<bool>(SUMO_ATTR_PRIORITY, nodeID.c_str(), ok, node->isTLControlled(), true);
     422          283 :     if (node->isTLControlled() && !priority) {
     423              :         // traffic_light nodes should always have priority crossings
     424            6 :         WRITE_WARNINGF(TL("Crossing at controlled node '%' must be prioritized"), nodeID);
     425              :         priority = true;
     426              :     }
     427          283 :     PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     428          283 :     if (!NBNetBuilder::transformCoordinates(customShape, true, myLocation)) {
     429            0 :         WRITE_ERRORF(TL("Unable to project shape for crossing at node '%'."), node->getID());
     430              :     }
     431          283 :     if (discard) {
     432            2 :         node->removeCrossing(edges);
     433              :     } else {
     434          281 :         if (node->checkCrossingDuplicated(edges)) {
     435              :             // possibly a diff
     436            3 :             NBNode::Crossing* existing =  node->getCrossing(edges);
     437            5 :             if (!(
     438            6 :                         (attrs.hasAttribute(SUMO_ATTR_WIDTH) && width != existing->width)
     439            2 :                         || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX) && tlIndex != existing->customTLIndex)
     440            2 :                         || (attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2) && tlIndex2 != existing->customTLIndex2)
     441            2 :                         || (attrs.hasAttribute(SUMO_ATTR_PRIORITY) && priority != existing->priority))) {
     442            6 :                 WRITE_ERRORF(TL("Crossing with edges '%' already exists at node '%'."), toString(edges), node->getID());
     443              :                 return;
     444              :             } else {
     445              :                 // replace existing, keep old attributes
     446            1 :                 if (!attrs.hasAttribute(SUMO_ATTR_WIDTH)) {
     447            0 :                     width = existing->width;
     448              :                 }
     449            1 :                 if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX)) {
     450            1 :                     tlIndex = existing->customTLIndex;
     451              :                 }
     452            1 :                 if (!attrs.hasAttribute(SUMO_ATTR_TLLINKINDEX2)) {
     453            1 :                     tlIndex2 = existing->customTLIndex2;
     454              :                 }
     455            1 :                 if (!attrs.hasAttribute(SUMO_ATTR_PRIORITY)) {
     456            1 :                     priority = existing->priority;
     457              :                 }
     458            1 :                 node->removeCrossing(edges);
     459              :             }
     460              :         }
     461          279 :         NBNode::Crossing* c = node->addCrossing(edges, width, priority, tlIndex, tlIndex2, customShape);
     462          279 :         myLastParameterised = c;
     463              :     }
     464          568 : }
     465              : 
     466              : 
     467              : void
     468            8 : NIXMLConnectionsHandler::addWalkingArea(const SUMOSAXAttributes& attrs) {
     469            8 :     bool ok = true;
     470              :     NBNode* node = nullptr;
     471              :     EdgeVector edges;
     472            8 :     const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, nullptr, ok);
     473              :     std::vector<std::string> edgeIDs;
     474            8 :     if (!attrs.hasAttribute(SUMO_ATTR_EDGES)) {
     475            0 :         WRITE_ERRORF(TL("No edges specified for walkingArea at node '%'."), nodeID);
     476            0 :         return;
     477              :     }
     478           20 :     for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, nodeID.c_str(), ok)) {
     479           12 :         NBEdge* edge = myEdgeCont.retrieve(id);
     480           12 :         if (edge == nullptr) {
     481            0 :             WRITE_ERRORF(TL("Edge '%' for walkingArea at node '%' is not known."), id, nodeID);
     482            0 :             return;
     483              :         }
     484           12 :         if (node == nullptr) {
     485            8 :             if (edge->getToNode()->getID() == nodeID) {
     486              :                 node = edge->getToNode();
     487            2 :             } else if (edge->getFromNode()->getID() == nodeID) {
     488              :                 node = edge->getFromNode();
     489              :             } else {
     490            0 :                 WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     491            0 :                 return;
     492              :             }
     493              :         } else {
     494            4 :             if (edge->getToNode() != node && edge->getFromNode() != node) {
     495            0 :                 WRITE_ERRORF(TL("Edge '%' does not touch node '%'."), id, nodeID);
     496            0 :                 return;
     497              :             }
     498              :         }
     499           12 :         edges.push_back(edge);
     500            8 :     }
     501            8 :     if (!ok) {
     502              :         return;
     503              :     }
     504            8 :     PositionVector customShape = attrs.getOpt<PositionVector>(SUMO_ATTR_SHAPE, nullptr, ok, PositionVector::EMPTY);
     505            8 :     double customWidth = attrs.getOpt<double>(SUMO_ATTR_WIDTH, nullptr, ok, NBEdge::UNSPECIFIED_WIDTH);
     506            8 :     if (!NBNetBuilder::transformCoordinates(customShape, true, myLocation)) {
     507            0 :         WRITE_ERRORF(TL("Unable to project shape for walkingArea at node '%'."), node->getID());
     508              :     }
     509            8 :     node->addWalkingAreaShape(edges, customShape, customWidth);
     510           16 : }
     511              : 
     512              : void
     513           14 : NIXMLConnectionsHandler::addProhibition(const SUMOSAXAttributes& attrs) {
     514           14 :     bool ok = true;
     515           14 :     std::string prohibitor = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITOR, nullptr, ok, "");
     516           14 :     std::string prohibited = attrs.getOpt<std::string>(SUMO_ATTR_PROHIBITED, nullptr, ok, "");
     517           14 :     if (!ok) {
     518              :         return;
     519              :     }
     520           14 :     NBConnection prohibitorC = parseConnectionDef("prohibitor", prohibitor);
     521           14 :     NBConnection prohibitedC = parseConnectionDef("prohibited", prohibited);
     522           14 :     if (prohibitorC == NBConnection::InvalidConnection || prohibitedC == NBConnection::InvalidConnection) {
     523              :         // something failed
     524              :         return;
     525              :     }
     526           14 :     NBNode* n = prohibitorC.getFrom()->getToNode();
     527           14 :     n->addSortedLinkFoes(prohibitorC, prohibitedC);
     528           14 : }
     529              : 
     530              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1