LCOV - code coverage report
Current view: top level - src/router - ROVehicle.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 84.2 % 190 160
Test Date: 2025-12-06 15:35:27 Functions: 100.0 % 9 9

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2002-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    ROVehicle.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Axel Wegener
      17              : /// @author  Michael Behrisch
      18              : /// @author  Jakob Erdmann
      19              : /// @date    Sept 2002
      20              : ///
      21              : // A vehicle as used by router
      22              : /****************************************************************************/
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <iostream>
      27              : #include <utils/common/StringUtils.h>
      28              : #include <utils/common/ToString.h>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/geom/GeoConvHelper.h>
      31              : #include <utils/vehicle/SUMOVTypeParameter.h>
      32              : #include <utils/options/OptionsCont.h>
      33              : #include <utils/iodevices/OutputDevice.h>
      34              : #include "RORouteDef.h"
      35              : #include "RORoute.h"
      36              : #include "ROHelper.h"
      37              : #include "RONet.h"
      38              : #include "ROLane.h"
      39              : #include "ROVehicle.h"
      40              : 
      41              : // ===========================================================================
      42              : // static members
      43              : // ===========================================================================
      44              : std::map<ConstROEdgeVector, std::string> ROVehicle::mySavedRoutes;
      45              : 
      46              : // ===========================================================================
      47              : // method definitions
      48              : // ===========================================================================
      49       297790 : ROVehicle::ROVehicle(const SUMOVehicleParameter& pars,
      50              :                      RORouteDef* route, const SUMOVTypeParameter* type,
      51       297790 :                      const RONet* net, MsgHandler* errorHandler):
      52              :     RORoutable(pars, type),
      53       297790 :     myRoute(route),
      54       297790 :     myJumpTime(-1) {
      55              :     getParameter().stops.clear();
      56       594667 :     if (route != nullptr && route->getFirstRoute() != nullptr) {
      57       594078 :         for (StopParVector::const_iterator s = route->getFirstRoute()->getStops().begin(); s != route->getFirstRoute()->getStops().end(); ++s) {
      58          324 :             addStop(*s, net, errorHandler);
      59              :         }
      60              :     }
      61       298243 :     for (StopParVector::const_iterator s = pars.stops.begin(); s != pars.stops.end(); ++s) {
      62          453 :         addStop(*s, net, errorHandler);
      63              :     }
      64       297790 :     if (pars.via.size() != 0) {
      65              :         // via takes precedence over stop edges
      66              :         // XXX check for inconsistencies #2275
      67              :         myStopEdges.clear();
      68        48261 :         for (std::vector<std::string>::const_iterator it = pars.via.begin(); it != pars.via.end(); ++it) {
      69              :             assert(net->getEdge(*it) != 0);
      70        67200 :             myStopEdges.push_back(net->getEdge(*it));
      71              :         }
      72              :     }
      73       297790 : }
      74              : 
      75              : 
      76              : void
      77          777 : ROVehicle::addStop(const SUMOVehicleParameter::Stop& stopPar, const RONet* net, MsgHandler* errorHandler) {
      78          777 :     const ROEdge* stopEdge = net->getEdge(stopPar.edge);
      79              :     assert(stopEdge != 0); // was checked when parsing the stop
      80          777 :     if (stopEdge->prohibits(this)) {
      81            0 :         if (errorHandler != nullptr) {
      82            0 :             errorHandler->inform("Stop edge '" + stopEdge->getID() + "' does not allow vehicle '" + getID() + "'.");
      83              :         }
      84            0 :         return;
      85              :     }
      86              :     // where to insert the stop
      87              :     StopParVector::iterator iter = getParameter().stops.begin();
      88              :     ConstROEdgeVector::iterator edgeIter = myStopEdges.begin();
      89          777 :     if (stopPar.index == STOP_INDEX_END || stopPar.index >= static_cast<int>(getParameter().stops.size())) {
      90          777 :         if (getParameter().stops.size() > 0) {
      91              :             iter = getParameter().stops.end();
      92              :             edgeIter = myStopEdges.end();
      93              :         }
      94              :     } else {
      95            0 :         if (stopPar.index == STOP_INDEX_FIT) {
      96            0 :             const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
      97            0 :             ConstROEdgeVector::const_iterator stopEdgeIt = std::find(edges.begin(), edges.end(), stopEdge);
      98            0 :             if (stopEdgeIt == edges.end()) {
      99              :                 iter = getParameter().stops.end();
     100              :                 edgeIter = myStopEdges.end();
     101              :             } else {
     102            0 :                 while (iter != getParameter().stops.end()) {
     103            0 :                     if (edgeIter > stopEdgeIt || (edgeIter == stopEdgeIt && iter->endPos >= stopPar.endPos)) {
     104              :                         break;
     105              :                     }
     106              :                     ++iter;
     107              :                     ++edgeIter;
     108              :                 }
     109              :             }
     110            0 :         } else {
     111              :             iter += stopPar.index;
     112              :             edgeIter += stopPar.index;
     113              :         }
     114              :     }
     115          777 :     getParameter().stops.insert(iter, stopPar);
     116          777 :     myStopEdges.insert(edgeIter, stopEdge);
     117          777 :     if (stopPar.jump >= 0) {
     118           24 :         if (stopEdge->isInternal()) {
     119            0 :             if (errorHandler != nullptr) {
     120            0 :                 errorHandler->inform("Jumps are not supported from internal stop edge '" + stopEdge->getID() + "'.");
     121              :             }
     122              :         } else {
     123           24 :             if (myJumpTime < 0) {
     124           20 :                 myJumpTime = 0;
     125              :             }
     126           24 :             myJumpTime += stopPar.jump;
     127              :         }
     128              :     }
     129              : }
     130              : 
     131              : 
     132       595493 : ROVehicle::~ROVehicle() {}
     133              : 
     134              : 
     135              : const ROEdge*
     136          511 : ROVehicle:: getDepartEdge() const {
     137          511 :     return myRoute->getFirstRoute()->getFirst();
     138              : }
     139              : 
     140              : 
     141              : void
     142       296649 : ROVehicle::computeRoute(const RORouterProvider& provider,
     143              :                         const bool removeLoops, MsgHandler* errorHandler) {
     144              :     SUMOAbstractRouter<ROEdge, ROVehicle>& router = provider.getVehicleRouter(getVClass());
     145       593298 :     std::string noRouteMsg = "The vehicle '" + getID() + "' has no valid route.";
     146              :     RORouteDef* const routeDef = getRouteDefinition();
     147              :     // check if the route definition is valid
     148       296649 :     if (routeDef == nullptr) {
     149            0 :         errorHandler->inform(noRouteMsg);
     150            0 :         myRoutingSuccess = false;
     151            0 :         return;
     152              :     }
     153       296649 :     routeDef->validateAlternatives(this, errorHandler);
     154       296646 :     if (routeDef->getFirstRoute() == nullptr) {
     155              :         // everything is invalid and no new routes will be computed
     156            3 :         myRoutingSuccess = false;
     157            3 :         return;
     158              :     }
     159       296646 :     RORoute* current = routeDef->buildCurrentRoute(router, getDepartureTime(), *this);
     160       296646 :     if (current == nullptr || current->size() == 0) {
     161        19117 :         delete current;
     162         1925 :         if (current == nullptr || !routeDef->discardSilent()) {
     163        51576 :             errorHandler->inform(noRouteMsg);
     164              :         }
     165        19117 :         myRoutingSuccess = false;
     166        19117 :         return;
     167              :     }
     168              :     // check whether we have to evaluate the route for not containing loops
     169       277529 :     if (removeLoops) {
     170         5218 :         const ROEdge* requiredStart = (getParameter().departPosProcedure == DepartPosDefinition::GIVEN
     171         5218 :                                        || getParameter().departLaneProcedure == DepartLaneDefinition::GIVEN ? current->getEdgeVector().front() : 0);
     172         5218 :         const ROEdge* requiredEnd = (getParameter().arrivalPosProcedure == ArrivalPosDefinition::GIVEN
     173         5218 :                                      || getParameter().arrivalLaneProcedure == ArrivalLaneDefinition::GIVEN ? current->getEdgeVector().back() : 0);
     174         5218 :         current->recheckForLoops(getMandatoryEdges(requiredStart, requiredEnd));
     175              :         // check whether the route is still valid
     176         5218 :         if (current->size() == 0) {
     177            0 :             delete current;
     178           16 :             errorHandler->inform(noRouteMsg + " (after removing loops)");
     179            0 :             myRoutingSuccess = false;
     180            0 :             return;
     181              :         }
     182              :     }
     183              :     // add built route
     184       277529 :     routeDef->addAlternative(router, this, current, getDepartureTime(), errorHandler);
     185       277513 :     myRoutingSuccess = true;
     186              : }
     187              : 
     188              : 
     189              : ConstROEdgeVector
     190       294441 : ROVehicle::getMandatoryEdges(const ROEdge* requiredStart, const ROEdge* requiredEnd) const {
     191              :     ConstROEdgeVector mandatory;
     192       294441 :     if (requiredStart) {
     193       289231 :         mandatory.push_back(requiredStart);
     194              :     }
     195       328682 :     for (const ROEdge* e : getStopEdges()) {
     196        34241 :         if (e->isInternal()) {
     197              :             // the edges before and after the internal edge are mandatory
     198            8 :             const ROEdge* before = e->getNormalBefore();
     199            8 :             const ROEdge* after = e->getNormalAfter();
     200            8 :             if (mandatory.size() == 0 || after != mandatory.back()) {
     201            8 :                 mandatory.push_back(before);
     202            8 :                 mandatory.push_back(after);
     203              :             }
     204              :         } else {
     205        34233 :             if (mandatory.size() == 0 || e != mandatory.back()) {
     206        28447 :                 mandatory.push_back(e);
     207              :             }
     208              :         }
     209              :     }
     210       294441 :     if (requiredEnd) {
     211       289231 :         if (mandatory.size() < 2 || mandatory.back() != requiredEnd) {
     212       287549 :             mandatory.push_back(requiredEnd);
     213              :         }
     214              :     }
     215       294441 :     return mandatory;
     216            0 : }
     217              : 
     218              : 
     219              : void
     220       289223 : ROVehicle::collectJumps(const ConstROEdgeVector& mandatory, std::set<ConstROEdgeVector::const_iterator>& jumpStarts) const {
     221       289223 :     auto itM = mandatory.begin();
     222              :     auto itS = getParameter().stops.begin();
     223              :     auto itSEnd = getParameter().stops.end();
     224       290106 :     while (itM != mandatory.end() && itS != itSEnd) {
     225              :         bool repeatMandatory = false;
     226              :         // if we stop twice on the same edge, we must treat this as a repeated
     227              :         // mandatory edge (even though the edge appears only once in the mandatory vector)
     228          883 :         if ((*itM)->getID() == itS->edge) {
     229          621 :             if (itS->jump >= 0) {
     230              :                 jumpStarts.insert(itM);
     231              :             }
     232              :             itS++;
     233          621 :             if (itS != itSEnd && itS->edge == (itS - 1)->edge) {
     234              :                 repeatMandatory = true;
     235              :             }
     236              :         }
     237              :         if (!repeatMandatory) {
     238              :             itM++;
     239              :         }
     240              :     }
     241       289223 : }
     242              : 
     243              : 
     244              : void
     245       390534 : ROVehicle::saveAsXML(OutputDevice& os, OutputDevice* const typeos, bool asAlternatives, OptionsCont& options, int cloneIndex) const {
     246       390534 :     if (typeos != nullptr && getType() != nullptr && !getType()->saved) {
     247          378 :         getType()->write(*typeos);
     248          378 :         getType()->saved = true;
     249              :     }
     250       390534 :     if (getType() != nullptr && !getType()->saved) {
     251         3996 :         getType()->write(os);
     252         3996 :         getType()->saved = asAlternatives;
     253              :     }
     254              : 
     255       776801 :     const bool writeTrip = options.exists("write-trips") && options.getBool("write-trips");
     256       390534 :     const bool writeGeoTrip = writeTrip && options.getBool("write-trips.geo");
     257       390534 :     const bool writeJunctions = writeTrip && options.getBool("write-trips.junctions");
     258       390534 :     const bool writeNamedRoute = !asAlternatives && options.getBool("named-routes");
     259       776801 :     const bool writeCosts = options.exists("write-costs") && options.getBool("write-costs");
     260       781068 :     const bool writeExit = options.exists("exit-times") && options.getBool("exit-times");
     261       776801 :     const bool writeLength = options.exists("route-length") && options.getBool("route-length");
     262       776801 :     const bool writeFlow = options.exists("keep-flows") && options.getBool("keep-flows") && isPartOfFlow();
     263              : 
     264              :     std::string routeID;
     265       390534 :     if (writeNamedRoute) {
     266           21 :         ConstROEdgeVector edges = myRoute->getUsedRoute()->getNormalEdges();
     267              :         auto it = mySavedRoutes.find(edges);
     268           21 :         if (it == mySavedRoutes.end()) {
     269           14 :             routeID = "r" + toString(mySavedRoutes.size());
     270            7 :             myRoute->getUsedRoute()->writeXMLDefinition(os, this, writeCosts, false, writeExit,
     271              :                     writeLength, routeID);
     272            7 :             mySavedRoutes[edges] = routeID;
     273              :         } else {
     274           14 :             routeID = it->second;
     275              :         }
     276           21 :     }
     277       390534 :     const SumoXMLTag tag = writeFlow ? SUMO_TAG_FLOW : (writeTrip ? SUMO_TAG_TRIP : SUMO_TAG_VEHICLE);
     278              :     // write the vehicle (new style, with included routes)
     279       390534 :     if (cloneIndex == 0) {
     280       780828 :         getParameter().write(os, options, tag);
     281              :     } else {
     282          120 :         SUMOVehicleParameter p = getParameter();
     283              :         // @note id collisions may occur if scale-suffic occurs in other vehicle ids
     284          240 :         p.id += options.getString("scale-suffix") + toString(cloneIndex);
     285          120 :         p.write(os, options, tag);
     286          120 :     }
     287              :     // save the route
     288       390534 :     if (writeTrip) {
     289       158652 :         const ConstROEdgeVector edges = myRoute->getFirstRoute()->getEdgeVector();
     290              :         const ROEdge* from = nullptr;
     291              :         const ROEdge* to = nullptr;
     292        79326 :         if (edges.size() > 0) {
     293        79326 :             if (edges.front()->isTazConnector()) {
     294         7284 :                 if (edges.size() > 1) {
     295         7284 :                     from = edges[1];
     296         7284 :                     if (from->isTazConnector() && writeJunctions && edges.front()->getSuccessors().size() > 0) {
     297              :                         // routing was skipped
     298            8 :                         from = edges.front()->getSuccessors(getVClass()).front();
     299              :                     }
     300              :                 }
     301              :             } else {
     302              :                 from = edges[0];
     303              :             }
     304        79326 :             if (edges.back()->isTazConnector()) {
     305         7284 :                 if (edges.size() > 1) {
     306         7284 :                     to = edges[edges.size() - 2];
     307         7284 :                     if (to->isTazConnector() && writeJunctions && edges.back()->getPredecessors().size() > 0) {
     308              :                         // routing was skipped
     309            4 :                         to = edges.back()->getPredecessors().front();
     310              :                     }
     311              :                 }
     312              :             } else {
     313        72042 :                 to = edges[edges.size() - 1];
     314              :             }
     315              :         }
     316        79326 :         if (from != nullptr) {
     317        79326 :             if (writeGeoTrip) {
     318           16 :                 Position fromPos = from->getLanes()[0]->getShape().positionAtOffset2D(0);
     319           16 :                 if (GeoConvHelper::getFinal().usingGeoProjection()) {
     320            0 :                     os.setPrecision(gPrecisionGeo);
     321            0 :                     GeoConvHelper::getFinal().cartesian2geo(fromPos);
     322            0 :                     os.writeAttr(SUMO_ATTR_FROMLONLAT, fromPos);
     323            0 :                     os.setPrecision(gPrecision);
     324              :                 } else {
     325           16 :                     os.writeAttr(SUMO_ATTR_FROMXY, fromPos);
     326              :                 }
     327        79310 :             } else if (writeJunctions) {
     328         7244 :                 os.writeAttr(SUMO_ATTR_FROM_JUNCTION, from->getFromJunction()->getID());
     329              :             } else {
     330        72066 :                 os.writeAttr(SUMO_ATTR_FROM, from->getID());
     331              :             }
     332              :         }
     333        79326 :         if (to != nullptr) {
     334        79326 :             if (writeGeoTrip) {
     335           16 :                 Position toPos = to->getLanes()[0]->getShape().positionAtOffset2D(to->getLanes()[0]->getShape().length2D());
     336           16 :                 if (GeoConvHelper::getFinal().usingGeoProjection()) {
     337            0 :                     os.setPrecision(gPrecisionGeo);
     338            0 :                     GeoConvHelper::getFinal().cartesian2geo(toPos);
     339            0 :                     os.writeAttr(SUMO_ATTR_TOLONLAT, toPos);
     340            0 :                     os.setPrecision(gPrecision);
     341              :                 } else {
     342           16 :                     os.writeAttr(SUMO_ATTR_TOXY, toPos);
     343              :                 }
     344        79310 :             } else if (writeJunctions) {
     345         7244 :                 os.writeAttr(SUMO_ATTR_TO_JUNCTION, to->getToJunction()->getID());
     346              :             } else {
     347        72066 :                 os.writeAttr(SUMO_ATTR_TO, to->getID());
     348              :             }
     349              :         }
     350        79326 :         if (getParameter().via.size() > 0) {
     351              :             std::vector<std::string> viaOut;
     352              :             SumoXMLAttr viaAttr = (writeGeoTrip
     353         7236 :                                    ? (GeoConvHelper::getFinal().usingGeoProjection() ? SUMO_ATTR_VIALONLAT : SUMO_ATTR_VIAXY)
     354         7228 :                                    : (writeJunctions ? SUMO_ATTR_VIAJUNCTIONS : SUMO_ATTR_VIA));
     355        21672 :             for (const std::string& viaID : getParameter().via) {
     356        14436 :                 const ROEdge* viaEdge = RONet::getInstance()->getEdge(viaID);
     357        14436 :                 if (viaEdge->isTazConnector()) {
     358           20 :                     if (viaEdge->getPredecessors().size() == 0) {
     359            0 :                         continue;
     360              :                     }
     361              :                     // XXX used edge that was used in route
     362           20 :                     viaEdge = viaEdge->getPredecessors().front();
     363              :                 }
     364              :                 assert(viaEdge != nullptr);
     365        14436 :                 if (writeGeoTrip) {
     366            8 :                     Position viaPos = viaEdge->getLanes()[0]->getShape().positionAtOffset2D(viaEdge->getLanes()[0]->getShape().length2D() / 2);
     367            8 :                     if (GeoConvHelper::getFinal().usingGeoProjection()) {
     368            0 :                         GeoConvHelper::getFinal().cartesian2geo(viaPos);
     369            0 :                         viaOut.push_back(toString(viaPos, gPrecisionGeo));
     370              :                     } else {
     371           16 :                         viaOut.push_back(toString(viaPos, gPrecision));
     372              :                     }
     373        14428 :                 } else if (writeJunctions) {
     374            8 :                     viaOut.push_back(viaEdge->getToJunction()->getID());
     375              :                 } else {
     376        14420 :                     viaOut.push_back(viaEdge->getID());
     377              :                 }
     378              :             }
     379         7236 :             os.writeAttr(viaAttr, viaOut);
     380         7236 :         }
     381       390534 :     } else if (writeNamedRoute) {
     382           21 :         os.writeAttr(SUMO_ATTR_ROUTE, routeID);
     383              :     } else {
     384       311187 :         myRoute->writeXMLDefinition(os, this, asAlternatives, writeExit, writeCosts, writeLength);
     385              :     }
     386       391780 :     for (StopParVector::const_iterator stop = getParameter().stops.begin(); stop != getParameter().stops.end(); ++stop) {
     387         1246 :         stop->write(os);
     388              :     }
     389       390534 :     getParameter().writeParams(os);
     390       781068 :     os.closeTag();
     391       390534 : }
     392              : 
     393              : 
     394              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1