LCOV - code coverage report
Current view: top level - src/netload - NLTriggerBuilder.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 81.1 % 514 417
Test Date: 2026-03-02 16:00:03 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-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    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        41234 : NLTriggerBuilder::NLTriggerBuilder()
      61        41234 :     : myHandler(nullptr), myParkingArea(nullptr), myCurrentStop(nullptr) {}
      62              : 
      63              : 
      64        41234 : NLTriggerBuilder::~NLTriggerBuilder() {}
      65              : 
      66              : void
      67        41234 : NLTriggerBuilder::setHandler(NLHandler* handler) {
      68        41234 :     myHandler = handler;
      69        41234 : }
      70              : 
      71              : 
      72              : void
      73          505 : NLTriggerBuilder::buildVaporizer(const SUMOSAXAttributes& attrs) {
      74          505 :     WRITE_WARNING(TL("Vaporizers are deprecated. Use rerouters instead."));
      75          505 :     bool ok = true;
      76              :     // get the id, throw if not given or empty...
      77          505 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
      78          505 :     if (!ok) {
      79              :         return;
      80              :     }
      81          493 :     MSEdge* e = MSEdge::dictionary(id);
      82          493 :     if (e == nullptr) {
      83           18 :         WRITE_ERRORF(TL("Unknown edge ('%') referenced in a vaporizer."), id);
      84            6 :         return;
      85              :     }
      86          487 :     SUMOTime begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok);
      87          487 :     SUMOTime end = attrs.getSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok);
      88          487 :     if (!ok) {
      89              :         return;
      90              :     }
      91          451 :     if (begin < 0) {
      92           18 :         WRITE_ERRORF(TL("A vaporization begin time is negative (edge id='%')."), id);
      93            6 :         return;
      94              :     }
      95          445 :     if (begin >= end) {
      96           36 :         WRITE_ERRORF(TL("A vaporization ends before it starts (edge id='%')."), id);
      97           12 :         return;
      98              :     }
      99          866 :     if (end >= string2time(OptionsCont::getOptions().getString("begin"))) {
     100          433 :         Command* cb = new WrappingCommand< MSEdge >(e, &MSEdge::incVaporization);
     101          433 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cb, begin);
     102          433 :         Command* ce = new WrappingCommand< MSEdge >(e, &MSEdge::decVaporization);
     103          433 :         MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(ce, end);
     104              :     }
     105              : }
     106              : 
     107              : 
     108              : void
     109          503 : NLTriggerBuilder::parseAndBuildLaneSpeedTrigger(MSNet& net, const SUMOSAXAttributes& attrs,
     110              :         const std::string& base) {
     111              :     // get the id, throw if not given or empty...
     112          503 :     bool ok = true;
     113              :     // get the id, throw if not given or empty...
     114          503 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     115          503 :     if (!ok) {
     116              :         return;
     117              :     }
     118              :     // get the file name to read further definitions from
     119          503 :     std::string file = getFileName(attrs, base, true);
     120          503 :     std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANES, id.c_str(), ok);
     121              :     std::vector<MSLane*> lanes;
     122         1060 :     for (const std::string& laneID : attrs.get<std::vector<std::string> >(SUMO_ATTR_LANES, id.c_str(), ok)) {
     123          557 :         MSLane* lane = MSLane::dictionary(laneID);
     124          557 :         if (lane == nullptr) {
     125            0 :             throw InvalidArgument("The lane '" + laneID + "' to use within MSLaneSpeedTrigger '" + id + "' is not known.");
     126              :         }
     127          557 :         lanes.push_back(lane);
     128          503 :     }
     129          503 :     if (!ok) {
     130            0 :         throw InvalidArgument("The lanes to use within MSLaneSpeedTrigger '" + id + "' are not known.");
     131              :     }
     132          503 :     if (lanes.size() == 0) {
     133            0 :         throw InvalidArgument("No lane defined for MSLaneSpeedTrigger '" + id + "'.");
     134              :     }
     135              :     try {
     136          503 :         MSLaneSpeedTrigger* trigger = buildLaneSpeedTrigger(net, id, lanes, file);
     137          503 :         if (file == "") {
     138          335 :             trigger->registerParent(SUMO_TAG_VSS, myHandler);
     139              :         }
     140            0 :     } catch (ProcessError& e) {
     141            0 :         throw InvalidArgument(e.what());
     142            0 :     }
     143          503 : }
     144              : 
     145              : 
     146              : void
     147        15241 : NLTriggerBuilder::parseAndBuildChargingStation(MSNet& net, const SUMOSAXAttributes& attrs) {
     148        15241 :     bool ok = true;
     149              : 
     150              :     // get the id, throw if not given or empty...
     151        15241 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     152        15241 :     if (!ok) {
     153            0 :         throw ProcessError();
     154              :     }
     155              : 
     156        30482 :     MSLane* const lane = getLane(attrs, "chargingStation", id);
     157        15241 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
     158        15241 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     159        15241 :     const double chargingPower = attrs.getOpt<double>(SUMO_ATTR_CHARGINGPOWER, id.c_str(), ok, 22000);
     160        15241 :     const double totalPower = attrs.getOpt<double>(SUMO_ATTR_TOTALPOWER, id.c_str(), ok, -1);
     161        15241 :     const double efficiency = attrs.getOpt<double>(SUMO_ATTR_EFFICIENCY, id.c_str(), ok, 0.95);
     162        15241 :     const bool chargeInTransit = attrs.getOpt<bool>(SUMO_ATTR_CHARGEINTRANSIT, id.c_str(), ok, 0);
     163        15241 :     const SUMOTime chargeDelay = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CHARGEDELAY, id.c_str(), ok, 0);
     164        30483 :     const std::string chargeType = attrs.getOpt<std::string>(SUMO_ATTR_CHARGETYPE, id.c_str(), ok, "normal");
     165        15241 :     const SUMOTime waitingTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, id.c_str(), ok, 900);
     166        15241 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     167        15242 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok, "");
     168        30482 :     MSParkingArea* parkingArea = getParkingArea(attrs, "parkingArea", id);
     169              : 
     170              :     // check charge type
     171        15241 :     if ((chargeType != "normal") && (chargeType != "battery-exchange") && (chargeType != "fuel")) {
     172            0 :         throw InvalidArgument("The chargeType to use within MSLaneSpeedTrigger '" + id + "' is invalid.");
     173              :     }
     174              : 
     175        15241 :     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        45721 :     buildChargingStation(net, id, lane, frompos, topos, name, chargingPower, totalPower, efficiency, chargeInTransit, chargeDelay, chargeType, waitingTime, parkingArea);
     180        15240 : }
     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        50889 : NLTriggerBuilder::parseAndBuildStoppingPlace(MSNet& net, const SUMOSAXAttributes& attrs, const SumoXMLTag element) {
     487        50889 :     bool ok = true;
     488              :     // get the id, throw if not given or empty...
     489        50889 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     490        50889 :     if (!ok) {
     491            0 :         throw ProcessError();
     492              :     }
     493              : 
     494              :     //get the name, leave blank if not given
     495       101789 :     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        50889 :     RGBColor color = attrs.getOpt<RGBColor>(SUMO_ATTR_COLOR, id.c_str(), ok, RGBColor::INVISIBLE);
     500              : 
     501       101778 :     MSLane* lane = getLane(attrs, toString(element), id);
     502              :     // get the positions
     503        50889 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0.);
     504        50889 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     505        50889 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 90);
     506        50889 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     507        50889 :     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        50897 :     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        50886 :     if (element != SUMO_TAG_CONTAINER_STOP) {
     514        37350 :         defaultCapacity = MAX2(MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element) * 3, 6);
     515              :         capacityAttr = SUMO_ATTR_PERSON_CAPACITY;
     516              :     } else {
     517        13536 :         defaultCapacity = MSStoppingPlace::getDefaultTransportablesAbreast(topos - frompos, element);
     518              :         capacityAttr = SUMO_ATTR_CONTAINER_CAPACITY;
     519              :     }
     520        50886 :     const int transportableCapacity = attrs.getOpt<int>(capacityAttr, id.c_str(), ok, defaultCapacity);
     521        50886 :     const double parkingLength = attrs.getOpt<double>(SUMO_ATTR_PARKING_LENGTH, id.c_str(), ok, 0);
     522              :     // build the bus stop
     523       203552 :     buildStoppingPlace(net, id, lines, lane, frompos, topos, element, ptStopName, transportableCapacity, parkingLength, color, angle);
     524       101764 : }
     525              : 
     526              : 
     527              : void
     528         2252 : NLTriggerBuilder::addAccess(MSNet& /* net */, const SUMOSAXAttributes& attrs) {
     529         2252 :     if (myCurrentStop == nullptr) {
     530           30 :         throw InvalidArgument("Could not add access outside a stopping place.");
     531              :     }
     532              :     // get the lane
     533         4474 :     MSLane* lane = getLane(attrs, "access", myCurrentStop->getID());
     534         2237 :     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         2236 :     bool ok = true;
     540         2236 :     const std::string accessPos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, "access", ok);
     541         2236 :     const bool random = accessPos == "random";
     542              :     MSStoppingPlace::AccessExit exit = MSStoppingPlace::AccessExit::PLATFORM;
     543         2236 :     if (accessPos == "doors") {
     544              :         exit = MSStoppingPlace::AccessExit::DOORS;
     545         2204 :     } else if (accessPos == "carriage") {
     546              :         exit = MSStoppingPlace::AccessExit::CARRIAGE;
     547              :     }
     548         2236 :     double startPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? 0. : attrs.getOpt<double>(SUMO_ATTR_POSITION, "access", ok, 0);
     549         2236 :     double endPos = random || exit != MSStoppingPlace::AccessExit::PLATFORM ? lane->getLength() : startPos;
     550         2236 :     const double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "access", ok, -1);
     551         2236 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, "access", ok, false);
     552         2236 :     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         2236 :     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        17407 : NLTriggerBuilder::parseAndBeginParkingArea(MSNet& net, const SUMOSAXAttributes& attrs) {
     564        17407 :     bool ok = true;
     565              :     // get the id, throw if not given or empty...
     566        17407 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     567        17407 :     if (!ok) {
     568            0 :         throw ProcessError();
     569              :     }
     570              :     // get the lane
     571        34814 :     MSLane* lane = getLane(attrs, "parkingArea", id);
     572              :     // get the positions
     573        17407 :     double frompos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, id.c_str(), ok, 0);
     574        17407 :     double topos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, id.c_str(), ok, lane->getLength());
     575        17407 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, id.c_str(), ok, false);
     576        17407 :     unsigned int capacity = attrs.getOpt<int>(SUMO_ATTR_ROADSIDE_CAPACITY, id.c_str(), ok, 0);
     577        17407 :     myParkingAreaCapacitySet = attrs.hasAttribute(SUMO_ATTR_ROADSIDE_CAPACITY);
     578        17407 :     bool onRoad = attrs.getOpt<bool>(SUMO_ATTR_ONROAD, id.c_str(), ok, false);
     579        17407 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, id.c_str(), ok, 0);
     580        17407 :     double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, id.c_str(), ok, 0);
     581        17407 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, id.c_str(), ok, 0);
     582        34815 :     const std::string name = attrs.getOpt<std::string>(SUMO_ATTR_NAME, id.c_str(), ok);
     583        34815 :     const std::string departPos = attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS, id.c_str(), ok);
     584        17407 :     bool lefthand = attrs.getOpt<bool>(SUMO_ATTR_LEFTHAND, id.c_str(), ok, false);
     585        17408 :     const std::vector<std::string>& acceptedBadges = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_ACCEPTED_BADGES, id.c_str(), ok);
     586        17407 :     const bool reservable = attrs.getOpt<bool>(SUMO_ATTR_RESERVABLE, id.c_str(), ok, false);
     587              : 
     588        17407 :     if (!ok || (myHandler->checkStopPos(frompos, topos, lane->getLength(), POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
     589            3 :         throw InvalidArgument("Invalid position for parking area '" + id + "'.");
     590              :     }
     591        17406 :     const std::vector<std::string>& lines = attrs.getOpt<std::vector<std::string> >(SUMO_ATTR_LINES, id.c_str(), ok);
     592              :     // build the parking area
     593        17406 :     beginParkingArea(net, id, lines, acceptedBadges, lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand, reservable);
     594        34813 : }
     595              : 
     596              : 
     597              : 
     598              : void
     599          655 : NLTriggerBuilder::parseAndAddLotEntry(const SUMOSAXAttributes& attrs) {
     600          655 :     bool ok = true;
     601              :     // Check for open parking area
     602          655 :     if (myParkingArea == nullptr) {
     603            0 :         throw ProcessError();
     604              :     }
     605              :     // get the positions
     606          655 :     double x = attrs.get<double>(SUMO_ATTR_X, "", ok);
     607          655 :     if (!ok) {
     608            0 :         throw InvalidArgument("Invalid x position for lot entry.");
     609              :     }
     610          655 :     double y = attrs.get<double>(SUMO_ATTR_Y, "", ok);
     611          655 :     if (!ok) {
     612            0 :         throw InvalidArgument("Invalid y position for lot entry.");
     613              :     }
     614          655 :     double z = attrs.getOpt<double>(SUMO_ATTR_Z, "", ok, 0.);
     615          655 :     double width = attrs.getOpt<double>(SUMO_ATTR_WIDTH, "", ok, myParkingArea->getWidth());
     616          655 :     double length = attrs.getOpt<double>(SUMO_ATTR_LENGTH, "", ok, myParkingArea->getLength());
     617          655 :     double angle = attrs.getOpt<double>(SUMO_ATTR_ANGLE, "", ok, myParkingArea->getAngle());
     618          655 :     double slope = attrs.getOpt<double>(SUMO_ATTR_SLOPE, "", ok, 0.);
     619              :     // add the lot entry
     620          655 :     addLotEntry(x, y, z, width, length, angle, slope);
     621          655 : }
     622              : 
     623              : 
     624              : void
     625          923 : NLTriggerBuilder::parseAndBuildCalibrator(MSNet& net, const SUMOSAXAttributes& attrs,
     626              :         const std::string& base) {
     627          923 :     bool ok = true;
     628              :     // get the id, throw if not given or empty...
     629          923 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     630          923 :     if (!ok) {
     631            0 :         throw ProcessError();
     632              :     }
     633              :     MSLane* lane = nullptr;
     634              :     MSEdge* edge = nullptr;
     635              :     MSJunction* node = nullptr;
     636          923 :     if (attrs.hasAttribute(SUMO_ATTR_NODE)) {
     637           14 :         if (attrs.hasAttribute(SUMO_ATTR_LANE) || attrs.hasAttribute(SUMO_ATTR_EDGE)) {
     638            0 :             throw InvalidArgument("The node calibrator '" + id + "' cannot define an edge or lane as well.");
     639              :         }
     640           14 :         const std::string nodeID = attrs.get<std::string>(SUMO_ATTR_NODE, id.c_str(), ok);
     641              :         node = net.getJunctionControl().get(nodeID);
     642           14 :         if (node == nullptr) {
     643            0 :             throw InvalidArgument("The node " + nodeID + " to use within the calibrator '" + id + "' is not known.");
     644              :         }
     645              :     } else {
     646          909 :         if (attrs.hasAttribute(SUMO_ATTR_EDGE)) {
     647          447 :             const std::string edgeID = attrs.get<std::string>(SUMO_ATTR_EDGE, id.c_str(), ok);
     648          447 :             edge = MSEdge::dictionary(edgeID);
     649          447 :             if (edge == nullptr) {
     650            0 :                 throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id + "' is not known.");
     651              :             }
     652          447 :             if (attrs.hasAttribute(SUMO_ATTR_LANE)) {
     653            0 :                 lane = getLane(attrs, "calibrator", id);
     654            0 :                 if (&lane->getEdge() != edge) {
     655            0 :                     throw InvalidArgument("The edge " + edgeID + " to use within the calibrator '" + id
     656            0 :                                           + "' does not match the calibrator lane '" + lane->getID() + ".");
     657              :                 }
     658              :             }
     659              :         } else {
     660          924 :             lane = getLane(attrs, "calibrator", id);
     661              :             edge = &lane->getEdge();
     662              :         }
     663              :     }
     664          923 :     const double pos = node != nullptr ? 0 : getPosition(attrs, lane, "calibrator", id, edge);
     665          923 :     SUMOTime period = attrs.getOptPeriod(id.c_str(), ok, DELTA_T); // !!! no error handling
     666          923 :     if (period > TIME2STEPS(1)) {
     667          291 :         WRITE_WARNINGF("Reducing period to 1 for calibrator '%'", id);
     668              :         period = TIME2STEPS(1);
     669              :     }
     670          923 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
     671          923 :     const std::string file = getFileName(attrs, base, true);
     672          923 :     const std::string outfile = attrs.getOpt<std::string>(SUMO_ATTR_OUTPUT, id.c_str(), ok, "");
     673          923 :     const std::string routeProbe = attrs.getOpt<std::string>(SUMO_ATTR_ROUTEPROBE, id.c_str(), ok, "");
     674              :     // differing defaults for backward compatibility, values are dimensionless
     675         1758 :     const double invalidJamThreshold = attrs.getOpt<double>(SUMO_ATTR_JAM_DIST_THRESHOLD, id.c_str(), ok, MSGlobals::gUseMesoSim ? 0.8 : 0.5);
     676          923 :     const bool local = attrs.getOpt<bool>(SUMO_ATTR_LOCAL, id.c_str(), ok, false);
     677              :     MSRouteProbe* probe = nullptr;
     678          923 :     if (routeProbe != "") {
     679           62 :         probe = dynamic_cast<MSRouteProbe*>(net.getDetectorControl().getTypedDetectors(SUMO_TAG_ROUTEPROBE).get(routeProbe));
     680           31 :         if (probe == nullptr) {
     681            0 :             throw InvalidArgument("The routeProbe '" + routeProbe + "' to use within the calibrator '" + id + "' is not known.");
     682              :         }
     683              :     }
     684          923 :     if (MSGlobals::gUseMesoSim) {
     685           88 :         if (lane != nullptr && edge->getLanes().size() > 1) {
     686           27 :             WRITE_WARNING("Meso calibrator '" + id
     687              :                           + "' defined for lane '" + lane->getID()
     688              :                           + "' will collect data for all lanes of edge '" + edge->getID() + "'.");
     689              :         }
     690           88 :         METriggeredCalibrator* trigger = buildMECalibrator(id, edge, pos, file, outfile, period, probe, invalidJamThreshold, vTypes);
     691           88 :         if (file == "") {
     692           74 :             trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
     693              :         }
     694              :     } else {
     695          835 :         MSCalibrator* trigger = buildCalibrator(id, edge, lane, node, pos, file, outfile, period, probe, invalidJamThreshold, vTypes, local);
     696          835 :         if (file == "") {
     697          800 :             trigger->registerParent(SUMO_TAG_CALIBRATOR, myHandler);
     698              :         }
     699              :     }
     700          923 : }
     701              : 
     702              : 
     703              : void
     704         4011 : NLTriggerBuilder::parseAndBuildRerouter(MSNet& net, const SUMOSAXAttributes& attrs) {
     705         4011 :     bool ok = true;
     706              :     // get the id, throw if not given or empty...
     707         4011 :     std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
     708         4011 :     if (!ok) {
     709            0 :         throw ProcessError();
     710              :     }
     711              :     if (MSTriggeredRerouter::getInstances().count(id) > 0) {
     712           21 :         throw InvalidArgument("Could not build rerouter '" + id + "'; probably declared twice.");
     713              :     }
     714              :     MSEdgeVector edges;
     715         9322 :     for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_EDGES, id.c_str(), ok)) {
     716         5318 :         MSEdge* edge = MSEdge::dictionary(edgeID);
     717         5318 :         if (edge == nullptr) {
     718            0 :             throw InvalidArgument("The edge '" + edgeID + "' to use within rerouter '" + id + "' is not known.");
     719              :         }
     720         5318 :         edges.push_back(edge);
     721         4004 :     }
     722         4004 :     if (!ok) {
     723            0 :         throw InvalidArgument("The edge to use within rerouter '" + id + "' is not known.");
     724              :     }
     725         4004 :     if (edges.size() == 0) {
     726            0 :         throw InvalidArgument("No edges found for rerouter '" + id + "'.");
     727              :     }
     728         4004 :     const double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, id.c_str(), ok, 1);
     729         4004 :     const bool off = attrs.getOpt<bool>(SUMO_ATTR_OFF, id.c_str(), ok, false);
     730         4004 :     const bool optional = attrs.getOpt<bool>(SUMO_ATTR_OPTIONAL, id.c_str(), ok, false);
     731         4004 :     const SUMOTime timeThreshold = TIME2STEPS(attrs.getOpt<double>(SUMO_ATTR_HALTING_TIME_THRESHOLD, id.c_str(), ok, 0));
     732         4004 :     const std::string vTypes = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id.c_str(), ok, "");
     733         8008 :     const std::string pos = attrs.getOpt<std::string>(SUMO_ATTR_POSITION, id.c_str(), ok, "");
     734         4004 :     const double radius = attrs.getOpt<double>(SUMO_ATTR_RADIUS, id.c_str(), ok, std::numeric_limits<double>::max());
     735         4004 :     if (attrs.hasAttribute(SUMO_ATTR_RADIUS) && !attrs.hasAttribute(SUMO_ATTR_POSITION)) {
     736            3 :         WRITE_WARNINGF(TL("It is strongly advisable to give an explicit position when using radius in the definition of rerouter '%'."), id)
     737              :     }
     738         4004 :     Position p = Position::INVALID;
     739         4004 :     if (pos != "") {
     740         1134 :         const std::vector<std::string> posSplit = StringTokenizer(pos, ",").getVector();
     741          378 :         if (posSplit.size() == 1) {
     742            2 :             double lanePos = StringUtils::toDouble(pos);
     743            2 :             if (lanePos < 0) {
     744            0 :                 lanePos += edges.front()->getLanes()[0]->getLength();
     745              :             }
     746            2 :             p = edges.front()->getLanes()[0]->geometryPositionAtOffset(lanePos);
     747          376 :         } else if (posSplit.size() == 2) {
     748          376 :             p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]));
     749            0 :         } else if (posSplit.size() == 3) {
     750            0 :             p = Position(StringUtils::toDouble(posSplit[0]), StringUtils::toDouble(posSplit[1]), StringUtils::toDouble(posSplit[2]));
     751              :         } else {
     752            0 :             throw InvalidArgument("Invalid position for rerouter '" + id + "'.");
     753              :         }
     754          378 :     }
     755         4004 :     if (!ok) {
     756            0 :         throw InvalidArgument("Could not parse rerouter '" + id + "'.");
     757              :     }
     758         4004 :     MSTriggeredRerouter* trigger = buildRerouter(net, id, edges, prob, off, optional, timeThreshold, vTypes, p, radius);
     759              :     // read in the trigger description
     760         4004 :     trigger->registerParent(SUMO_TAG_REROUTER, myHandler);
     761         8008 : }
     762              : 
     763              : 
     764              : // -------------------------
     765              : 
     766              : 
     767              : MSLaneSpeedTrigger*
     768          401 : NLTriggerBuilder::buildLaneSpeedTrigger(MSNet& /*net*/, const std::string& id,
     769              :                                         const std::vector<MSLane*>& destLanes,
     770              :                                         const std::string& file) {
     771          401 :     return new MSLaneSpeedTrigger(id, destLanes, file);
     772              : }
     773              : 
     774              : 
     775              : METriggeredCalibrator*
     776           88 : NLTriggerBuilder::buildMECalibrator(const std::string& id,
     777              :                                     MSEdge* edge,
     778              :                                     double pos,
     779              :                                     const std::string& file,
     780              :                                     const std::string& outfile,
     781              :                                     const SUMOTime freq,
     782              :                                     MSRouteProbe* probe,
     783              :                                     const double invalidJamThreshold,
     784              :                                     const std::string& vTypes) {
     785              :     return new METriggeredCalibrator(id, edge, pos, file, outfile, freq,
     786           84 :                                      edge == nullptr ? 0. : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)->getLength(),
     787          172 :                                      probe, invalidJamThreshold, vTypes);
     788              : }
     789              : 
     790              : 
     791              : MSCalibrator*
     792          835 : NLTriggerBuilder::buildCalibrator(const std::string& id,
     793              :                                   MSEdge* edge,
     794              :                                   MSLane* lane,
     795              :                                   MSJunction* node,
     796              :                                   double pos,
     797              :                                   const std::string& file,
     798              :                                   const std::string& outfile,
     799              :                                   const SUMOTime freq,
     800              :                                   const MSRouteProbe* probe,
     801              :                                   const double invalidJamThreshold,
     802              :                                   const std::string& vTypes,
     803              :                                   const bool local) {
     804              :     return new MSCalibrator(id, edge, lane, node, pos, file, outfile, freq,
     805              :                             edge == nullptr ? 0. : edge->getLength(),
     806         1660 :                             probe, invalidJamThreshold, vTypes, local, true);
     807              : }
     808              : 
     809              : 
     810              : MSTriggeredRerouter*
     811         3276 : NLTriggerBuilder::buildRerouter(MSNet&, const std::string& id,
     812              :                                 MSEdgeVector& edges, double prob, bool off, bool optional,
     813              :                                 SUMOTime timeThreshold, const std::string& vTypes, const Position& pos, const double radius) {
     814         3276 :     return new MSTriggeredRerouter(id, edges, prob, off, optional, timeThreshold, vTypes, pos, radius);
     815              : }
     816              : 
     817              : 
     818              : void
     819        48690 : NLTriggerBuilder::buildStoppingPlace(MSNet& net, std::string id, std::vector<std::string> lines, MSLane* lane,
     820              :                                      double frompos, double topos, const SumoXMLTag element, std::string ptStopName,
     821              :                                      int personCapacity, double parkingLength, RGBColor& color, double angle) {
     822        97380 :     myCurrentStop = new MSStoppingPlace(id, element, lines, *lane, frompos, topos, ptStopName, personCapacity, parkingLength, color, angle);
     823        48690 :     if (!net.addStoppingPlace(element, myCurrentStop)) {
     824            5 :         delete myCurrentStop;
     825            5 :         myCurrentStop = nullptr;
     826           15 :         throw InvalidArgument("Could not build " + toString(element) + " '" + id + "'; probably declared twice.");
     827              :     }
     828        48685 : }
     829              : 
     830              : 
     831              : void
     832        16533 : NLTriggerBuilder::beginParkingArea(MSNet& net, const std::string& id,
     833              :                                    const std::vector<std::string>& lines,
     834              :                                    const std::vector<std::string>& badges,
     835              :                                    MSLane* lane, double frompos, double topos,
     836              :                                    unsigned int capacity,
     837              :                                    double width, double length, double angle, const std::string& name,
     838              :                                    bool onRoad,
     839              :                                    const std::string& departPos,
     840              :                                    bool lefthand, bool reservable) {
     841              :     // Close previous parking area if there are no lots inside
     842        16533 :     MSParkingArea* stop = new MSParkingArea(id, lines, badges, *lane, frompos, topos, capacity, width, length, angle, name, onRoad, departPos, lefthand, reservable);
     843        16533 :     if (!net.addStoppingPlace(SUMO_TAG_PARKING_AREA, stop)) {
     844            0 :         delete stop;
     845            0 :         throw InvalidArgument("Could not build parking area '" + id + "'; probably declared twice.");
     846              :     } else {
     847        16533 :         myParkingArea = stop;
     848              :     }
     849        16533 : }
     850              : 
     851              : 
     852              : void
     853          655 : NLTriggerBuilder::addLotEntry(double x, double y, double z,
     854              :                               double width, double length,
     855              :                               double angle, double slope) {
     856          655 :     if (myParkingArea != nullptr) {
     857          655 :         if (!myParkingArea->parkOnRoad()) {
     858          655 :             myParkingArea->addLotEntry(x, y, z, width, length, angle, slope);
     859          655 :             myParkingAreaCapacitySet = true;
     860              :         } else {
     861            0 :             throw InvalidArgument("Cannot not add lot entry to on-road parking area.");
     862              :         }
     863              :     } else {
     864            0 :         throw InvalidArgument("Could not add lot entry outside a parking area.");
     865              :     }
     866          655 : }
     867              : 
     868              : 
     869              : void
     870        16534 : NLTriggerBuilder::endParkingArea() {
     871        16534 :     if (myParkingArea != nullptr) {
     872        16533 :         myParkingArea = nullptr;
     873        16533 :         myParkingAreaCapacitySet = false;
     874              :     } else {
     875            2 :         throw InvalidArgument("Could not end a parking area that is not opened.");
     876              :     }
     877        16533 : }
     878              : 
     879              : 
     880              : void
     881        63634 : NLTriggerBuilder::endStoppingPlace() {
     882        63634 :     if (myCurrentStop != nullptr) {
     883        63625 :         myCurrentStop->finishedLoading();
     884        63625 :         myCurrentStop = nullptr;
     885              :     } else {
     886           18 :         throw InvalidArgument("Could not end a stopping place that is not opened.");
     887              :     }
     888        63625 : }
     889              : 
     890              : 
     891              : void
     892        14940 : NLTriggerBuilder::buildChargingStation(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
     893              :                                        const std::string& name, double chargingPower, double totalPower, double efficiency,
     894              :                                        bool chargeInTransit, SUMOTime chargeDelay, std::string chargeType, SUMOTime waitingTime, MSParkingArea* parkingArea) {
     895        14940 :     MSChargingStation* chargingStation = (parkingArea == nullptr) ? new MSChargingStation(id, *lane, frompos, topos, name, chargingPower, totalPower, efficiency,
     896        14928 :                                          chargeInTransit, chargeDelay, chargeType, waitingTime) : new MSChargingStation(id, parkingArea, name, chargingPower, totalPower, efficiency,
     897           12 :                                                  chargeInTransit, chargeDelay, chargeType, waitingTime);
     898        14940 :     if (!net.addStoppingPlace(SUMO_TAG_CHARGING_STATION, chargingStation)) {
     899            0 :         delete chargingStation;
     900            0 :         throw InvalidArgument("Could not build charging station '" + id + "'; probably declared twice.");
     901              :     }
     902        14940 :     myCurrentStop = chargingStation;
     903        14940 : }
     904              : 
     905              : 
     906              : void
     907           50 : NLTriggerBuilder::buildOverheadWireSegment(MSNet& net, const std::string& id, MSLane* lane, double frompos, double topos,
     908              :         bool voltageSource) {
     909           50 :     MSOverheadWire* overheadWireSegment = new MSOverheadWire(id, *lane, frompos, topos, voltageSource);
     910           50 :     if (!net.addStoppingPlace(SUMO_TAG_OVERHEAD_WIRE_SEGMENT, overheadWireSegment)) {
     911            0 :         delete overheadWireSegment;
     912            0 :         throw InvalidArgument("Could not build overheadWireSegment '" + id + "'; probably declared twice.");
     913              :     }
     914           50 : }
     915              : 
     916              : void
     917            4 : NLTriggerBuilder::buildInnerOverheadWireSegments(MSNet& net, const MSLane* connection, const MSLane* frontConnection, const MSLane* behindConnection) {
     918            4 :     if (frontConnection == NULL && behindConnection == NULL) {
     919            8 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     920            0 :     } else if (frontConnection != NULL && behindConnection == NULL) {
     921            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
     922            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     923            0 :     } else if (frontConnection == NULL && behindConnection != NULL) {
     924            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
     925            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     926            0 :     } else if (frontConnection != NULL && behindConnection != NULL) {
     927            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + frontConnection->getID(), const_cast<MSLane*>(frontConnection), 0, frontConnection->getLength(), false);
     928            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + behindConnection->getID(), const_cast<MSLane*>(behindConnection), 0, behindConnection->getLength(), false);
     929            0 :         buildOverheadWireSegment(net, "ovrhd_inner_" + connection->getID(), const_cast<MSLane*>(connection), 0, connection->getLength(), false);
     930              :     }
     931            4 : }
     932              : 
     933              : void
     934           17 : NLTriggerBuilder::buildTractionSubstation(MSNet& net, std::string id, double voltage, double currentLimit) {
     935           17 :     MSTractionSubstation* myTractionSubstation = new MSTractionSubstation(id, voltage, currentLimit);
     936           17 :     if (!net.addTractionSubstation(myTractionSubstation)) {
     937            0 :         delete myTractionSubstation;
     938            0 :         throw InvalidArgument("Could not build traction substation '" + id + "'; probably declared twice.");
     939              :     }
     940           17 : }
     941              : 
     942              : void
     943           10 : NLTriggerBuilder::buildOverheadWireClamp(MSNet& /*net*/, const std::string& /*id*/, MSLane* /*lane_start*/, MSLane* /*lane_end*/) {
     944           10 : }
     945              : 
     946              : std::string
     947         1426 : NLTriggerBuilder::getFileName(const SUMOSAXAttributes& attrs,
     948              :                               const std::string& base,
     949              :                               const bool allowEmpty) {
     950              :     // get the file name to read further definitions from
     951         1426 :     bool ok = true;
     952         1426 :     std::string file = attrs.getOpt<std::string>(SUMO_ATTR_FILE, nullptr, ok, "");
     953         1426 :     if (file == "") {
     954         1209 :         if (allowEmpty) {
     955         1209 :             return file;
     956              :         }
     957            0 :         throw InvalidArgument("No filename given.");
     958              :     }
     959              :     // check whether absolute or relative filenames are given
     960          217 :     if (!FileHelpers::isAbsolute(file)) {
     961          217 :         return FileHelpers::getConfigurationRelative(base, file);
     962              :     }
     963            0 :     return file;
     964              : }
     965              : 
     966              : 
     967              : MSLane*
     968        86294 : NLTriggerBuilder::getLane(const SUMOSAXAttributes& attrs,
     969              :                           const std::string& tt,
     970              :                           const std::string& tid) {
     971        86294 :     bool ok = true;
     972        86294 :     std::string objectid = attrs.get<std::string>(SUMO_ATTR_LANE, tid.c_str(), ok);
     973        86294 :     MSLane* lane = MSLane::dictionary(objectid);
     974        86294 :     if (lane == nullptr) {
     975              :         // Either a lane that is non-existent/broken, or a lane that is internal and has been ignored.
     976              :         // We assume that internal lane names start with ':'.
     977            0 :         if (objectid[0] == ':' && !MSGlobals::gUsingInternalLanes) {
     978              :             return nullptr;
     979              :         }
     980              :         // Throw the exception only in case that the lane really does not exist in the network file
     981              :         // or it is broken.
     982            0 :         throw InvalidArgument("The lane " + objectid + " to use within the " + tt + " '" + tid + "' is not known.");
     983              :     }
     984              :     return lane;
     985              : }
     986              : 
     987              : 
     988              : MSParkingArea*
     989        15241 : NLTriggerBuilder::getParkingArea(const SUMOSAXAttributes& attrs, const std::string& tt, const std::string& tid) {
     990        15241 :     bool ok = true;
     991        15241 :     std::string objectID = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, tid.c_str(), ok);
     992        15241 :     if (!ok || objectID.size() == 0) {
     993              :         return nullptr;
     994              :     }
     995           17 :     MSParkingArea* pa = static_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(objectID, SUMO_TAG_PARKING_AREA));
     996           17 :     if (pa == nullptr) {
     997              :         // Throw the exception only in case that the lane really does not exist in the network file
     998              :         // or it is broken.
     999            0 :         throw InvalidArgument("The parkingArea " + objectID + " to use within the " + tt + " '" + tid + "' is not known.");
    1000              :     }
    1001              :     return pa;
    1002              : }
    1003              : 
    1004              : 
    1005              : double
    1006          909 : NLTriggerBuilder::getPosition(const SUMOSAXAttributes& attrs,
    1007              :                               MSLane* lane,
    1008              :                               const std::string& tt, const std::string& tid,
    1009              :                               MSEdge* edge) {
    1010              :     assert(lane != nullptr || edge != nullptr);
    1011          909 :     const double length = lane != nullptr ? lane->getLength() : edge->getLength();
    1012          909 :     bool ok = true;
    1013          909 :     double pos = attrs.get<double>(SUMO_ATTR_POSITION, nullptr, ok);
    1014          909 :     const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
    1015          909 :     if (!ok) {
    1016            0 :         throw InvalidArgument("Error on parsing a position information.");
    1017              :     }
    1018          909 :     if (pos < 0) {
    1019            2 :         pos = length + pos;
    1020              :     }
    1021          909 :     if (pos > length) {
    1022            0 :         if (friendlyPos) {
    1023            0 :             pos = length - (double) 0.1;
    1024              :         } else {
    1025            0 :             if (lane != nullptr) {
    1026            0 :                 throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the lane's '" + lane->getID() + "' length.");
    1027              :             } else {
    1028            0 :                 throw InvalidArgument("The position of " + tt + " '" + tid + "' lies beyond the edge's '" + edge->getID() + "' length.");
    1029              :             }
    1030              :         }
    1031              :     }
    1032          909 :     return pos;
    1033              : }
    1034              : 
    1035              : 
    1036              : void
    1037        17407 : NLTriggerBuilder::updateParkingAreaDefaultCapacity() {
    1038        17407 :     if (myParkingArea != nullptr && !myParkingAreaCapacitySet) {
    1039        12949 :         myParkingArea->setRoadsideCapacity(1);
    1040              :     }
    1041        17407 : }
    1042              : 
    1043              : 
    1044              : MSStoppingPlace*
    1045        83524 : NLTriggerBuilder::getCurrentStop() {
    1046        83524 :     return myParkingArea == nullptr ? myCurrentStop : myParkingArea;
    1047              : }
    1048              : 
    1049              : 
    1050              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1