LCOV - code coverage report
Current view: top level - src/router - RONet.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 91.0 % 487 443
Test Date: 2025-11-13 15:38:19 Functions: 100.0 % 45 45

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    RONet.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @date    Sept 2002
      19              : ///
      20              : // The router's network representation
      21              : /****************************************************************************/
      22              : #include <config.h>
      23              : 
      24              : #include <algorithm>
      25              : #include <utils/router/RouteCostCalculator.h>
      26              : #include <utils/vehicle/SUMOVTypeParameter.h>
      27              : #include <utils/router/SUMOAbstractRouter.h>
      28              : #include <utils/options/OptionsCont.h>
      29              : #include <utils/common/UtilExceptions.h>
      30              : #include <utils/common/ToString.h>
      31              : #include <utils/common/StringUtils.h>
      32              : #include <utils/common/RandHelper.h>
      33              : #include <utils/common/SUMOVehicleClass.h>
      34              : #include <utils/iodevices/OutputDevice.h>
      35              : #include "ROEdge.h"
      36              : #include "ROLane.h"
      37              : #include "RONode.h"
      38              : #include "ROPerson.h"
      39              : #include "RORoute.h"
      40              : #include "RORouteDef.h"
      41              : #include "ROVehicle.h"
      42              : #include "ROAbstractEdgeBuilder.h"
      43              : #include "RONet.h"
      44              : 
      45              : 
      46              : // ===========================================================================
      47              : // static member definitions
      48              : // ===========================================================================
      49              : RONet* RONet::myInstance = nullptr;
      50              : 
      51              : 
      52              : // ===========================================================================
      53              : // method definitions
      54              : // ===========================================================================
      55              : RONet*
      56      4443078 : RONet::getInstance(void) {
      57      4443078 :     if (myInstance != nullptr) {
      58      4443078 :         return myInstance;
      59              :     }
      60            0 :     throw ProcessError(TL("A network was not yet constructed."));
      61              : }
      62              : 
      63              : 
      64         4241 : RONet::RONet() :
      65         4241 :     myVehicleTypes(), myDefaultVTypeMayBeDeleted(true),
      66         4241 :     myDefaultPedTypeMayBeDeleted(true),
      67         4241 :     myDefaultBikeTypeMayBeDeleted(true),
      68         4241 :     myDefaultTaxiTypeMayBeDeleted(true),
      69         4241 :     myDefaultRailTypeMayBeDeleted(true),
      70         4241 :     myHaveActiveFlows(true),
      71         4241 :     myRoutesOutput(nullptr), myRouteAlternativesOutput(nullptr), myTypesOutput(nullptr),
      72         4241 :     myReadRouteNo(0), myDiscardedRouteNo(0), myWrittenRouteNo(0),
      73         4241 :     myHavePermissions(false),
      74         4241 :     myNumInternalEdges(0),
      75         4241 :     myErrorHandler(OptionsCont::getOptions().exists("ignore-errors")
      76         8035 :                    && OptionsCont::getOptions().getBool("ignore-errors") ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
      77         4241 :     myKeepVTypeDist(OptionsCont::getOptions().exists("keep-vtype-distributions")
      78         8035 :                     && OptionsCont::getOptions().getBool("keep-vtype-distributions")),
      79         4241 :     myDoPTRouting(!OptionsCont::getOptions().exists("ptline-routing")
      80         7575 :                   || OptionsCont::getOptions().getBool("ptline-routing")),
      81         4241 :     myKeepFlows(OptionsCont::getOptions().exists("keep-flows")
      82         7575 :                   && OptionsCont::getOptions().getBool("keep-flows")),
      83         8482 :     myHasBidiEdges(false) {
      84         4241 :     if (myInstance != nullptr) {
      85            0 :         throw ProcessError(TL("A network was already constructed."));
      86              :     }
      87         4241 :     SUMOVTypeParameter* type = new SUMOVTypeParameter(DEFAULT_VTYPE_ID, SVC_PASSENGER);
      88         4241 :     type->onlyReferenced = true;
      89         4241 :     myVehicleTypes.add(type->id, type);
      90              : 
      91         4241 :     SUMOVTypeParameter* defPedType = new SUMOVTypeParameter(DEFAULT_PEDTYPE_ID, SVC_PEDESTRIAN);
      92         4241 :     defPedType->onlyReferenced = true;
      93         4241 :     defPedType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
      94         4241 :     myVehicleTypes.add(defPedType->id, defPedType);
      95              : 
      96         4241 :     SUMOVTypeParameter* defBikeType = new SUMOVTypeParameter(DEFAULT_BIKETYPE_ID, SVC_BICYCLE);
      97         4241 :     defBikeType->onlyReferenced = true;
      98         4241 :     defBikeType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
      99         4241 :     myVehicleTypes.add(defBikeType->id, defBikeType);
     100              : 
     101         4241 :     SUMOVTypeParameter* defTaxiType = new SUMOVTypeParameter(DEFAULT_TAXITYPE_ID, SVC_TAXI);
     102         4241 :     defTaxiType->onlyReferenced = true;
     103         4241 :     defTaxiType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
     104         4241 :     myVehicleTypes.add(defTaxiType->id, defTaxiType);
     105              : 
     106         4241 :     SUMOVTypeParameter* defRailType = new SUMOVTypeParameter(DEFAULT_RAILTYPE_ID, SVC_RAIL);
     107         4241 :     defRailType->onlyReferenced = true;
     108         4241 :     defRailType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
     109         4241 :     myVehicleTypes.add(defRailType->id, defRailType);
     110              : 
     111         4241 :     myInstance = this;
     112         4241 : }
     113              : 
     114              : 
     115         8025 : RONet::~RONet() {
     116         4440 :     for (const auto& routables : myRoutables) {
     117          418 :         for (RORoutable* const r : routables.second) {
     118          209 :             const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
     119              :             // delete routes and the vehicle
     120          209 :             if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
     121          200 :                 if (!myRoutes.remove(veh->getRouteDefinition()->getID())) {
     122           76 :                     delete veh->getRouteDefinition();
     123              :                 }
     124              :             }
     125          209 :             delete r;
     126              :         }
     127              :     }
     128         4267 :     for (const RORoutable* const r : myPTVehicles) {
     129           36 :         const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
     130              :         // delete routes and the vehicle
     131           36 :         if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
     132           36 :             if (!myRoutes.remove(veh->getRouteDefinition()->getID())) {
     133            8 :                 delete veh->getRouteDefinition();
     134              :             }
     135              :         }
     136           36 :         delete r;
     137              :     }
     138              :     myRoutables.clear();
     139         4275 :     for (const auto& vTypeDist : myVTypeDistDict) {
     140           88 :         delete vTypeDist.second;
     141              :     }
     142        29180 : }
     143              : 
     144              : 
     145              : void
     146           17 : RONet::addRestriction(const std::string& id, const SUMOVehicleClass svc, const double speed) {
     147           17 :     myRestrictions[id][svc] = speed;
     148           17 : }
     149              : 
     150              : 
     151              : double
     152         1286 : RONet::getPreference(const std::string& routingType, const SUMOVTypeParameter& pars) const {
     153         1286 :     if (gRoutingPreferences) {
     154         1286 :         auto it = myVTypePreferences.find(pars.id);
     155         1286 :         if (it != myVTypePreferences.end()) {
     156              :             auto it2 = it->second.find(routingType);
     157           86 :             if (it2 != it->second.end()) {
     158           22 :                 return it2->second;
     159              :             }
     160              :         }
     161              :         auto it3 = myVClassPreferences.find(pars.vehicleClass);
     162         1264 :         if (it3 != myVClassPreferences.end()) {
     163              :             auto it4 = it3->second.find(routingType);
     164          176 :             if (it4 != it3->second.end()) {
     165           47 :                 return it4->second;
     166              :             }
     167              :         }
     168              :         // fallback to generel preferences
     169         1217 :         it = myVTypePreferences.find("");
     170         1217 :         if (it != myVTypePreferences.end()) {
     171              :             auto it2 = it->second.find(routingType);
     172          774 :             if (it2 != it->second.end()) {
     173          140 :                 return it2->second;
     174              :             }
     175              :         }
     176              :     }
     177              :     return 1;
     178              : }
     179              : 
     180              : 
     181              : void
     182           24 : RONet::addPreference(const std::string& routingType, SUMOVehicleClass svc, double prio) {
     183           24 :     myVClassPreferences[svc][routingType] = prio;
     184           24 :     gRoutingPreferences = true;
     185           24 : }
     186              : 
     187              : 
     188              : void
     189           32 : RONet::addPreference(const std::string& routingType, std::string vType, double prio) {
     190           32 :     myVTypePreferences[vType][routingType] = prio;
     191           32 :     gRoutingPreferences = true;
     192           32 : }
     193              : 
     194              : 
     195              : 
     196              : const std::map<SUMOVehicleClass, double>*
     197           66 : RONet::getRestrictions(const std::string& id) const {
     198              :     std::map<std::string, std::map<SUMOVehicleClass, double> >::const_iterator i = myRestrictions.find(id);
     199           66 :     if (i == myRestrictions.end()) {
     200              :         return nullptr;
     201              :     }
     202           26 :     return &i->second;
     203              : }
     204              : 
     205              : 
     206              : bool
     207       327129 : RONet::addEdge(ROEdge* edge) {
     208       327129 :     if (!myEdges.add(edge->getID(), edge)) {
     209           36 :         WRITE_ERRORF(TL("The edge '%' occurs at least twice."), edge->getID());
     210           12 :         delete edge;
     211           12 :         return false;
     212              :     }
     213       327117 :     if (edge->isInternal()) {
     214       159862 :         myNumInternalEdges += 1;
     215              :     }
     216              :     return true;
     217              : }
     218              : 
     219              : 
     220              : bool
     221         1816 : RONet::addDistrict(const std::string id, ROEdge* source, ROEdge* sink) {
     222              :     if (myDistricts.count(id) > 0) {
     223            0 :         WRITE_ERRORF(TL("The TAZ '%' occurs at least twice."), id);
     224            0 :         delete source;
     225            0 :         delete sink;
     226            0 :         return false;
     227              :     }
     228              :     sink->setFunction(SumoXMLEdgeFunc::CONNECTOR);
     229         1816 :     if (!addEdge(sink)) {
     230              :         return false;
     231              :     }
     232              :     source->setFunction(SumoXMLEdgeFunc::CONNECTOR);
     233         1816 :     if (!addEdge(source)) {
     234              :         return false;
     235              :     }
     236              :     sink->setOtherTazConnector(source);
     237              :     source->setOtherTazConnector(sink);
     238         3632 :     myDistricts[id] = std::make_pair(std::vector<std::string>(), std::vector<std::string>());
     239         1816 :     return true;
     240              : }
     241              : 
     242              : 
     243              : bool
     244         1379 : RONet::addDistrictEdge(const std::string tazID, const std::string edgeID, const bool isSource) {
     245              :     if (myDistricts.count(tazID) == 0) {
     246            0 :         WRITE_ERRORF(TL("The TAZ '%' is unknown."), tazID);
     247            0 :         return false;
     248              :     }
     249              :     ROEdge* edge = getEdge(edgeID);
     250         1379 :     if (edge == nullptr) {
     251            0 :         WRITE_ERRORF(TL("The edge '%' for TAZ '%' is unknown."), edgeID, tazID);
     252            0 :         return false;
     253              :     }
     254         1379 :     if (isSource) {
     255         2067 :         getEdge(tazID + "-source")->addSuccessor(edge);
     256          689 :         myDistricts[tazID].first.push_back(edgeID);
     257              :     } else {
     258         2070 :         edge->addSuccessor(getEdge(tazID + "-sink"));
     259          690 :         myDistricts[tazID].second.push_back(edgeID);
     260              :     }
     261              :     return true;
     262              : }
     263              : 
     264              : 
     265              : void
     266          101 : RONet::addJunctionTaz(ROAbstractEdgeBuilder& eb) {
     267         1544 :     for (auto item : myNodes) {
     268              :         const std::string tazID = item.first;
     269            8 :         if (myDistricts.count(tazID) != 0) {
     270           24 :             WRITE_WARNINGF(TL("A TAZ with id '%' already exists. Not building junction TAZ."), tazID);
     271            8 :             continue;
     272              :         }
     273         1435 :         const std::string sourceID = tazID + "-source";
     274         1435 :         const std::string sinkID = tazID + "-sink";
     275              :         // sink must be added before source
     276         2870 :         ROEdge* sink = eb.buildEdge(sinkID, nullptr, nullptr, 0, "", "");
     277         2870 :         ROEdge* source = eb.buildEdge(sourceID, nullptr, nullptr, 0, "", "");
     278              :         sink->setOtherTazConnector(source);
     279              :         source->setOtherTazConnector(sink);
     280         2870 :         if (!addDistrict(tazID, source, sink)) {
     281              :             continue;
     282              :         }
     283         1435 :         auto& district = myDistricts[tazID];
     284         1435 :         const RONode* junction = item.second;
     285        12452 :         for (const ROEdge* edge : junction->getIncoming()) {
     286        11017 :             if (!edge->isInternal()) {
     287         3129 :                 const_cast<ROEdge*>(edge)->addSuccessor(sink);
     288         3129 :                 district.second.push_back(edge->getID());
     289              :             }
     290              :         }
     291        12452 :         for (const ROEdge* edge : junction->getOutgoing()) {
     292        11017 :             if (!edge->isInternal()) {
     293         3129 :                 source->addSuccessor(const_cast<ROEdge*>(edge));
     294         3129 :                 district.first.push_back(edge->getID());
     295              :             }
     296              :         }
     297              :     }
     298          101 : }
     299              : 
     300              : 
     301              : void
     302         3510 : RONet::setBidiEdges(const std::map<ROEdge*, std::string>& bidiMap) {
     303        15594 :     for (const auto& item : bidiMap) {
     304        12084 :         ROEdge* bidi = myEdges.get(item.second);
     305        12084 :         if (bidi == nullptr) {
     306            0 :             WRITE_ERRORF(TL("The bidi edge '%' is not known."), item.second);
     307              :         }
     308        12084 :         item.first->setBidiEdge(bidi);
     309        12084 :         myHasBidiEdges = true;
     310              :     }
     311         3510 : }
     312              : 
     313              : 
     314              : void
     315        73144 : RONet::addNode(RONode* node) {
     316        73144 :     if (!myNodes.add(node->getID(), node)) {
     317            0 :         WRITE_ERRORF(TL("The node '%' occurs at least twice."), node->getID());
     318            0 :         delete node;
     319              :     }
     320        73144 : }
     321              : 
     322              : 
     323              : void
     324         1968 : RONet::addStoppingPlace(const std::string& id, const SumoXMLTag category, SUMOVehicleParameter::Stop* stop) {
     325         3816 :     if (!myStoppingPlaces[category == SUMO_TAG_TRAIN_STOP ? SUMO_TAG_BUS_STOP : category].add(id, stop)) {
     326           12 :         WRITE_ERRORF(TL("The % '%' occurs at least twice."), toString(category), id);
     327            4 :         delete stop;
     328              :     }
     329         1968 : }
     330              : 
     331              : 
     332              : bool
     333       280934 : RONet::addRouteDef(RORouteDef* def) {
     334       280934 :     return myRoutes.add(def->getID(), def);
     335              : }
     336              : 
     337              : 
     338              : void
     339         2815 : RONet::openOutput(const OptionsCont& options) {
     340         8443 :     if (options.isSet("output-file") && options.getString("output-file") != "") {
     341         2814 :         myRoutesOutput = &OutputDevice::getDevice(options.getString("output-file"));
     342         2808 :         if (myRoutesOutput->isNull()) {
     343            1 :             myRoutesOutput = nullptr;
     344              :         } else {
     345         5614 :             myRoutesOutput->writeXMLHeader("routes", "routes_file.xsd");
     346              :         }
     347              :     }
     348         8247 :     if (options.exists("alternatives-output") && options.isSet("alternatives-output")
     349        13129 :             && !(options.exists("write-trips") && options.getBool("write-trips"))) {
     350         2435 :         myRouteAlternativesOutput = &OutputDevice::getDevice(options.getString("alternatives-output"));
     351         2435 :         if (myRouteAlternativesOutput->isNull()) {
     352          132 :             myRouteAlternativesOutput = nullptr;
     353              :         } else {
     354         4606 :             myRouteAlternativesOutput->writeXMLHeader("routes", "routes_file.xsd");
     355              :         }
     356              :     }
     357         5618 :     if (options.isSet("vtype-output")) {
     358           32 :         myTypesOutput = &OutputDevice::getDevice(options.getString("vtype-output"));
     359           64 :         myTypesOutput->writeXMLHeader("routes", "routes_file.xsd");
     360              :     }
     361         2809 : }
     362              : 
     363              : 
     364              : void
     365         2597 : RONet::writeIntermodal(const OptionsCont& options, ROIntermodalRouter& router) const {
     366         5194 :     if (options.exists("intermodal-network-output") && options.isSet("intermodal-network-output")) {
     367           32 :         OutputDevice::createDeviceByOption("intermodal-network-output", "intermodal");
     368           32 :         router.writeNetwork(OutputDevice::getDevice(options.getString("intermodal-network-output")));
     369              :     }
     370         5194 :     if (options.exists("intermodal-weight-output") && options.isSet("intermodal-weight-output")) {
     371           16 :         OutputDevice::createDeviceByOption("intermodal-weight-output", "weights", "meandata_file.xsd");
     372            8 :         OutputDevice& dev = OutputDevice::getDeviceByOption("intermodal-weight-output");
     373            8 :         dev.openTag(SUMO_TAG_INTERVAL);
     374            8 :         dev.writeAttr(SUMO_ATTR_ID, "intermodalweights");
     375            8 :         dev.writeAttr(SUMO_ATTR_BEGIN, 0);
     376            8 :         dev.writeAttr(SUMO_ATTR_END, SUMOTime_MAX);
     377            8 :         router.writeWeights(dev);
     378           16 :         dev.closeTag();
     379              :     }
     380         2597 : }
     381              : 
     382              : 
     383              : void
     384         2814 : RONet::cleanup() {
     385              :     // end writing
     386         2814 :     if (myRoutesOutput != nullptr) {
     387         2807 :         myRoutesOutput->close();
     388              :     }
     389              :     // only if opened
     390         2814 :     if (myRouteAlternativesOutput != nullptr) {
     391         2303 :         myRouteAlternativesOutput->close();
     392              :     }
     393              :     // only if opened
     394         2814 :     if (myTypesOutput != nullptr) {
     395           32 :         myTypesOutput->close();
     396              :     }
     397              :     RouteCostCalculator<RORoute, ROEdge, ROVehicle>::cleanup();
     398              : #ifdef HAVE_FOX
     399         2814 :     if (myThreadPool.size() > 0) {
     400           30 :         myThreadPool.clear();
     401              :     }
     402              : #endif
     403         2814 : }
     404              : 
     405              : 
     406              : 
     407              : SUMOVTypeParameter*
     408       495714 : RONet::getVehicleTypeSecure(const std::string& id) {
     409              :     // check whether the type was already known
     410              :     SUMOVTypeParameter* type = myVehicleTypes.get(id);
     411       495714 :     if (id == DEFAULT_VTYPE_ID) {
     412       455098 :         myDefaultVTypeMayBeDeleted = false;
     413        40616 :     } else if (id == DEFAULT_PEDTYPE_ID) {
     414         5324 :         myDefaultPedTypeMayBeDeleted = false;
     415        35292 :     } else if (id == DEFAULT_BIKETYPE_ID) {
     416           53 :         myDefaultBikeTypeMayBeDeleted = false;
     417        35239 :     } else if (id == DEFAULT_TAXITYPE_ID) {
     418           93 :         myDefaultTaxiTypeMayBeDeleted = false;
     419        35146 :     } else if (id == DEFAULT_RAILTYPE_ID) {
     420            0 :         myDefaultRailTypeMayBeDeleted = false;
     421              :     }
     422       495714 :     if (type != nullptr) {
     423              :         return type;
     424              :     }
     425              :     VTypeDistDictType::iterator it2 = myVTypeDistDict.find(id);
     426         1478 :     if (it2 != myVTypeDistDict.end()) {
     427         1148 :         return it2->second->get();
     428              :     }
     429          330 :     if (id == "") {
     430              :         // ok, no vehicle type or an unknown type was given within the user input
     431              :         //  return the default type
     432            0 :         myDefaultVTypeMayBeDeleted = false;
     433              :         return myVehicleTypes.get(DEFAULT_VTYPE_ID);
     434              :     }
     435              :     return type;
     436              : }
     437              : 
     438              : 
     439              : bool
     440         2094 : RONet::checkVType(const std::string& id) {
     441         2094 :     if (id == DEFAULT_VTYPE_ID) {
     442           65 :         if (myDefaultVTypeMayBeDeleted) {
     443           65 :             myVehicleTypes.remove(id);
     444           65 :             myDefaultVTypeMayBeDeleted = false;
     445              :         } else {
     446              :             return false;
     447              :         }
     448         2029 :     } else if (id == DEFAULT_PEDTYPE_ID) {
     449            0 :         if (myDefaultPedTypeMayBeDeleted) {
     450            0 :             myVehicleTypes.remove(id);
     451            0 :             myDefaultPedTypeMayBeDeleted = false;
     452              :         } else {
     453              :             return false;
     454              :         }
     455         2029 :     } else if (id == DEFAULT_BIKETYPE_ID) {
     456            0 :         if (myDefaultBikeTypeMayBeDeleted) {
     457            0 :             myVehicleTypes.remove(id);
     458            0 :             myDefaultBikeTypeMayBeDeleted = false;
     459              :         } else {
     460              :             return false;
     461              :         }
     462         2029 :     } else if (id == DEFAULT_TAXITYPE_ID) {
     463            0 :         if (myDefaultTaxiTypeMayBeDeleted) {
     464            0 :             myVehicleTypes.remove(id);
     465            0 :             myDefaultTaxiTypeMayBeDeleted = false;
     466              :         } else {
     467              :             return false;
     468              :         }
     469         2029 :     } else if (id == DEFAULT_RAILTYPE_ID) {
     470            0 :         if (myDefaultRailTypeMayBeDeleted) {
     471            0 :             myVehicleTypes.remove(id);
     472            0 :             myDefaultRailTypeMayBeDeleted = false;
     473              :         } else {
     474              :             return false;
     475              :         }
     476              :     } else {
     477         2029 :         if (myVehicleTypes.get(id) != 0 || myVTypeDistDict.find(id) != myVTypeDistDict.end()) {
     478            0 :             return false;
     479              :         }
     480              :     }
     481              :     return true;
     482              : }
     483              : 
     484              : 
     485              : bool
     486         2050 : RONet::addVehicleType(SUMOVTypeParameter* type) {
     487         2050 :     if (checkVType(type->id)) {
     488         2050 :         myVehicleTypes.add(type->id, type);
     489              :     } else {
     490            0 :         WRITE_ERRORF(TL("The vehicle type '%' occurs at least twice."), type->id);
     491            0 :         delete type;
     492            0 :         return false;
     493              :     }
     494         2050 :     return true;
     495              : }
     496              : 
     497              : 
     498              : bool
     499           44 : RONet::addVTypeDistribution(const std::string& id, RandomDistributor<SUMOVTypeParameter*>* vehTypeDistribution) {
     500           44 :     if (checkVType(id)) {
     501           44 :         myVTypeDistDict[id] = vehTypeDistribution;
     502           44 :         return true;
     503              :     }
     504            0 :     delete vehTypeDistribution;
     505              :     return false;
     506              : }
     507              : 
     508              : 
     509              : bool
     510       296866 : RONet::addVehicle(const std::string& id, ROVehicle* veh) {
     511       296866 :     if (myVehIDs.find(id) == myVehIDs.end()) {
     512       593692 :         myVehIDs[id] = veh->getParameter().departProcedure == DepartDefinition::TRIGGERED ? -1 : veh->getDepartureTime();
     513              : 
     514       296862 :         if (veh->isPublicTransport()) {
     515           44 :             if (!veh->isPartOfFlow()) {
     516           36 :                 myPTVehicles.push_back(veh);
     517              :             }
     518           44 :             if (!myDoPTRouting) {
     519              :                 return true;
     520              :             }
     521              :         }
     522       296826 :         myRoutables[veh->getDepart()].push_back(veh);
     523       296826 :         return true;
     524              :     }
     525           12 :     WRITE_ERRORF(TL("Another vehicle with the id '%' exists."), id);
     526            4 :     delete veh;
     527              :     return false;
     528              : }
     529              : 
     530              : 
     531              : bool
     532          256 : RONet::knowsVehicle(const std::string& id) const {
     533          256 :     return myVehIDs.find(id) != myVehIDs.end();
     534              : }
     535              : 
     536              : SUMOTime
     537           28 : RONet::getDeparture(const std::string& vehID) const {
     538              :     auto it = myVehIDs.find(vehID);
     539           28 :     if (it != myVehIDs.end()) {
     540           28 :         return it->second;
     541              :     } else {
     542            0 :         throw ProcessError(TLF("Requesting departure time for unknown vehicle '%'", vehID));
     543              :     }
     544              : }
     545              : 
     546              : 
     547              : bool
     548         2550 : RONet::addFlow(SUMOVehicleParameter* flow, const bool randomize) {
     549         2550 :     if (randomize && flow->repetitionOffset >= 0) {
     550           10 :         myDepartures[flow->id].reserve(flow->repetitionNumber);
     551          110 :         for (int i = 0; i < flow->repetitionNumber; ++i) {
     552          100 :             myDepartures[flow->id].push_back(flow->depart + RandHelper::rand(flow->repetitionNumber * flow->repetitionOffset));
     553              :         }
     554           10 :         std::sort(myDepartures[flow->id].begin(), myDepartures[flow->id].end());
     555           10 :         std::reverse(myDepartures[flow->id].begin(), myDepartures[flow->id].end());
     556              :     }
     557         2550 :     const bool added = myFlows.add(flow->id, flow);
     558         2550 :     if (added) {
     559         2546 :         myHaveActiveFlows = true;
     560              :     }
     561         2550 :     return added;
     562              : }
     563              : 
     564              : 
     565              : bool
     566         3338 : RONet::addPerson(ROPerson* person) {
     567              :     if (myPersonIDs.count(person->getID()) == 0) {
     568              :         myPersonIDs.insert(person->getID());
     569         3338 :         myRoutables[person->getDepart()].push_back(person);
     570         3338 :         return true;
     571              :     }
     572            0 :     WRITE_ERRORF(TL("Another person with the id '%' exists."), person->getID());
     573            0 :     return false;
     574              : }
     575              : 
     576              : 
     577              : void
     578           56 : RONet::addContainer(const SUMOTime depart, const std::string desc) {
     579           56 :     myContainers.insert(std::pair<const SUMOTime, const std::string>(depart, desc));
     580           56 : }
     581              : 
     582              : 
     583              : void
     584        13241 : RONet::checkFlows(SUMOTime time, MsgHandler* errorHandler) {
     585        13241 :     myHaveActiveFlows = false;
     586        51173 :     for (const auto& i : myFlows) {
     587        37932 :         SUMOVehicleParameter* const pars = i.second;
     588        37932 :         if (pars->line != "" && !myDoPTRouting) {
     589          737 :             continue;
     590              :         }
     591        37195 :         if (myKeepFlows) {
     592          796 :             if (pars->repetitionsDone < pars->repetitionNumber) {
     593              :                 // each each flow only once
     594          790 :                 pars->repetitionsDone = pars->repetitionNumber;
     595          790 :                 const SUMOVTypeParameter* type = getVehicleTypeSecure(pars->vtypeid);
     596          790 :                 if (type == nullptr) {
     597            0 :                     type = getVehicleTypeSecure(DEFAULT_VTYPE_ID);
     598              :                 } else {
     599              :                     auto dist = getVTypeDistribution(pars->vtypeid);
     600            0 :                     if (dist != nullptr) {
     601            0 :                         WRITE_WARNINGF("Keeping flow '%' with a vTypeDistribution can lead to invalid routes if the distribution contains different vClasses", pars->id);
     602              :                     }
     603              :                 }
     604         1580 :                 RORouteDef* route = getRouteDef(pars->routeid)->copy(pars->routeid, pars->depart);
     605          790 :                 ROVehicle* veh = new ROVehicle(*pars, route, type, this, errorHandler);
     606          790 :                 addVehicle(pars->id, veh);
     607              :             }
     608        36399 :         } else if (pars->repetitionProbability > 0) {
     609          108 :             if (pars->repetitionEnd > pars->depart && pars->repetitionsDone < pars->repetitionNumber) {
     610           96 :                 myHaveActiveFlows = true;
     611              :             }
     612              :             const SUMOTime origDepart = pars->depart;
     613         1020 :             while (pars->depart < time && pars->repetitionsDone < pars->repetitionNumber) {
     614          928 :                 if (pars->repetitionEnd <= pars->depart) {
     615              :                     break;
     616              :                 }
     617              :                 // only call rand if all other conditions are met
     618          912 :                 if (RandHelper::rand() < (pars->repetitionProbability * TS)) {
     619          168 :                     SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
     620          336 :                     newPars->id = pars->id + "." + toString(pars->repetitionsDone);
     621          168 :                     newPars->depart = pars->depart;
     622          168 :                     for (StopParVector::iterator stop = newPars->stops.begin(); stop != newPars->stops.end(); ++stop) {
     623            0 :                         if (stop->until >= 0) {
     624            0 :                             stop->until += pars->depart - origDepart;
     625              :                         }
     626              :                     }
     627          168 :                     pars->repetitionsDone++;
     628              :                     // try to build the vehicle
     629          168 :                     const SUMOVTypeParameter* type = getVehicleTypeSecure(pars->vtypeid);
     630          168 :                     if (type == nullptr) {
     631           32 :                         type = getVehicleTypeSecure(DEFAULT_VTYPE_ID);
     632          136 :                     } else if (!myKeepVTypeDist) {
     633              :                         // fix the type id in case we used a distribution
     634          136 :                         newPars->vtypeid = type->id;
     635              :                     }
     636          168 :                     const SUMOTime stopOffset = pars->routeid[0] == '!' ? pars->depart - origDepart : pars->depart;
     637          336 :                     RORouteDef* route = getRouteDef(pars->routeid)->copy("!" + newPars->id, stopOffset);
     638          168 :                     ROVehicle* veh = new ROVehicle(*newPars, route, type, this, errorHandler);
     639          168 :                     addVehicle(newPars->id, veh);
     640          168 :                     delete newPars;
     641              :                 }
     642          912 :                 pars->depart += DELTA_T;
     643              :             }
     644              :         } else {
     645        36291 :             SUMOTime depart = static_cast<SUMOTime>(pars->depart + pars->repetitionTotalOffset);
     646        54019 :             while (pars->repetitionsDone < pars->repetitionNumber && pars->repetitionEnd >= depart) {
     647        28450 :                 myHaveActiveFlows = true;
     648        28450 :                 depart = static_cast<SUMOTime>(pars->depart + pars->repetitionTotalOffset);
     649        28450 :                 if (myDepartures.find(pars->id) != myDepartures.end()) {
     650          110 :                     depart = myDepartures[pars->id].back();
     651              :                 }
     652        28450 :                 if (depart >= time + DELTA_T) {
     653              :                     break;
     654              :                 }
     655        17728 :                 if (myDepartures.find(pars->id) != myDepartures.end()) {
     656          100 :                     myDepartures[pars->id].pop_back();
     657              :                 }
     658        17728 :                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
     659        35456 :                 newPars->id = pars->id + "." + toString(pars->repetitionsDone);
     660        17728 :                 newPars->depart = depart;
     661        17848 :                 for (StopParVector::iterator stop = newPars->stops.begin(); stop != newPars->stops.end(); ++stop) {
     662          120 :                     if (stop->until >= 0) {
     663            0 :                         stop->until += depart - pars->depart;
     664              :                     }
     665              :                 }
     666        17728 :                 pars->incrementFlow(1);
     667              :                 // try to build the vehicle
     668        17728 :                 const SUMOVTypeParameter* type = getVehicleTypeSecure(pars->vtypeid);
     669        17728 :                 if (type == nullptr) {
     670           62 :                     type = getVehicleTypeSecure(DEFAULT_VTYPE_ID);
     671        17666 :                 } else if (!myKeepVTypeDist) {
     672              :                     // fix the type id in case we used a distribution
     673        17626 :                     newPars->vtypeid = type->id;
     674              :                 }
     675        17728 :                 const SUMOTime stopOffset = pars->routeid[0] == '!' ? depart - pars->depart : depart;
     676        35456 :                 RORouteDef* route = getRouteDef(pars->routeid)->copy("!" + newPars->id, stopOffset);
     677        17728 :                 ROVehicle* veh = new ROVehicle(*newPars, route, type, this, errorHandler);
     678        17728 :                 addVehicle(newPars->id, veh);
     679        17728 :                 delete newPars;
     680              :             }
     681              :         }
     682              :     }
     683        13241 : }
     684              : 
     685              : 
     686              : void
     687           56 : RONet::createBulkRouteRequests(const RORouterProvider& provider, const SUMOTime time, const bool removeLoops) {
     688              :     std::map<const int, std::vector<RORoutable*> > bulkVehs;
     689              :     int numBulked = 0;
     690           84 :     for (RoutablesMap::const_iterator i = myRoutables.begin(); i != myRoutables.end(); ++i) {
     691           56 :         if (i->first >= time) {
     692              :             break;
     693              :         }
     694          539 :         for (RORoutable* const routable : i->second) {
     695          511 :             const ROEdge* const depEdge = routable->getDepartEdge();
     696          511 :             bulkVehs[depEdge->getNumericalID()].push_back(routable);
     697          511 :             RORoutable* const first = bulkVehs[depEdge->getNumericalID()].front();
     698          511 :             numBulked++;
     699          511 :             if (first->getMaxSpeed() != routable->getMaxSpeed()) {
     700            6 :                 WRITE_WARNINGF(TL("Bulking different maximum speeds ('%' and '%') may lead to suboptimal routes."), first->getID(), routable->getID());
     701              :             }
     702          511 :             if (first->getVClass() != routable->getVClass()) {
     703            6 :                 WRITE_WARNINGF(TL("Bulking different vehicle classes ('%' and '%') may lead to invalid routes."), first->getID(), routable->getID());
     704              :             }
     705              :         }
     706              :     }
     707              : #ifdef HAVE_FOX
     708              :     int workerIndex = 0;
     709              : #endif
     710           56 :     if ((int)bulkVehs.size() < numBulked) {
     711           56 :         WRITE_MESSAGE(TLF("Using bulk-mode for % entities from % origins", numBulked, bulkVehs.size()));
     712              :     }
     713          157 :     for (std::map<const int, std::vector<RORoutable*> >::const_iterator i = bulkVehs.begin(); i != bulkVehs.end(); ++i) {
     714              : #ifdef HAVE_FOX
     715          101 :         if (myThreadPool.size() > 0) {
     716              :             bool bulk = true;
     717          132 :             for (RORoutable* const r : i->second) {
     718          120 :                 myThreadPool.add(new RoutingTask(r, removeLoops, myErrorHandler), workerIndex);
     719          120 :                 if (bulk) {
     720           12 :                     myThreadPool.add(new BulkmodeTask(true), workerIndex);
     721              :                     bulk = false;
     722              :                 }
     723              :             }
     724           12 :             myThreadPool.add(new BulkmodeTask(false), workerIndex);
     725           12 :             workerIndex++;
     726           12 :             if (workerIndex == (int)myThreadPool.size()) {
     727              :                 workerIndex = 0;
     728              :             }
     729           12 :             continue;
     730           12 :         }
     731              : #endif
     732          480 :         for (RORoutable* const r : i->second) {
     733          391 :             r->computeRoute(provider, removeLoops, myErrorHandler);
     734          391 :             provider.setBulkMode(true);
     735              :         }
     736           89 :         provider.setBulkMode(false);
     737              :     }
     738           56 : }
     739              : 
     740              : 
     741              : SUMOTime
     742        19226 : RONet::saveAndRemoveRoutesUntil(OptionsCont& options, const RORouterProvider& provider,
     743              :                                 SUMOTime time) {
     744        19226 :     MsgHandler* mh = (options.getBool("ignore-errors") ?
     745        19226 :                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
     746        19226 :     if (myHaveActiveFlows) {
     747        13241 :         checkFlows(time, mh);
     748              :     }
     749              :     SUMOTime lastTime = -1;
     750        19226 :     const bool removeLoops = options.getBool("remove-loops");
     751              : #ifdef HAVE_FOX
     752        38452 :     const int maxNumThreads = options.getInt("routing-threads");
     753              : #endif
     754        19226 :     if (myRoutables.size() != 0) {
     755        18428 :         if (options.getBool("bulk-routing")) {
     756              : #ifdef HAVE_FOX
     757           64 :             while ((int)myThreadPool.size() < maxNumThreads) {
     758            8 :                 new WorkerThread(myThreadPool, provider);
     759              :             }
     760              : #endif
     761           56 :             createBulkRouteRequests(provider, time, removeLoops);
     762              :         } else {
     763       250959 :             for (RoutablesMap::const_iterator i = myRoutables.begin(); i != myRoutables.end(); ++i) {
     764       248219 :                 if (i->first >= time) {
     765              :                     break;
     766              :                 }
     767       541261 :                 for (RORoutable* const routable : i->second) {
     768              : #ifdef HAVE_FOX
     769              :                     // add task
     770       299460 :                     if (maxNumThreads > 0) {
     771              :                         const int numThreads = (int)myThreadPool.size();
     772          827 :                         if (numThreads == 0) {
     773              :                             // This is the very first routing. Since at least the CHRouter needs initialization
     774              :                             // before it gets cloned, we do not do this in parallel
     775           24 :                             routable->computeRoute(provider, removeLoops, myErrorHandler);
     776           24 :                             new WorkerThread(myThreadPool, provider);
     777              :                         } else {
     778              :                             // add thread if necessary
     779          803 :                             if (numThreads < maxNumThreads && myThreadPool.isFull()) {
     780           18 :                                 new WorkerThread(myThreadPool, provider);
     781              :                             }
     782          803 :                             myThreadPool.add(new RoutingTask(routable, removeLoops, myErrorHandler));
     783              :                         }
     784          827 :                         continue;
     785          827 :                     }
     786              : #endif
     787       298633 :                     routable->computeRoute(provider, removeLoops, myErrorHandler);
     788              :                 }
     789              :             }
     790              :         }
     791              : #ifdef HAVE_FOX
     792         9198 :         myThreadPool.waitAll();
     793              : #endif
     794              :     }
     795        38148 :     const double scale = options.exists("scale-suffix") ? options.getFloat("scale") : 1;
     796              :     // write all vehicles (and additional structures)
     797       261087 :     while (myRoutables.size() != 0 || myContainers.size() != 0) {
     798              :         // get the next vehicle, person or container
     799              :         RoutablesMap::iterator routables = myRoutables.begin();
     800       248315 :         const SUMOTime routableTime = routables == myRoutables.end() ? SUMOTime_MAX : routables->first;
     801              :         ContainerMap::iterator container = myContainers.begin();
     802       248315 :         const SUMOTime containerTime = container == myContainers.end() ? SUMOTime_MAX : container->first;
     803              :         // check whether it shall not yet be computed
     804       248315 :         if (routableTime >= time && containerTime >= time) {
     805              :             lastTime = MIN2(routableTime, containerTime);
     806              :             break;
     807              :         }
     808              :         const SUMOTime minTime = MIN2(routableTime, containerTime);
     809       241877 :         if (routableTime == minTime) {
     810              :             // check whether to print the output
     811       241829 :             if (lastTime != routableTime && lastTime != -1) {
     812              :                 // report writing progress
     813       235666 :                 if (options.getInt("stats-period") >= 0 && ((int)routableTime % options.getInt("stats-period")) == 0) {
     814            0 :                     WRITE_MESSAGE("Read: " + toString(myVehIDs.size()) + ",  Discarded: " + toString(myDiscardedRouteNo) + ",  Written: " + toString(myWrittenRouteNo));
     815              :                 }
     816              :             }
     817              :             lastTime = routableTime;
     818       541784 :             for (const RORoutable* const r : routables->second) {
     819              :                 // ok, check whether it has been routed
     820       299955 :                 if (r->getRoutingSuccess()) {
     821              :                     // write the route
     822       280773 :                     int quota = getScalingQuota(scale, myWrittenRouteNo);
     823       280773 :                     r->write(myRoutesOutput, myRouteAlternativesOutput, myTypesOutput, options, quota);
     824       280773 :                     myWrittenRouteNo++;
     825              :                 } else {
     826        19182 :                     myDiscardedRouteNo++;
     827              :                 }
     828              :                 // we need to keep individual public transport vehicles but not the flows
     829       299955 :                 if (!r->isPublicTransport() || r->isPartOfFlow()) {
     830              :                     // delete routes and the vehicle
     831       299955 :                     const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
     832       299955 :                     if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
     833       296626 :                         if (r->isPartOfFlow() || !myRoutes.remove(veh->getRouteDefinition()->getID())) {
     834        19877 :                             delete veh->getRouteDefinition();
     835              :                         }
     836              :                     }
     837       299955 :                     delete r;
     838              :                 }
     839              :             }
     840              :             myRoutables.erase(routables);
     841              :         }
     842       241877 :         if (containerTime == minTime) {
     843           48 :             myRoutesOutput->writePreformattedTag(container->second);
     844           48 :             if (myRouteAlternativesOutput != nullptr) {
     845              :                 myRouteAlternativesOutput->writePreformattedTag(container->second);
     846              :             }
     847              :             myContainers.erase(container);
     848              :         }
     849              :     }
     850        19210 :     return lastTime;
     851              : }
     852              : 
     853              : 
     854              : bool
     855        41170 : RONet::furtherStored() {
     856        41170 :     return myRoutables.size() > 0 || (myFlows.size() > 0 && myHaveActiveFlows) || myContainers.size() > 0;
     857              : }
     858              : 
     859              : 
     860              : int
     861          123 : RONet::getEdgeNumber() const {
     862          123 :     return myEdges.size();
     863              : }
     864              : 
     865              : 
     866              : int
     867            1 : RONet::getInternalEdgeNumber() const {
     868            1 :     return myNumInternalEdges;
     869              : }
     870              : 
     871              : 
     872              : ROEdge*
     873         9031 : RONet::getEdgeForLaneID(const std::string& laneID) const {
     874        18062 :     return getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(laneID));
     875              : }
     876              : 
     877              : 
     878              : ROLane*
     879         1136 : RONet::getLane(const std::string& laneID) const {
     880         1136 :     int laneIndex = SUMOXMLDefinitions::getIndexFromLane(laneID);
     881         1136 :     return getEdgeForLaneID(laneID)->getLanes()[laneIndex];
     882              : }
     883              : 
     884              : 
     885              : void
     886          593 : RONet::adaptIntermodalRouter(ROIntermodalRouter& router) {
     887          593 :     double taxiWait = STEPS2TIME(string2time(OptionsCont::getOptions().getString("persontrip.taxi.waiting-time")));
     888          880 :     for (const auto& stopType : myInstance->myStoppingPlaces) {
     889              :         // add access to all stopping places
     890          287 :         const SumoXMLTag element = stopType.first;
     891         2062 :         for (const auto& stop : stopType.second) {
     892         1775 :             router.getNetwork()->addAccess(stop.first, myInstance->getEdgeForLaneID(stop.second->lane),
     893         1775 :                                            stop.second->startPos, stop.second->endPos, 0., element, false, taxiWait);
     894              :             // add access to all public transport stops
     895         1775 :             if (element == SUMO_TAG_BUS_STOP) {
     896         3831 :                 for (const auto& a : stop.second->accessPos) {
     897         2076 :                     router.getNetwork()->addAccess(stop.first, myInstance->getEdgeForLaneID(std::get<0>(a)),
     898              :                                                    std::get<1>(a), std::get<1>(a), std::get<2>(a), SUMO_TAG_BUS_STOP, true, taxiWait);
     899              :                 }
     900              :             }
     901              :         }
     902              :     }
     903              :     // fill the public transport router with pre-parsed public transport lines
     904         1346 :     for (const auto& i : myInstance->myFlows) {
     905          753 :         if (i.second->line != "") {
     906          741 :             const RORouteDef* const route = myInstance->getRouteDef(i.second->routeid);
     907              :             const StopParVector* addStops = nullptr;
     908         1482 :             if (route != nullptr && route->getFirstRoute() != nullptr) {
     909              :                 addStops = &route->getFirstRoute()->getStops();
     910              :             }
     911          741 :             router.getNetwork()->addSchedule(*i.second, addStops);
     912              :         }
     913              :     }
     914          629 :     for (const RORoutable* const veh : myInstance->myPTVehicles) {
     915              :         // add single vehicles with line attribute which are not part of a flow
     916              :         // no need to add route stops here, they have been added to the vehicle before
     917           36 :         router.getNetwork()->addSchedule(veh->getParameter());
     918              :     }
     919              :     // add access to transfer from walking to taxi-use
     920          593 :     if ((router.getCarWalkTransfer() & ModeChangeOptions::TAXI_PICKUP_ANYWHERE) != 0) {
     921        81723 :         for (const ROEdge* edge : ROEdge::getAllEdges()) {
     922        81142 :             if (!edge->isTazConnector() && (edge->getPermissions() & SVC_PEDESTRIAN) != 0 && (edge->getPermissions() & SVC_TAXI) != 0) {
     923        22470 :                 router.getNetwork()->addCarAccess(edge, SVC_TAXI, taxiWait);
     924              :             }
     925              :         }
     926              :     }
     927          593 : }
     928              : 
     929              : 
     930              : bool
     931      4185159 : RONet::hasPermissions() const {
     932      4185159 :     return myHavePermissions;
     933              : }
     934              : 
     935              : 
     936              : void
     937       252822 : RONet::setPermissionsFound() {
     938       252822 :     myHavePermissions = true;
     939       252822 : }
     940              : 
     941              : bool
     942           12 : RONet::hasLoadedEffort() const {
     943           38 :     for (const auto& item : myEdges) {
     944           35 :         if (item.second->hasStoredEffort()) {
     945              :             return true;
     946              :         }
     947              :     }
     948              :     return false;
     949              : }
     950              : 
     951              : const std::string
     952         1591 : RONet::getStoppingPlaceName(const std::string& id) const {
     953         1783 :     for (const auto& mapItem : myStoppingPlaces) {
     954              :         SUMOVehicleParameter::Stop* stop = mapItem.second.get(id);
     955         1591 :         if (stop != nullptr) {
     956              :             // see RONetHandler::parseStoppingPlace
     957              :             return stop->busstop;
     958              :         }
     959              :     }
     960            0 :     return "";
     961              : }
     962              : 
     963              : const std::string
     964         1471 : RONet::getStoppingPlaceElement(const std::string& id) const {
     965         1615 :     for (const auto& mapItem : myStoppingPlaces) {
     966              :         SUMOVehicleParameter::Stop* stop = mapItem.second.get(id);
     967         1471 :         if (stop != nullptr) {
     968              :             // see RONetHandler::parseStoppingPlace
     969              :             return stop->actType;
     970              :         }
     971              :     }
     972            0 :     return toString(SUMO_TAG_BUS_STOP);
     973              : }
     974              : 
     975              : 
     976              : #ifdef HAVE_FOX
     977              : // ---------------------------------------------------------------------------
     978              : // RONet::RoutingTask-methods
     979              : // ---------------------------------------------------------------------------
     980              : void
     981          923 : RONet::RoutingTask::run(MFXWorkerThread* context) {
     982          923 :     myRoutable->computeRoute(*static_cast<WorkerThread*>(context), myRemoveLoops, myErrorHandler);
     983          923 : }
     984              : #endif
     985              : 
     986              : 
     987              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1