LCOV - code coverage report
Current view: top level - src/netload - NLTriggerBuilder.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 81.7 % 513 419
Test Date: 2025-12-06 15:35:27 Functions: 97.3 % 37 36

            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    NLTriggerBuilder.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Tino Morenz
      17              : /// @author  Jakob Erdmann
      18              : /// @author  Eric Nicolay
      19              : /// @author  Sascha Krieg
      20              : /// @author  Michael Behrisch
      21              : /// @author  Johannes Rummel
      22              : /// @date    Thu, 17 Oct 2002
      23              : ///
      24              : // Builds trigger objects for microsim
      25              : /****************************************************************************/
      26              : #include <config.h>
      27              : 
      28              : #include <string>
      29              : #include <mesosim/MELoop.h>
      30              : #include <mesosim/METriggeredCalibrator.h>
      31              : #include <microsim/MSEventControl.h>
      32              : #include <microsim/MSJunctionControl.h>
      33              : #include <microsim/MSLane.h>
      34              : #include <microsim/MSEdge.h>
      35              : #include <microsim/MSGlobals.h>
      36              : #include <microsim/MSParkingArea.h>
      37              : #include <microsim/MSStoppingPlace.h>
      38              : #include <microsim/output/MSDetectorControl.h>
      39              : #include <microsim/output/MSRouteProbe.h>
      40              : #include <microsim/trigger/MSLaneSpeedTrigger.h>
      41              : #include <microsim/trigger/MSTriggeredRerouter.h>
      42              : #include <microsim/trigger/MSCalibrator.h>
      43              : #include <microsim/trigger/MSChargingStation.h>
      44              : #include <microsim/trigger/MSOverheadWire.h>
      45              : #include <utils/common/StringTokenizer.h>
      46              : #include <utils/common/FileHelpers.h>
      47              : #include <utils/common/UtilExceptions.h>
      48              : #include <utils/common/WrappingCommand.h>
      49              : #include <utils/common/RGBColor.h>
      50              : #include <utils/options/OptionsCont.h>
      51              : #include <utils/xml/SUMOXMLDefinitions.h>
      52              : #include <utils/xml/XMLSubSys.h>
      53              : #include "NLHandler.h"
      54              : #include "NLTriggerBuilder.h"
      55              : 
      56              : 
      57              : // ===========================================================================
      58              : // method definitions
      59              : // ===========================================================================
      60        39133 : NLTriggerBuilder::NLTriggerBuilder()
      61        39133 :     : myHandler(nullptr), myParkingArea(nullptr), myCurrentStop(nullptr) {}
      62              : 
      63              : 
      64        39133 : NLTriggerBuilder::~NLTriggerBuilder() {}
      65              : 
      66              : void
      67        39133 : NLTriggerBuilder::setHandler(NLHandler* handler) {
      68        39133 :     myHandler = handler;
      69        39133 : }
      70              : 
      71              : 
      72              : void
      73          668 : NLTriggerBuilder::buildVaporizer(const SUMOSAXAttributes& attrs) {
      74          668 :     WRITE_WARNING(TL("Vaporizers are deprecated. Use rerouters instead."));
      75          668 :     bool ok = true;
      76              :     // get the id, throw if not given or empty...
      77          668 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
      78          668 :     if (!ok) {
      79              :         return;
      80              :     }
      81          656 :     MSEdge* e = MSEdge::dictionary(id);
      82          656 :     if (e == nullptr) {
      83           18 :         WRITE_ERRORF(TL("Unknown edge ('%') referenced in a vaporizer."), id);
      84            6 :         return;
      85              :     }
      86          650 :     SUMOTime begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok);
      87          650 :     SUMOTime end = attrs.getSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok);
      88          650 :     if (!ok) {
      89              :         return;
      90              :     }
      91          614 :     if (begin < 0) {
      92           18 :         WRITE_ERRORF(TL("A vaporization begin time is negative (edge id='%')."), id);
      93            6 :         return;
      94              :     }
      95          608 :     if (begin >= end) {
      96           36 :         WRITE_ERRORF(TL("A vaporization ends before it starts (edge id='%')."), id);
      97           12 :         return;
      98              :     }
      99         1192 :     if (end >= string2time(OptionsCont::getOptions().getString("begin"))) {
     100          596 :         Command* cb = new WrappingCommand< MSEdge >(e, &MSEdge::incVaporization);
     101          596 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cb, begin);
     102          596 :         Command* ce = new WrappingCommand< MSEdge >(e, &MSEdge::decVaporization);
     103          596 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(ce, end);
     104              :     }
     105              : }
     106              : 
     107              : 
     108              : void
     109          568 : NLTriggerBuilder::parseAndBuildLaneSpeedTrigger(MSNet& net, const SUMOSAXAttributes& attrs,
     110              :         const std::string& base) {
     111              :     // get the id, throw if not given or empty...
     112          568 :     bool ok = true;
     113              :     // get the id, throw if not given or empty...
     114          568 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     115          568 :     if (!ok) {
     116              :         return;
     117              :     }
     118              :     // get the file name to read further definitions from
     119          568 :     std::string file = getFileName(attrs, base, true);
     120          568 :     std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANES, id.c_str(), ok);
     121              :     std::vector<MSLane*> lanes;
     122         1189 :     for (const std::string& laneID : attrs.get<std::vector<std::string> >(SUMO_ATTR_LANES, id.c_str(), ok)) {
     123          621 :         MSLane* lane = MSLane::dictionary(laneID);
     124          621 :         if (lane == nullptr) {
     125            0 :             throw InvalidArgument("The lane '" + laneID + "' to use within MSLaneSpeedTrigger '" + id + "' is not known.");
     126              :         }
     127          621 :         lanes.push_back(lane);
     128          568 :     }
     129          568 :     if (!ok) {
     130            0 :         throw InvalidArgument("The lanes to use within MSLaneSpeedTrigger '" + id + "' are not known.");
     131              :     }
     132          568 :     if (lanes.size() == 0) {
     133            0 :         throw InvalidArgument("No lane defined for MSLaneSpeedTrigger '" + id + "'.");
     134              :     }
     135              :     try {
     136          568 :         MSLaneSpeedTrigger* trigger = buildLaneSpeedTrigger(net, id, lanes, file);
     137          568 :         if (file == "") {
     138          400 :             trigger->registerParent(SUMO_TAG_VSS, myHandler);
     139              :         }
     140            0 :     } catch (ProcessError& e) {
     141            0 :         throw InvalidArgument(e.what());
     142            0 :     }
     143          568 : }
     144              : 
     145              : 
     146              : void
     147        12232 : NLTriggerBuilder::parseAndBuildChargingStation(MSNet& net, const SUMOSAXAttributes& attrs) {
     148        12232 :     bool ok = true;
     149              : 
     150              :     // get the id, throw if not given or empty...
     151        12232 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     152        12232 :     if (!ok) {
     153            0 :         throw ProcessError();
     154              :     }
     155              : 
     156        24464 :     MSLane* const lane = getLane(attrs, "chargingStation", id);
     157        12232 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
     158        12232 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     159        12232 :     const double chargingPower = attrs.getOpt<double>(SUMO_ATTR_CHARGINGPOWER, id.c_str(), ok, 22000);
     160        12232 :     const double totalPower = attrs.getOpt<double>(SUMO_ATTR_TOTALPOWER, id.c_str(), ok, -1);
     161        12232 :     const double efficiency = attrs.getOpt<double>(SUMO_ATTR_EFFICIENCY, id.c_str(), ok, 0.95);
     162        12232 :     const bool chargeInTransit = attrs.getOpt<bool>(SUMO_ATTR_CHARGEINTRANSIT, id.c_str(), ok, 0);
     163        12232 :     const SUMOTime chargeDelay = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CHARGEDELAY, id.c_str(), ok, 0);
     164        24465 :     const std::string chargeType = attrs.getOpt<std::string>(SUMO_ATTR_CHARGETYPE, id.c_str(), ok, "normal");
     165        12232 :     const SUMOTime waitingTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, id.c_str(), ok, 900);
     166        12232 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     167        12233 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     168        24464 :     MSParkingArea* parkingArea = getParkingArea(attrs, "parkingArea", id);
     169              : 
     170              :     // check charge type
     171        12232 :     if ((chargeType != "normal") && (chargeType != "battery-exchange") && (chargeType != "fuel")) {
     172            0 :         throw InvalidArgument("The chargeType to use within MSLaneSpeedTrigger '" + id + "' is invalid.");
     173              :     }
     174              : 
     175        12232 :     if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     176            3 :         throw InvalidArgument("Invalid position for charging station '" + id + "'.");
     177              :     }
     178              : 
     179        36694 :     buildChargingStation(net, id, lane, frompos, topos, name, chargingPower, totalPower, efficiency, chargeInTransit, chargeDelay, chargeType, waitingTime, parkingArea);
     180        12231 : }
     181              : 
     182              : 
     183              : void
     184           58 : NLTriggerBuilder::parseAndBuildOverheadWireSegment(MSNet& net, const SUMOSAXAttributes& attrs) {
     185           58 :     bool ok = true;
     186              : 
     187              :     // get the id, throw if not given or empty...
     188           58 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
     189           58 :     if (!ok) {
     190            0 :         throw ProcessError();
     191              :     }
     192              : 
     193              :     /* The following call may either throw InvalidArgument exeption or return NULL:
     194              :         NULL is returned in case when the overhead wire segment should be built over an already
     195              :         ignored internal lane of an intersection, the exeption is thrown in case that
     196              :         the overhead wire segment references a non-existent lane. */
     197           58 :     MSLane* const lane = getLane(attrs, "overheadWireSegment", id);
     198           58 :     if (lane == nullptr) {
     199            0 :         WRITE_MESSAGEF(TL("The overheadWireSegment '%' was not created as it is attached to internal lane. It will be build automatically."), id);
     200            0 :         return;
     201              :     }
     202              : 
     203           58 :     if (lane->isInternal()) {
     204            0 :         WRITE_MESSAGEF(TL("The overheadWireSegment '%' not built as it is attached to internal lane. It will be build automatically."), id);
     205            0 :         return;
     206              :     }
     207              : 
     208           58 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
     209           58 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     210           58 :     const bool voltageSource = attrs.getOpt<bool>(SUMO_ATTR_VOLTAGESOURCE, id.c_str(), ok, false);
     211           58 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     212              : 
     213           58 :     if (!ok || myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID) {
     214            0 :         frompos = 0;
     215            0 :         topos = lane->getLength();
     216            0 :         WRITE_MESSAGEF(TL("The overheadWireSegment '%' has wrong position. Automatically set from 0 to the length of the lane."), id);
     217              :         //throw InvalidArgument("Invalid position for overheadWireSegment'" + id + "'.");
     218              :     }
     219              : 
     220           58 :     buildOverheadWireSegment(net, id, lane, frompos, topos, voltageSource);
     221              : #ifndef HAVE_EIGEN
     222              :     if (MSGlobals::gOverheadWireSolver && !myHaveWarnedAboutEigen) {
     223              :         myHaveWarnedAboutEigen = true;
     224              :         WRITE_WARNING(TL("Overhead wire solver (Eigen) not compiled in, expect errors in overhead wire simulation"))
     225              :     }
     226              : #endif // !HAVE_EIGEN
     227              : }
     228              : 
     229              : void
     230            8 : NLTriggerBuilder::parseAndBuildOverheadWireSection(MSNet& net, const SUMOSAXAttributes& attrs) {
     231            8 :     bool ok = true;
     232            8 :     std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
     233            8 :     if (!ok) {
     234            0 :         throw ProcessError();
     235              :     }
     236              : 
     237            8 :     MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
     238            8 :     if (substation == nullptr) {
     239            0 :         throw InvalidArgument("Traction substation '" + substationId + "' refereced by an OverheadWire Section is not known.");
     240            8 :     } else if (substation->isAnySectionPreviouslyDefined()) {
     241            0 :         throw InvalidArgument("Traction substation '" + substationId + "' refereced by an OverheadWire Section is probably referenced twice (a known limitation of the actual version of overhead wire simulation).");
     242              :     }
     243              : 
     244              :     // @todo This may be a relict of older approach to processing the attributes ...
     245            8 :     std::string segmentStrings = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENTS, substationId.c_str(), ok);
     246            8 :     if (!ok) {
     247            0 :         throw InvalidArgument("Segments referenced by Traction substation '" + substationId + "' are not declared .");
     248              :     }
     249              : 
     250              :     // process forbidden internal lanes
     251            8 :     const std::vector<std::string>& forbiddenInnerLanesIDs = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_FORBIDDEN, substationId.c_str(), ok);
     252              :     /// @todo for cycle abbreviation?
     253            8 :     for (const std::string& laneID : forbiddenInnerLanesIDs) {
     254            0 :         MSLane* lane = MSLane::dictionary(laneID);
     255            0 :         if (lane != nullptr) {
     256            0 :             substation->addForbiddenLane(lane);
     257              :         } else {
     258            0 :             throw InvalidArgument("Unknown lane '" + laneID + "' in Traction substation '" + substationId + "'.");
     259              :         }
     260              :     }
     261              : 
     262              :     // @todo Check this as well ...
     263              :     // Original version from 2018
     264              :     // std::vector<std::string> segmentIDs;
     265              :     // SUMOSAXAttributes::parseStringVector(segmentStrings, segmentIDs);
     266            8 :     const std::vector<std::string>& segmentIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_SEGMENTS, substationId.c_str(), ok);
     267              :     std::vector<MSOverheadWire*> segments;
     268              : 
     269              :     // ----------------------------------------------
     270              :     // Add overhead wire segments over internal lanes
     271              :     // ----------------------------------------------
     272              : 
     273              :     // Adding internal overhead wire segments (segments on neighboring inner lanes if a connection between two regular lane with overhead wire segment exists)
     274           46 :     for (const std::string& segmentID : segmentIDs) {
     275              :         const MSLane* connection = nullptr;
     276           38 :         MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     277              :         std::string neigboringOvrhdSegmentID;
     278              :         MSOverheadWire* neigboringOvrhdSegment;
     279              :         MSTractionSubstation* neigboringOvrhdSegmentTractionSubstation;
     280           38 :         if (ovrhdSegment == nullptr) {
     281            0 :             throw InvalidArgument("The OverheadWireSegment with id='" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' was not defined.");
     282              :         }
     283              : 
     284              :         MSTractionSubstation* ts = ovrhdSegment->getTractionSubstation();
     285           38 :         if (!(ts == substation || ts == nullptr)) {
     286              :             std::string tsName = ts->getID();
     287            0 :             throw InvalidArgument("The OverheadWireSegment '" + segmentID + "' referenced by OverheadWireSegment for substation '" + substationId + "' is already assigned to substation '" + tsName + "'.");
     288              :         }
     289              :         ovrhdSegment->setTractionSubstation(substation);
     290              : 
     291           38 :         const MSLane* lane = &(ovrhdSegment->getLane());
     292              : 
     293              :         /* in version before SUMO 1.0.1 the function getOutgoingLanes() returning MSLane* exists,
     294              :         in new version of SUMO the funciton getOutgoingViaLanes() returning MSLane* and MSEdge* pair exists */
     295           38 :         const std::vector<std::pair<const MSLane*, const MSEdge*> > outgoingLanesAndEdges = lane->getOutgoingViaLanes();
     296              :         std::vector<const MSLane*> neigboringInnerLanes;
     297           38 :         neigboringInnerLanes.reserve(outgoingLanesAndEdges.size());
     298           89 :         for (size_t it = 0; it < outgoingLanesAndEdges.size(); ++it) {
     299           51 :             neigboringInnerLanes.push_back(outgoingLanesAndEdges[it].first);
     300              :         }
     301              : 
     302              :         // Check if an outgoing lane has an overhead wire segment. If not, do nothing, otherwise find connnecting internal lanes and
     303              :         // add overhead wire segments over all detected internal lanes
     304           89 :         for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
     305              :             // If the overhead wire segment is over the outgoing (not internal) lane
     306          102 :             neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     307           51 :             if (neigboringOvrhdSegmentID != "") {
     308           25 :                 neigboringOvrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(neigboringOvrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     309              :                 neigboringOvrhdSegmentTractionSubstation = neigboringOvrhdSegment->getTractionSubstation();
     310              :             } else {
     311              :                 neigboringOvrhdSegment = nullptr;
     312              :                 neigboringOvrhdSegmentTractionSubstation = nullptr;
     313              :             }
     314              : 
     315           51 :             if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
     316            2 :                 connection = lane->getInternalFollowingLane(*it);
     317            2 :                 if (connection != nullptr) {
     318              :                     //is connection forbidden?
     319            2 :                     if (!(substation->isForbidden(connection) || substation->isForbidden(lane->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(*it)))) {
     320            2 :                         buildInnerOverheadWireSegments(net, connection, lane->getInternalFollowingLane(connection), connection->getInternalFollowingLane(*it));
     321              :                     }
     322              :                 }
     323              :             }
     324              :         }
     325              : 
     326              :         // Check if an incoming lane has an overhead wire segment. If not, do nothing, otherwise find connnecting internal lanes and
     327              :         // add overhead wire segments over all detected internal lanes
     328           38 :         neigboringInnerLanes = lane->getNormalIncomingLanes();
     329          111 :         for (std::vector<const MSLane*>::iterator it = neigboringInnerLanes.begin(); it != neigboringInnerLanes.end(); ++it) {
     330              :             // If the overhead wire segment is over the incoming (not internal) lane
     331          146 :             neigboringOvrhdSegmentID = MSNet::getInstance()->getStoppingPlaceID(*it, (*it)->getLength() - NUMERICAL_EPS, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
     332           73 :             if (neigboringOvrhdSegmentID != "") {
     333           19 :                 neigboringOvrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(neigboringOvrhdSegmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     334              :                 neigboringOvrhdSegmentTractionSubstation = neigboringOvrhdSegment->getTractionSubstation();
     335              :             } else {
     336              :                 neigboringOvrhdSegment = nullptr;
     337              :                 neigboringOvrhdSegmentTractionSubstation = nullptr;
     338              :             }
     339              : 
     340           73 :             if (neigboringOvrhdSegmentTractionSubstation == substation && !(*it)->isInternal()) {
     341           17 :                 connection = (*it)->getInternalFollowingLane(lane);
     342           17 :                 if (connection != nullptr) {
     343              :                     //is connection forbidden?
     344            2 :                     if (!(substation->isForbidden(connection) || substation->isForbidden((*it)->getInternalFollowingLane(connection)) || substation->isForbidden(connection->getInternalFollowingLane(lane)))) {
     345            2 :                         buildInnerOverheadWireSegments(net, connection, (*it)->getInternalFollowingLane(connection), connection->getInternalFollowingLane(lane));
     346              :                     }
     347              :                 }
     348              :             }
     349              :         }
     350           38 :     }
     351              : 
     352              : 
     353              :     // ----- *** adding segments into the electric circuit*** -----
     354              : 
     355              :     // setting nullptr for substation (a fragment from old version of adding segments into the circuit)
     356           46 :     for (const std::string& segmentID : segmentIDs) {
     357           38 :         MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     358              :         ovrhdSegment->setTractionSubstation(nullptr);
     359              :     }
     360              : 
     361           46 :     for (const std::string& segmentID : segmentIDs) {
     362           38 :         if (segmentID == "") {
     363            0 :             continue;
     364              :         }
     365           38 :         MSOverheadWire* ovrhdSegment = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(segmentID, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     366           38 :         substation->addOverheadWireSegmentToCircuit(ovrhdSegment);
     367           38 :         segments.push_back(ovrhdSegment);
     368              :     }
     369              : 
     370              :     // adding overhead wire clamp
     371           16 :     std::string clampsString = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok, "");
     372            8 :     if (clampsString != "" && MSGlobals::gOverheadWireSolver) {
     373              : #ifdef HAVE_EIGEN
     374            5 :         const std::vector<std::string>& clampIDs = attrs.get<std::vector<std::string> >(SUMO_ATTR_OVERHEAD_WIRE_CLAMPS, nullptr, ok);
     375           16 :         for (const std::string& clampID : clampIDs) {
     376           11 :             MSTractionSubstation::OverheadWireClamp* clamp = substation->findClamp(clampID);
     377           11 :             if (clamp != nullptr) {
     378           11 :                 if (clamp->start->getTractionSubstation() == substation && clamp->end->getTractionSubstation() == substation) {
     379           11 :                     substation->addOverheadWireClampToCircuit(clamp->id, clamp->start, clamp->end);
     380           11 :                     buildOverheadWireClamp(net, clamp->id, const_cast<MSLane*>(&clamp->start->getLane()), const_cast<MSLane*>(&clamp->end->getLane()));
     381           11 :                     clamp->usage = true;
     382              :                 } else {
     383            0 :                     if (clamp->start->getTractionSubstation() != substation) {
     384            0 :                         WRITE_WARNINGF(TL("A connecting overhead wire start segment '%' defined for overhead wire clamp '%' is not assigned to the traction substation '%'."), clamp->start->getID(), clampID, substationId);
     385              :                     } else {
     386            0 :                         WRITE_WARNINGF(TL("A connecting overhead wire end segment '%' defined for overhead wire clamp '%' is not assigned to the traction substation '%'."), clamp->end->getID(), clampID, substationId);
     387              :                     }
     388              :                 }
     389              :             } else {
     390            0 :                 WRITE_WARNINGF(TL("The overhead wire clamp '%' defined in an overhead wire section was not assigned to the substation '%'. Please define proper <overheadWireClamp .../> in additional files before defining overhead wire section."), clampID, substationId);
     391              :             }
     392              :         }
     393              : #else
     394              :         WRITE_WARNING(TL("Overhead circuit solver requested, but solver support (Eigen) not compiled in."));
     395              : #endif
     396            5 :     }
     397              : 
     398            8 :     if (segments.size() == 0) {
     399            0 :         throw InvalidArgument("No segments found for overHeadWireSection '" + substationId + "'.");
     400            8 :     } else if (MSGlobals::gOverheadWireSolver) {
     401              : #ifdef HAVE_EIGEN
     402              :         // check that the electric circuit makes sense
     403           24 :         segments[0]->getCircuit()->checkCircuit(substationId);
     404              : #else
     405              :         WRITE_WARNING(TL("Cannot check circuit, overhead circuit solver support (Eigen) not compiled in."));
     406              : #endif
     407              :     }
     408           16 : }
     409              : 
     410              : void
     411           17 : NLTriggerBuilder::parseAndBuildTractionSubstation(MSNet& net, const SUMOSAXAttributes& attrs) {
     412           17 :     bool ok = true;
     413              : 
     414              :     // get the id, throw if not given or empty...
     415           17 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
     416           17 :     if (!ok) {
     417            0 :         throw ProcessError();
     418              :     }
     419              : 
     420              :     // RICE_TODO Limits are fixed, change them to some predefined constants ...
     421           17 :     const double voltage = attrs.getOpt<double>(SUMO_ATTR_VOLTAGE, id.c_str(), ok, 600);
     422           17 :     const double currentLimit = attrs.getOpt<double>(SUMO_ATTR_CURRENTLIMIT, id.c_str(), ok, 400);
     423           34 :     buildTractionSubstation(net, id, voltage, currentLimit);
     424           17 : }
     425              : 
     426              : void
     427           11 : NLTriggerBuilder::parseAndBuildOverheadWireClamp(MSNet& /*net*/, const SUMOSAXAttributes& attrs) {
     428           11 :     if (MSGlobals::gOverheadWireSolver) {
     429              : #ifdef HAVE_EIGEN
     430           11 :         bool ok = true;
     431           11 :         std::string id = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
     432           11 :         if (!ok) {
     433            0 :             throw ProcessError();
     434              :         }
     435              : 
     436           11 :         std::string substationId = attrs.get<std::string>(SUMO_ATTR_SUBSTATIONID, 0, ok);
     437           11 :         if (!ok) {
     438            0 :             throw ProcessError();
     439              :         }
     440           11 :         MSTractionSubstation* substation = MSNet::getInstance()->findTractionSubstation(substationId);
     441           11 :         if (substation == nullptr) {
     442            0 :             throw InvalidArgument("Traction substation '" + substationId + "' using within an overheadWireClamp '" + id + "' is not known.");
     443              :         }
     444              : 
     445           11 :         std::string overhead_fromItsStart = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_START, 0, ok);
     446           11 :         if (!ok) {
     447            0 :             throw ProcessError();
     448              :         }
     449           11 :         MSOverheadWire* ovrhdSegment_fromItsStart = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsStart, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     450           11 :         if (ovrhdSegment_fromItsStart == nullptr) {
     451            0 :             throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsStart + "' to use within overheadWireClamp '" + id + "' is not known.");
     452              :         }
     453              :         /*if (ovrhdSegment_fromItsStart->getTractionSubstation() != substation) {
     454              :             throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsStart + "' to use within overheadWireClamp is assign to a different overhead wire section or substation.");
     455              :         }
     456              :         */
     457           11 :         std::string overhead_fromItsEnd = attrs.get<std::string>(SUMO_ATTR_OVERHEAD_WIRE_CLAMP_END, 0, ok);
     458           11 :         if (!ok) {
     459            0 :             throw ProcessError();
     460              :         }
     461           11 :         MSOverheadWire* ovrhdSegment_fromItsEnd = dynamic_cast<MSOverheadWire*>(MSNet::getInstance()->getStoppingPlace(overhead_fromItsEnd, SUMO_TAG_OVERHEAD_WIRE_SEGMENT));
     462           11 :         if (ovrhdSegment_fromItsEnd == nullptr) {
     463            0 :             throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsEnd + "' to use within overheadWireClamp '" + id + "' is not known.");
     464              :         }
     465              :         /*
     466              :         if (ovrhdSegment_fromItsEnd->getTractionSubstation() != substation) {
     467              :             throw InvalidArgument("The overheadWireSegment '" + overhead_fromItsEnd + "' to use within overheadWireClamp is assign to a different overhead wire section or substation.");
     468              :         }
     469              :         */
     470           22 :         if (substation->findClamp(id) == nullptr) {
     471           11 :             substation->addClamp(id, ovrhdSegment_fromItsStart, ovrhdSegment_fromItsEnd);
     472              :         } else {
     473            0 :             WRITE_ERROR("The overhead wire clamp '" + id + "' is probably declared twice.")
     474              :         }
     475              : #else
     476              :         UNUSED_PARAMETER(attrs);
     477              :         WRITE_WARNING(TL("Not building overhead wire clamps, overhead wire solver support (Eigen) not compiled in."));
     478              : #endif
     479              :     } else {
     480            0 :         WRITE_WARNING(TL("Ignoring overhead wire clamps, they make no sense when overhead wire circuit solver is off."));
     481              :     }
     482           11 : }
     483              : 
     484              : 
     485              : void
     486        41525 : NLTriggerBuilder::parseAndBuildStoppingPlace(MSNet& net, const SUMOSAXAttributes& attrs, const SumoXMLTag element) {
     487        41525 :     bool ok = true;
     488              :     // get the id, throw if not given or empty...
     489        41525 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     490        41525 :     if (!ok) {
     491            0 :         throw ProcessError();
     492              :     }
     493              : 
     494              :     //get the name, leave blank if not given
     495        83061 :     const std::string ptStopName = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     496              : 
     497              :     //get the color, use default if not given
     498              :     // default color, copy from GUIVisualizationStoppingPlaceSettings::busStopColor / containerStopColor
     499        41525 :     RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor::INVISIBLE);
     500              : 
     501        83050 :     MSLane* lane = getLane(attrs, toString(element), id);
     502              :     // get the positions
     503        41525 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0.);
     504        41525 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     505        41525 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 90);
     506        41525 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     507        41525 :     if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     508            9 :         throw InvalidArgument("Invalid position for " + toString(element) + " '" + id + "'.");
     509              :     }
     510        41533 :     const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
     511              :     int defaultCapacity;
     512              :     SumoXMLAttr capacityAttr;
     513        41522 :     if (element != SUMO_TAG_CONTAINER_STOP) {
     514        30985 :         defaultCapacity = MAX2(MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element) * 3, 6);
     515              :         capacityAttr = SUMO_ATTR_PERSON_CAPACITY;
     516              :     } else {
     517        10537 :         defaultCapacity = MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element);
     518              :         capacityAttr = SUMO_ATTR_CONTAINER_CAPACITY;
     519              :     }
     520        41522 :     const int transportableCapacity = attrs.getOpt<int>(capacityAttr, id.c_str(), ok, defaultCapacity);
     521        41522 :     const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
     522              :     // build the bus stop
     523       166096 :     buildStoppingPlace(net, id, lines, lane, frompos, topos, element, ptStopName, transportableCapacity, parkingLength, color, angle);
     524        83036 : }
     525              : 
     526              : 
     527              : void
     528         2318 : NLTriggerBuilder::addAccess(MSNet& /* net */, const SUMOSAXAttributes& attrs) {
     529         2318 :     if (myCurrentStop == nullptr) {
     530           30 :         throw InvalidArgument("Could not add access outside a stopping place.");
     531              :     }
     532              :     // get the lane
     533         4606 :     MSLane* lane = getLane(attrs, "access", myCurrentStop->getID());
     534         2303 :     if (!lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
     535            3 :         WRITE_WARNINGF(TL("Ignoring invalid access from non-pedestrian lane '%' in busStop '%'."), lane->getID(), myCurrentStop->getID());
     536            1 :         return;
     537              :     }
     538              :     // get the positions
     539         2302 :     bool ok = true;
     540         2302 :     const std::string accessPos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, "access", ok);
     541         2302 :     const bool random = accessPos == "random";
     542              :     MSStoppingPlace::AccessExit exit = MSStoppingPlace::AccessExit::PLATFORM;
     543         2302 :     if (accessPos == "doors") {
     544              :         exit = MSStoppingPlace::AccessExit::DOORS;
     545         2270 :     } else if (accessPos == "carriage") {
     546              :         exit = MSStoppingPlace::AccessExit::CARRIAGE;
     547              :     }
     548         2302 :     double startPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? 0. : attrs.getOpt<double>(SUMO_ATTR_POSITION, "access", ok, 0);
     549         2302 :     double endPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? lane->getLength() : startPos;
     550         2302 :     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
     551         2302 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
     552         2302 :     if (!ok || (myHandler->checkStopPos(startPos, endPos, lane->getLength(), 0, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     553            0 :         throw InvalidArgument("Invalid position " + attrs.getString(SUMO_ATTR_POSITION) + " for access on lane '" + lane->getID() + "' in stop '" + myCurrentStop->getID() + "'.");
     554              :     }
     555              :     // add bus stop access
     556         2302 :     if (!myCurrentStop->addAccess(lane, startPos, endPos, length, exit)) {
     557           21 :         throw InvalidArgument("Duplicate access on lane '" + lane->getID() + "' for stop '" + myCurrentStop->getID() + "'");
     558              :     }
     559              : }
     560              : 
     561              : 
     562              : void
     563        14363 : NLTriggerBuilder::parseAndBeginParkingArea(MSNet& net, const SUMOSAXAttributes& attrs) {
     564        14363 :     bool ok = true;
     565              :     // get the id, throw if not given or empty...
     566        14363 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     567        14363 :     if (!ok) {
     568            0 :         throw ProcessError();
     569              :     }
     570              :     // get the lane
     571        28726 :     MSLane* lane = getLane(attrs, "parkingArea", id);
     572              :     // get the positions
     573        14363 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
     574        14363 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     575        14363 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     576        14363 :     unsigned int capacity = attrs.getOpt<int>(SUMO_ATTR_ROADSIDE_CAPACITY, id.c_str(), ok, 0);
     577        14363 :     myParkingAreaCapacitySet = attrs.hasAttribute(SUMO_ATTR_ROADSIDE_CAPACITY);
     578        14363 :     bool onRoad = attrs.getOpt<bool>(SUMO_ATTR_ONROAD, id.c_str(), ok, false);
     579        14363 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, 0);
     580        14363 :     double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
     581        14363 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 0);
     582        28727 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
     583        28727 :     const std::string departPos = attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS, id.c_str(), ok);
     584        14363 :     bool lefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, id.c_str(), ok, false);
     585        14364 :     const std::vector<std::string>& acceptedBadges = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_ACCEPTED_BADGES, id.c_str(), ok);
     586              : 
     587        14363 :     if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     588            3 :         throw InvalidArgument("Invalid position for parking area '" + id + "'.");
     589              :     }
     590        14362 :     const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
     591              :     // build the parking area
     592        14362 :     beginParkingArea(net, id, lines, acceptedBadges, lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand);
     593        28725 : }
     594              : 
     595              : 
     596              : 
     597              : void
     598          805 : NLTriggerBuilder::parseAndAddLotEntry(const SUMOSAXAttributes& attrs) {
     599          805 :     bool ok = true;
     600              :     // Check for open parking area
     601          805 :     if (myParkingArea == nullptr) {
     602            0 :         throw ProcessError();
     603              :     }
     604              :     // get the positions
     605          805 :     double x = attrs.get<double>(SUMO_ATTR_X, "", ok);
     606          805 :     if (!ok) {
     607            0 :         throw InvalidArgument("Invalid x position for lot entry.");
     608              :     }
     609          805 :     double y = attrs.get<double>(SUMO_ATTR_Y, "", ok);
     610          805 :     if (!ok) {
     611            0 :         throw InvalidArgument("Invalid y position for lot entry.");
     612              :     }
     613          805 :     double z = attrs.getOpt<double>(SUMO_ATTR_Z, "", ok, 0.);
     614          805 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, "", ok, myParkingArea->getWidth());
     615          805 :     double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "", ok, myParkingArea->getLength());
     616          805 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, "", ok, myParkingArea->getAngle());
     617          805 :     double slope = attrs.getOpt<double>(SUMO_ATTR_SLOPE, "", ok, 0.);
     618              :     // add the lot entry
     619          805 :     addLotEntry(x, y, z, width, length, angle, slope);
     620          805 : }
     621              : 
     622              : 
     623              : void
     624         1138 : NLTriggerBuilder::parseAndBuildCalibrator(MSNet& net, const SUMOSAXAttributes& attrs,
     625              :         const std::string& base) {
     626         1138 :     bool ok = true;
     627              :     // get the id, throw if not given or empty...
     628         1138 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     629         1138 :     if (!ok) {
     630            0 :         throw ProcessError();
     631              :     }
     632              :     MSLane* lane = nullptr;
     633              :     MSEdge* edge = nullptr;
     634              :     MSJunction* node = nullptr;
     635         1138 :     if (attrs.hasAttribute(SUMO_ATTR_NODE)) {
     636           14 :         if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_EDGE)) {
     637            0 :             throw InvalidArgument("The node calibrator '" + id + "' cannot define an edge or lane as well.");
     638              :         }
     639           14 :         const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, id.c_str(), ok);
     640              :         node = net.getJunctionControl().get(nodeID);
     641           14 :         if (node == nullptr) {
     642            0 :             throw InvalidArgument("The node " + nodeID + " to use within the calibrator '" + id + "' is not known.");
     643              :         }
     644              :     } else {
     645         1124 :         if (attrs.hasAttribute(SUMO_ATTR_EDGE)) {
     646          599 :             const std::string edgeID = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
     647          599 :             edge = MSEdge::dictionary(edgeID);
     648          599 :             if (edge == nullptr) {
     649            0 :                 throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id + "' is not known.");
     650              :             }
     651          599 :             if (attrs.hasAttribute(SUMO_ATTR_LANE)) {
     652            0 :                 lane = getLane(attrs, "calibrator", id);
     653            0 :                 if (&lane->getEdge() != edge) {
     654            0 :                     throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id
     655            0 :                                           + "' does not match the calibrator lane '" + lane->getID() + ".");
     656              :                 }
     657              :             }
     658              :         } else {
     659         1050 :             lane = getLane(attrs, "calibrator", id);
     660              :             edge = &lane->getEdge();
     661              :         }
     662              :     }
     663         1138 :     const double pos = node != nullptr ? 0 : getPosition(attrs, lane, "calibrator", id, edge);
     664         1137 :     SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, DELTA_T); // !!! no error handling
     665         1137 :     if (period > TIME2STEPS(1)) {
     666          288 :         WRITE_WARNINGF("Reducing period to 1 for calibrator '%'", id);
     667              :         period = TIME2STEPS(1);
     668              :     }
     669         1138 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
     670         1137 :     const std::string file = getFileName(attrs, base, true);
     671         1137 :     const std::string outfile = attrs.getOpt<std::string>(SUMO_ATTR_OUTPUT, id.c_str(), ok, "");
     672         1137 :     const std::string routeProbe = attrs.getOpt<std::string>(SUMO_ATTR_ROUTEPROBE, id.c_str(), ok, "");
     673              :     // differing defaults for backward compatibility, values are dimensionless
     674         2186 :     const double invalidJamThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, MSGlobals::gUseMesoSim ? 0.8 : 0.5);
     675         1137 :     const bool local = attrs.getOpt<bool>(SUMO_ATTR_LOCAL, id.c_str(), ok, false);
     676              :     MSRouteProbe* probe = nullptr;
     677         1137 :     if (routeProbe != "") {
     678           64 :         probe = dynamic_cast<MSRouteProbe*>(net.getDetectorControl().getTypedDetectors(SUMO_TAG_ROUTEPROBE).get(routeProbe));
     679           32 :         if (probe == nullptr) {
     680            0 :             throw InvalidArgument("The routeProbe '" + routeProbe + "' to use within the calibrator '" + id + "' is not known.");
     681              :         }
     682              :     }
     683         1137 :     if (MSGlobals::gUseMesoSim) {
     684           88 :         if (lane != nullptr && edge->getLanes().size() > 1) {
     685           27 :             WRITE_WARNING("Meso calibrator '" + id
     686              :                           + "' defined for lane '" + lane->getID()
     687              :                           + "' will collect data for all lanes of edge '" + edge->getID() + "'.");
     688              :         }
     689           88 :         METriggeredCalibrator* trigger = buildMECalibrator(id, edge, pos, file, outfile, period, probe, invalidJamThreshold, vTypes);
     690           88 :         if (file == "") {
     691           74 :             trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
     692              :         }
     693              :     } else {
     694         1049 :         MSCalibrator* trigger = buildCalibrator(id, edge, lane, node, pos, file, outfile, period, probe, invalidJamThreshold, vTypes, local);
     695         1049 :         if (file == "") {
     696         1014 :             trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
     697              :         }
     698              :     }
     699         1137 : }
     700              : 
     701              : 
     702              : void
     703         4005 : NLTriggerBuilder::parseAndBuildRerouter(MSNet& net, const SUMOSAXAttributes& attrs) {
     704         4005 :     bool ok = true;
     705              :     // get the id, throw if not given or empty...
     706         4005 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     707         4005 :     if (!ok) {
     708            0 :         throw ProcessError();
     709              :     }
     710              :     if (MSTriggeredRerouter::getInstances().count(id) > 0) {
     711           21 :         throw InvalidArgument("Could not build rerouter '" + id + "'; probably declared twice.");
     712              :     }
     713              :     MSEdgeVector edges;
     714         9298 :     for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok)) {
     715         5300 :         MSEdge* edge = MSEdge::dictionary(edgeID);
     716         5300 :         if (edge == nullptr) {
     717            0 :             throw InvalidArgument("The edge '" + edgeID + "' to use within rerouter '" + id + "' is not known.");
     718              :         }
     719         5300 :         edges.push_back(edge);
     720         3998 :     }
     721         3998 :     if (!ok) {
     722            0 :         throw InvalidArgument("The edge to use within rerouter '" + id + "' is not known.");
     723              :     }
     724         3998 :     if (edges.size() == 0) {
     725            0 :         throw InvalidArgument("No edges found for rerouter '" + id + "'.");
     726              :     }
     727         3998 :     const double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, id.c_str(), ok, 1);
     728         3998 :     const bool off = attrs.getOpt<bool>(SUMO_ATTR_OFF, id.c_str(), ok, false);
     729         3998 :     const bool optional = attrs.getOpt<bool>(SUMO_ATTR_OPTIONAL, id.c_str(), ok, false);
     730         3998 :     const SUMOTime timeThreshold = TIME2STEPS(attrs.getOpt<double>(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, 0));
     731         3998 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
     732         7996 :     const std::string pos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, id.c_str(), ok, "");
     733         3998 :     const double radius = attrs.getOpt<double>(SUMO_ATTR_RADIUS, id.c_str(), ok, std::numeric_limits<double>::max());
     734         3998 :     if (attrs.hasAttribute(SUMO_ATTR_RADIUS) && !attrs.hasAttribute(SUMO_ATTR_POSITION)) {
     735            3 :         WRITE_WARNINGF(TL("It is strongly advisable to give an explicit position when using radius in the definition of rerouter '%'."), id)
     736              :     }
     737         3998 :     Position p = Position::INVALID;
     738         3998 :     if (pos != "") {
     739         1251 :         const std::vector<std::string> posSplit = StringTokenizer(pos, ",").getVector();
     740          417 :         if (posSplit.size() == 1) {
     741            2 :             double lanePos = StringUtils::toDouble(pos);
     742            2 :             if (lanePos < 0) {
     743            0 :                 lanePos += edges.front()->getLanes()[0]->getLength();
     744              :             }
     745            2 :             p = edges.front()->getLanes()[0]->geometryPositionAtOffset(lanePos);
     746          415 :         } else if (posSplit.size() == 2) {
     747          415 :             p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]));
     748            0 :         } else if (posSplit.size() == 3) {
     749            0 :             p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]), StringUtils::toDouble(posSplit[2]));
     750              :         } else {
     751            0 :             throw InvalidArgument("Invalid position for rerouter '" + id + "'.");
     752              :         }
     753          417 :     }
     754         3998 :     if (!ok) {
     755            0 :         throw InvalidArgument("Could not parse rerouter '" + id + "'.");
     756              :     }
     757         3998 :     MSTriggeredRerouter* trigger = buildRerouter(net, id, edges, prob, off, optional, timeThreshold, vTypes, p, radius);
     758              :     // read in the trigger description
     759         3998 :     trigger->registerParent(SUMO_TAG_REROUTER, myHandler);
     760         7996 : }
     761              : 
     762              : 
     763              : // -------------------------
     764              : 
     765              : 
     766              : MSLaneSpeedTrigger*
     767          468 : NLTriggerBuilder::buildLaneSpeedTrigger(MSNet& /*net*/, const std::string& id,
     768              :                                         const std::vector<MSLane*>& destLanes,
     769              :                                         const std::string& file) {
     770          468 :     return new MSLaneSpeedTrigger(id, destLanes, file);
     771              : }
     772              : 
     773              : 
     774              : METriggeredCalibrator*
     775           88 : NLTriggerBuilder::buildMECalibrator(const std::string& id,
     776              :                                     MSEdge* edge,
     777              :                                     double pos,
     778              :                                     const std::string& file,
     779              :                                     const std::string& outfile,
     780              :                                     const SUMOTime freq,
     781              :                                     MSRouteProbe* probe,
     782              :                                     const double invalidJamThreshold,
     783              :                                     const std::string& vTypes) {
     784              :     return new METriggeredCalibrator(id, edge, pos, file, outfile, freq,
     785           84 :                                      edge == nullptr ? 0. : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)->getLength(),
     786          172 :                                      probe, invalidJamThreshold, vTypes);
     787              : }
     788              : 
     789              : 
     790              : MSCalibrator*
     791         1049 : NLTriggerBuilder::buildCalibrator(const std::string& id,
     792              :                                   MSEdge* edge,
     793              :                                   MSLane* lane,
     794              :                                   MSJunction* node,
     795              :                                   double pos,
     796              :                                   const std::string& file,
     797              :                                   const std::string& outfile,
     798              :                                   const SUMOTime freq,
     799              :                                   const MSRouteProbe* probe,
     800              :                                   const double invalidJamThreshold,
     801              :                                   const std::string& vTypes,
     802              :                                   const bool local) {
     803              :     return new MSCalibrator(id, edge, lane, node, pos, file, outfile, freq,
     804              :                             edge == nullptr ? 0. : edge->getLength(),
     805         2088 :                             probe, invalidJamThreshold, vTypes, local, true);
     806              : }
     807              : 
     808              : 
     809              : MSTriggeredRerouter*
     810         3289 : NLTriggerBuilder::buildRerouter(MSNet&, const std::string& id,
     811              :                                 MSEdgeVector& edges, double prob, bool off, bool optional,
     812              :                                 SUMOTime timeThreshold, const std::string& vTypes, const Position& pos, const double radius) {
     813         3289 :     return new MSTriggeredRerouter(id, edges, prob, off, optional, timeThreshold, vTypes, pos, radius);
     814              : }
     815              : 
     816              : 
     817              : void
     818        39440 : NLTriggerBuilder::buildStoppingPlace(MSNet& net, std::string id, std::vector<std::string> lines, MSLane* lane,
     819              :                                      double frompos, double topos, const SumoXMLTag element, std::string ptStopName,
     820              :                                      int personCapacity, double parkingLength, RGBColor& color, double angle) {
     821        78880 :     myCurrentStop = new MSStoppingPlace(id, element, lines, *lane, frompos, topos, ptStopName, personCapacity, parkingLength, color, angle);
     822        39440 :     if (!net.addStoppingPlace(element, myCurrentStop)) {
     823            5 :         delete myCurrentStop;
     824            5 :         myCurrentStop = nullptr;
     825           15 :         throw InvalidArgument("Could not build " + toString(element) + " '" + id + "'; probably declared twice.");
     826              :     }
     827        39435 : }
     828              : 
     829              : 
     830              : void
     831        13509 : NLTriggerBuilder::beginParkingArea(MSNet& net, const std::string& id,
     832              :                                    const std::vector<std::string>& lines,
     833              :                                    const std::vector<std::string>& badges,
     834              :                                    MSLane* lane, double frompos, double topos,
     835              :                                    unsigned int capacity,
     836              :                                    double width, double length, double angle, const std::string& name,
     837              :                                    bool onRoad,
     838              :                                    const std::string& departPos,
     839              :                                    bool lefthand) {
     840              :     // Close previous parking area if there are no lots inside
     841        13509 :     MSParkingArea* stop = new MSParkingArea(id, lines, badges, *lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand);
     842        13509 :     if (!net.addStoppingPlace(SUMO_TAG_PARKING_AREA, stop)) {
     843            0 :         delete stop;
     844            0 :         throw InvalidArgument("Could not build parking area '" + id + "'; probably declared twice.");
     845              :     } else {
     846        13509 :         myParkingArea = stop;
     847              :     }
     848        13509 : }
     849              : 
     850              : 
     851              : void
     852          805 : NLTriggerBuilder::addLotEntry(double x, double y, double z,
     853              :                               double width, double length,
     854              :                               double angle, double slope) {
     855          805 :     if (myParkingArea != nullptr) {
     856          805 :         if (!myParkingArea->parkOnRoad()) {
     857          805 :             myParkingArea->addLotEntry(x, y, z, width, length, angle, slope);
     858          805 :             myParkingAreaCapacitySet = true;
     859              :         } else {
     860            0 :             throw InvalidArgument("Cannot not add lot entry to on-road parking area.");
     861              :         }
     862              :     } else {
     863            0 :         throw InvalidArgument("Could not add lot entry outside a parking area.");
     864              :     }
     865          805 : }
     866              : 
     867              : 
     868              : void
     869        13510 : NLTriggerBuilder::endParkingArea() {
     870        13510 :     if (myParkingArea != nullptr) {
     871        13509 :         myParkingArea = nullptr;
     872        13509 :         myParkingAreaCapacitySet = false;
     873              :     } else {
     874            2 :         throw InvalidArgument("Could not end a parking area that is not opened.");
     875              :     }
     876        13509 : }
     877              : 
     878              : 
     879              : void
     880        51375 : NLTriggerBuilder::endStoppingPlace() {
     881        51375 :     if (myCurrentStop != nullptr) {
     882        51366 :         myCurrentStop->finishedLoading();
     883        51366 :         myCurrentStop = nullptr;
     884              :     } else {
     885           18 :         throw InvalidArgument("Could not end a stopping place that is not opened.");
     886              :     }
     887        51366 : }
     888              : 
     889              : 
     890              : void
     891        11931 : NLTriggerBuilder::buildChargingStation(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
     892              :                                        const std::string& name, double chargingPower, double totalPower, double efficiency, 
     893              :                                        bool chargeInTransit, SUMOTime chargeDelay, std::string chargeType, SUMOTime waitingTime, MSParkingArea* parkingArea) {
     894        11931 :     MSChargingStation* chargingStation = (parkingArea == nullptr) ? new MSChargingStation(id, *lane, frompos, topos, name, chargingPower, totalPower, efficiency,
     895        11919 :                                          chargeInTransit, chargeDelay, chargeType, waitingTime) : new MSChargingStation(id, parkingArea, name, chargingPower, totalPower, efficiency,
     896           12 :                                                  chargeInTransit, chargeDelay, chargeType, waitingTime);
     897        11931 :     if (!net.addStoppingPlace(SUMO_TAG_CHARGING_STATION, chargingStation)) {
     898            0 :         delete chargingStation;
     899            0 :         throw InvalidArgument("Could not build charging station '" + id + "'; probably declared twice.");
     900              :     }
     901        11931 :     myCurrentStop = chargingStation;
     902        11931 : }
     903              : 
     904              : 
     905              : void
     906           50 : NLTriggerBuilder::buildOverheadWireSegment(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
     907              :         bool voltageSource) {
     908           50 :     MSOverheadWire* overheadWireSegment = new MSOverheadWire(id, *lane, frompos, topos, voltageSource);
     909           50 :     if (!net.addStoppingPlace(SUMO_TAG_OVERHEAD_WIRE_SEGMENT, overheadWireSegment)) {
     910            0 :         delete overheadWireSegment;
     911            0 :         throw InvalidArgument("Could not build overheadWireSegment '" + id + "'; probably declared twice.");
     912              :     }
     913           50 : }
     914              : 
     915              : void
     916            4 : NLTriggerBuilder::buildInnerOverheadWireSegments(MSNet& net, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
     917            4 :     if (frontConnection == NULL && behindConnection == NULL) {
     918            8 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     919            0 :     } else if (frontConnection != NULL && behindConnection == NULL) {
     920            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
     921            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     922            0 :     } else if (frontConnection == NULL && behindConnection != NULL) {
     923            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
     924            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     925            0 :     } else if (frontConnection != NULL && behindConnection != NULL) {
     926            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
     927            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
     928            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     929              :     }
     930            4 : }
     931              : 
     932              : void
     933           17 : NLTriggerBuilder::buildTractionSubstation(MSNet& net, std::string id, double voltage, double currentLimit) {
     934           17 :     MSTractionSubstation* myTractionSubstation = new MSTractionSubstation(id, voltage, currentLimit);
     935           17 :     if (!net.addTractionSubstation(myTractionSubstation)) {
     936            0 :         delete myTractionSubstation;
     937            0 :         throw InvalidArgument("Could not build traction substation '" + id + "'; probably declared twice.");
     938              :     }
     939           17 : }
     940              : 
     941              : void
     942           10 : NLTriggerBuilder::buildOverheadWireClamp(MSNet& /*net*/, const std::string& /*id*/, MSLane* /*lane_start*/, MSLane* /*lane_end*/) {
     943           10 : }
     944              : 
     945              : std::string
     946         1705 : NLTriggerBuilder::getFileName(const SUMOSAXAttributes& attrs,
     947              :                               const std::string& base,
     948              :                               const bool allowEmpty) {
     949              :     // get the file name to read further definitions from
     950         1705 :     bool ok = true;
     951         1705 :     std::string file = attrs.getOpt<std::string>(SUMO_ATTR_FILE, nullptr, ok, "");
     952         1705 :     if (file == "") {
     953         1488 :         if (allowEmpty) {
     954         1488 :             return file;
     955              :         }
     956            0 :         throw InvalidArgument("No filename given.");
     957              :     }
     958              :     // check whether absolute or relative filenames are given
     959          217 :     if (!FileHelpers::isAbsolute(file)) {
     960          217 :         return FileHelpers::getConfigurationRelative(base, file);
     961              :     }
     962            0 :     return file;
     963              : }
     964              : 
     965              : 
     966              : MSLane*
     967        71006 : NLTriggerBuilder::getLane(const SUMOSAXAttributes& attrs,
     968              :                           const std::string& tt,
     969              :                           const std::string& tid) {
     970        71006 :     bool ok = true;
     971        71006 :     std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANE, tid.c_str(), ok);
     972        71006 :     MSLane* lane = MSLane::dictionary(objectid);
     973        71006 :     if (lane == nullptr) {
     974              :         // Either a lane that is non-existent/broken, or a lane that is internal and has been ignored.
     975              :         // We assume that internal lane names start with ':'.
     976            0 :         if (objectid[0] == ':' && !MSGlobals::gUsingInternalLanes) {
     977              :             return nullptr;
     978              :         }
     979              :         // Throw the exception only in case that the lane really does not exist in the network file
     980              :         // or it is broken.
     981            0 :         throw InvalidArgument("The lane " + objectid + " to use within the " + tt + " '" + tid + "' is not known.");
     982              :     }
     983              :     return lane;
     984              : }
     985              : 
     986              : 
     987              : MSParkingArea*
     988        12232 : NLTriggerBuilder::getParkingArea(const SUMOSAXAttributes& attrs, const std::string& tt, const std::string& tid) {
     989        12232 :     bool ok = true;
     990        12232 :     std::string objectID = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, tid.c_str(), ok);
     991        12232 :     if (!ok || objectID.size() == 0) {
     992              :         return nullptr;
     993              :     }
     994           17 :     MSParkingArea* pa = static_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(objectID, SUMO_TAG_PARKING_AREA));
     995           17 :     if (pa == nullptr) {
     996              :         // Throw the exception only in case that the lane really does not exist in the network file
     997              :         // or it is broken.
     998            0 :         throw InvalidArgument("The parkingArea " + objectID + " to use within the " + tt + " '" + tid + "' is not known.");
     999              :     }
    1000              :     return pa;
    1001              : }
    1002              : 
    1003              : 
    1004              : double
    1005         1124 : NLTriggerBuilder::getPosition(const SUMOSAXAttributes& attrs,
    1006              :                               MSLane* lane,
    1007              :                               const std::string& tt, const std::string& tid,
    1008              :                               MSEdge* edge) {
    1009              :     assert(lane != nullptr || edge != nullptr);
    1010         1124 :     const double length = lane != nullptr ? lane->getLength() : edge->getLength();
    1011         1124 :     bool ok = true;
    1012         1124 :     double pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
    1013         1124 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
    1014         1124 :     if (!ok) {
    1015            0 :         throw InvalidArgument("Error on parsing a position information.");
    1016              :     }
    1017         1124 :     if (pos < 0) {
    1018            2 :         pos = length + pos;
    1019              :     }
    1020         1124 :     if (pos > length) {
    1021            1 :         if (friendlyPos) {
    1022            0 :             pos = length - (double) 0.1;
    1023              :         } else {
    1024            1 :             if (lane != nullptr) {
    1025            3 :                 throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the lane's '" + lane->getID() + "' length.");
    1026              :             } else {
    1027            0 :                 throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the edge's '" + edge->getID() + "' length.");
    1028              :             }
    1029              :         }
    1030              :     }
    1031         1123 :     return pos;
    1032              : }
    1033              : 
    1034              : 
    1035              : void
    1036        14363 : NLTriggerBuilder::updateParkingAreaDefaultCapacity() {
    1037        14363 :     if (myParkingArea != nullptr && !myParkingAreaCapacitySet) {
    1038         9829 :         myParkingArea->setRoadsideCapacity(1);
    1039              :     }
    1040        14363 : }
    1041              : 
    1042              : 
    1043              : MSStoppingPlace*
    1044        68107 : NLTriggerBuilder::getCurrentStop() {
    1045        68107 :     return myParkingArea == nullptr ? myCurrentStop : myParkingArea;
    1046              : }
    1047              : 
    1048              : 
    1049              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1