1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    RONet.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Sept 2002
15 /// @version $Id$
16 ///
17 // The router's network representation
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <algorithm>
27 #include <utils/router/RouteCostCalculator.h>
28 #include <utils/vehicle/SUMOVTypeParameter.h>
29 #include <utils/router/SUMOAbstractRouter.h>
30 #include <utils/options/OptionsCont.h>
31 #include <utils/common/UtilExceptions.h>
32 #include <utils/common/ToString.h>
33 #include <utils/common/RandHelper.h>
34 #include <utils/common/SUMOVehicleClass.h>
35 #include <utils/iodevices/OutputDevice.h>
36 #include "ROEdge.h"
37 #include "ROLane.h"
38 #include "RONode.h"
39 #include "ROPerson.h"
40 #include "RORoute.h"
41 #include "RORouteDef.h"
42 #include "ROVehicle.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*
getInstance(void)56 RONet::getInstance(void) {
57     if (myInstance != nullptr) {
58         return myInstance;
59     }
60     throw ProcessError("A network was not yet constructed.");
61 }
62 
63 
RONet()64 RONet::RONet()
65     : myVehicleTypes(), myDefaultVTypeMayBeDeleted(true),
66       myDefaultPedTypeMayBeDeleted(true), myDefaultBikeTypeMayBeDeleted(true),
67       myHaveActiveFlows(true),
68       myRoutesOutput(nullptr), myRouteAlternativesOutput(nullptr), myTypesOutput(nullptr),
69       myReadRouteNo(0), myDiscardedRouteNo(0), myWrittenRouteNo(0),
70       myHavePermissions(false),
71       myNumInternalEdges(0),
72       myErrorHandler(OptionsCont::getOptions().exists("ignore-errors")
73                      && OptionsCont::getOptions().getBool("ignore-errors") ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
74       myKeepVTypeDist(OptionsCont::getOptions().exists("keep-vtype-distributions")
75                       && OptionsCont::getOptions().getBool("keep-vtype-distributions")) {
76     if (myInstance != nullptr) {
77         throw ProcessError("A network was already constructed.");
78     }
79     SUMOVTypeParameter* type = new SUMOVTypeParameter(DEFAULT_VTYPE_ID, SVC_PASSENGER);
80     type->onlyReferenced = true;
81     myVehicleTypes.add(type->id, type);
82     SUMOVTypeParameter* defPedType = new SUMOVTypeParameter(DEFAULT_PEDTYPE_ID, SVC_PEDESTRIAN);
83     defPedType->onlyReferenced = true;
84     defPedType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
85     myVehicleTypes.add(defPedType->id, defPedType);
86     SUMOVTypeParameter* defBikeType = new SUMOVTypeParameter(DEFAULT_BIKETYPE_ID, SVC_BICYCLE);
87     defBikeType->onlyReferenced = true;
88     defBikeType->parametersSet |= VTYPEPARS_VEHICLECLASS_SET;
89     myVehicleTypes.add(defBikeType->id, defBikeType);
90     myInstance = this;
91 }
92 
93 
~RONet()94 RONet::~RONet() {
95     for (RoutablesMap::iterator routables = myRoutables.begin(); routables != myRoutables.end(); ++routables) {
96         for (RORoutable* const r : routables->second) {
97             const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
98             // delete routes and the vehicle
99             if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
100                 if (!myRoutes.remove(veh->getRouteDefinition()->getID())) {
101                     delete veh->getRouteDefinition();
102                 }
103             }
104             delete r;
105         }
106     }
107     for (const RORoutable* const r : myPTVehicles) {
108         const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
109         // delete routes and the vehicle
110         if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
111             if (!myRoutes.remove(veh->getRouteDefinition()->getID())) {
112                 delete veh->getRouteDefinition();
113             }
114         }
115         delete r;
116     }
117     myRoutables.clear();
118 }
119 
120 
121 void
addRestriction(const std::string & id,const SUMOVehicleClass svc,const double speed)122 RONet::addRestriction(const std::string& id, const SUMOVehicleClass svc, const double speed) {
123     myRestrictions[id][svc] = speed;
124 }
125 
126 
127 const std::map<SUMOVehicleClass, double>*
getRestrictions(const std::string & id) const128 RONet::getRestrictions(const std::string& id) const {
129     std::map<std::string, std::map<SUMOVehicleClass, double> >::const_iterator i = myRestrictions.find(id);
130     if (i == myRestrictions.end()) {
131         return nullptr;
132     }
133     return &i->second;
134 }
135 
136 
137 bool
addEdge(ROEdge * edge)138 RONet::addEdge(ROEdge* edge) {
139     if (!myEdges.add(edge->getID(), edge)) {
140         WRITE_ERROR("The edge '" + edge->getID() + "' occurs at least twice.");
141         delete edge;
142         return false;
143     }
144     if (edge->isInternal()) {
145         myNumInternalEdges += 1;
146     }
147     return true;
148 }
149 
150 
151 bool
addDistrict(const std::string id,ROEdge * source,ROEdge * sink)152 RONet::addDistrict(const std::string id, ROEdge* source, ROEdge* sink) {
153     if (myDistricts.count(id) > 0) {
154         WRITE_ERROR("The TAZ '" + id + "' occurs at least twice.");
155         delete source;
156         delete sink;
157         return false;
158     }
159     sink->setFunction(EDGEFUNC_CONNECTOR);
160     addEdge(sink);
161     source->setFunction(EDGEFUNC_CONNECTOR);
162     addEdge(source);
163     myDistricts[id] = std::make_pair(std::vector<std::string>(), std::vector<std::string>());
164     return true;
165 }
166 
167 
168 bool
addDistrictEdge(const std::string tazID,const std::string edgeID,const bool isSource)169 RONet::addDistrictEdge(const std::string tazID, const std::string edgeID, const bool isSource) {
170     if (myDistricts.count(tazID) == 0) {
171         WRITE_ERROR("The TAZ '" + tazID + "' is unknown.");
172         return false;
173     }
174     ROEdge* edge = getEdge(edgeID);
175     if (edge == nullptr) {
176         WRITE_ERROR("The edge '" + edgeID + "' for TAZ '" + tazID + "' is unknown.");
177         return false;
178     }
179     if (isSource) {
180         getEdge(tazID + "-source")->addSuccessor(edge);
181         myDistricts[tazID].first.push_back(edgeID);
182     } else {
183         edge->addSuccessor(getEdge(tazID + "-sink"));
184         myDistricts[tazID].second.push_back(edgeID);
185     }
186     return true;
187 }
188 
189 
190 void
addNode(RONode * node)191 RONet::addNode(RONode* node) {
192     if (!myNodes.add(node->getID(), node)) {
193         WRITE_ERROR("The node '" + node->getID() + "' occurs at least twice.");
194         delete node;
195     }
196 }
197 
198 
199 void
addStoppingPlace(const std::string & id,const SumoXMLTag category,SUMOVehicleParameter::Stop * stop)200 RONet::addStoppingPlace(const std::string& id, const SumoXMLTag category, SUMOVehicleParameter::Stop* stop) {
201     if (!myStoppingPlaces[category == SUMO_TAG_TRAIN_STOP ? SUMO_TAG_BUS_STOP : category].add(id, stop)) {
202         WRITE_ERROR("The " + toString(category) + " '" + id + "' occurs at least twice.");
203         delete stop;
204     }
205 }
206 
207 
208 bool
addRouteDef(RORouteDef * def)209 RONet::addRouteDef(RORouteDef* def) {
210     return myRoutes.add(def->getID(), def);
211 }
212 
213 
214 void
openOutput(const OptionsCont & options)215 RONet::openOutput(const OptionsCont& options) {
216     if (options.isSet("output-file") && options.getString("output-file") != "") {
217         myRoutesOutput = &OutputDevice::getDevice(options.getString("output-file"));
218         myRoutesOutput->writeHeader<ROEdge>(SUMO_TAG_ROUTES);
219         myRoutesOutput->writeAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance").writeAttr("xsi:noNamespaceSchemaLocation", "http://sumo.dlr.de/xsd/routes_file.xsd");
220     }
221     if (options.exists("alternatives-output") && options.isSet("alternatives-output")
222             && !(options.exists("write-trips") && options.getBool("write-trips"))) {
223         myRouteAlternativesOutput = &OutputDevice::getDevice(options.getString("alternatives-output"));
224         myRouteAlternativesOutput->writeHeader<ROEdge>(SUMO_TAG_ROUTES);
225         myRouteAlternativesOutput->writeAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance").writeAttr("xsi:noNamespaceSchemaLocation", "http://sumo.dlr.de/xsd/routes_file.xsd");
226     }
227     if (options.isSet("vtype-output")) {
228         myTypesOutput = &OutputDevice::getDevice(options.getString("vtype-output"));
229         myTypesOutput->writeHeader<ROEdge>(SUMO_TAG_ROUTES);
230         myTypesOutput->writeAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance").writeAttr("xsi:noNamespaceSchemaLocation", "http://sumo.dlr.de/xsd/routes_file.xsd");
231     }
232 }
233 
234 
235 void
writeIntermodal(const OptionsCont & options,ROIntermodalRouter & router) const236 RONet::writeIntermodal(const OptionsCont& options, ROIntermodalRouter& router) const {
237     if (options.exists("intermodal-network-output") && options.isSet("intermodal-network-output")) {
238         OutputDevice::createDeviceByOption("intermodal-network-output", "intermodal");
239         router.writeNetwork(OutputDevice::getDevice(options.getString("intermodal-network-output")));
240     }
241     if (options.exists("intermodal-weight-output") && options.isSet("intermodal-weight-output")) {
242         OutputDevice::createDeviceByOption("intermodal-weight-output", "weights", "meandata_file.xsd");
243         OutputDevice& dev = OutputDevice::getDeviceByOption("intermodal-weight-output");
244         dev.openTag(SUMO_TAG_INTERVAL);
245         dev.writeAttr(SUMO_ATTR_ID, "intermodalweights");
246         dev.writeAttr(SUMO_ATTR_BEGIN, 0);
247         dev.writeAttr(SUMO_ATTR_END, SUMOTime_MAX);
248         router.writeWeights(dev);
249         dev.closeTag();
250     }
251 }
252 
253 
254 void
cleanup()255 RONet::cleanup() {
256     // end writing
257     if (myRoutesOutput != nullptr) {
258         myRoutesOutput->close();
259     }
260     // only if opened
261     if (myRouteAlternativesOutput != nullptr) {
262         myRouteAlternativesOutput->close();
263     }
264     // only if opened
265     if (myTypesOutput != nullptr) {
266         myTypesOutput->close();
267     }
268     RouteCostCalculator<RORoute, ROEdge, ROVehicle>::cleanup();
269 #ifdef HAVE_FOX
270     if (myThreadPool.size() > 0) {
271         myThreadPool.clear();
272     }
273 #endif
274 }
275 
276 
277 
278 SUMOVTypeParameter*
getVehicleTypeSecure(const std::string & id)279 RONet::getVehicleTypeSecure(const std::string& id) {
280     // check whether the type was already known
281     SUMOVTypeParameter* type = myVehicleTypes.get(id);
282     if (id == DEFAULT_VTYPE_ID) {
283         myDefaultVTypeMayBeDeleted = false;
284     }
285     if (id == DEFAULT_PEDTYPE_ID) {
286         myDefaultPedTypeMayBeDeleted = false;
287     }
288     if (id == DEFAULT_BIKETYPE_ID) {
289         myDefaultBikeTypeMayBeDeleted = false;
290     }
291     if (type != nullptr) {
292         return type;
293     }
294     VTypeDistDictType::iterator it2 = myVTypeDistDict.find(id);
295     if (it2 != myVTypeDistDict.end()) {
296         return it2->second->get();
297     }
298     if (id == "") {
299         // ok, no vehicle type or an unknown type was given within the user input
300         //  return the default type
301         myDefaultVTypeMayBeDeleted = false;
302         return myVehicleTypes.get(DEFAULT_VTYPE_ID);
303     }
304     return type;
305 }
306 
307 
308 bool
checkVType(const std::string & id)309 RONet::checkVType(const std::string& id) {
310     if (id == DEFAULT_VTYPE_ID) {
311         if (myDefaultVTypeMayBeDeleted) {
312             myVehicleTypes.remove(id);
313             myDefaultVTypeMayBeDeleted = false;
314         } else {
315             return false;
316         }
317     } else if (id == DEFAULT_PEDTYPE_ID) {
318         if (myDefaultPedTypeMayBeDeleted) {
319             myVehicleTypes.remove(id);
320             myDefaultPedTypeMayBeDeleted = false;
321         } else {
322             return false;
323         }
324     } else {
325         if (myVehicleTypes.get(id) != 0 || myVTypeDistDict.find(id) != myVTypeDistDict.end()) {
326             return false;
327         }
328     }
329     return true;
330 }
331 
332 
333 bool
addVehicleType(SUMOVTypeParameter * type)334 RONet::addVehicleType(SUMOVTypeParameter* type) {
335     if (checkVType(type->id)) {
336         myVehicleTypes.add(type->id, type);
337     } else {
338         WRITE_ERROR("The vehicle type '" + type->id + "' occurs at least twice.");
339         delete type;
340         return false;
341     }
342     return true;
343 }
344 
345 
346 bool
addVTypeDistribution(const std::string & id,RandomDistributor<SUMOVTypeParameter * > * vehTypeDistribution)347 RONet::addVTypeDistribution(const std::string& id, RandomDistributor<SUMOVTypeParameter*>* vehTypeDistribution) {
348     if (checkVType(id)) {
349         myVTypeDistDict[id] = vehTypeDistribution;
350         return true;
351     }
352     return false;
353 }
354 
355 
356 bool
addVehicle(const std::string & id,ROVehicle * veh)357 RONet::addVehicle(const std::string& id, ROVehicle* veh) {
358     if (myVehIDs.find(id) == myVehIDs.end()) {
359         myVehIDs.insert(id);
360         if (veh->isPublicTransport()) {
361             if (!veh->isPartOfFlow()) {
362                 myPTVehicles.push_back(veh);
363             }
364             if (OptionsCont::getOptions().exists("ptline-routing") && !OptionsCont::getOptions().getBool("ptline-routing")) {
365                 return true;
366             }
367         }
368         myRoutables[veh->getDepart()].push_back(veh);
369         return true;
370     }
371     WRITE_ERROR("Another vehicle with the id '" + id + "' exists.");
372     return false;
373 }
374 
375 
376 bool
addFlow(SUMOVehicleParameter * flow,const bool randomize)377 RONet::addFlow(SUMOVehicleParameter* flow, const bool randomize) {
378     if (randomize) {
379         myDepartures[flow->id].reserve(flow->repetitionNumber);
380         for (int i = 0; i < flow->repetitionNumber; ++i) {
381             myDepartures[flow->id].push_back(flow->depart + RandHelper::rand(flow->repetitionNumber * flow->repetitionOffset));
382         }
383         std::sort(myDepartures[flow->id].begin(), myDepartures[flow->id].end());
384         std::reverse(myDepartures[flow->id].begin(), myDepartures[flow->id].end());
385     }
386     return myFlows.add(flow->id, flow);
387 }
388 
389 
390 bool
addPerson(ROPerson * person)391 RONet::addPerson(ROPerson* person) {
392     if (myPersonIDs.count(person->getID()) == 0) {
393         myPersonIDs.insert(person->getID());
394         myRoutables[person->getDepart()].push_back(person);
395         return true;
396     }
397     WRITE_ERROR("Another person with the id '" + person->getID() + "' exists.");
398     return false;
399 }
400 
401 
402 void
addContainer(const SUMOTime depart,const std::string desc)403 RONet::addContainer(const SUMOTime depart, const std::string desc) {
404     myContainers.insert(std::pair<const SUMOTime, const std::string>(depart, desc));
405 }
406 
407 
408 void
checkFlows(SUMOTime time,MsgHandler * errorHandler)409 RONet::checkFlows(SUMOTime time, MsgHandler* errorHandler) {
410     myHaveActiveFlows = false;
411     for (const auto& i : myFlows) {
412         SUMOVehicleParameter* pars = i.second;
413         if (pars->repetitionProbability > 0) {
414             if (pars->repetitionEnd > pars->depart) {
415                 myHaveActiveFlows = true;
416             }
417             const SUMOTime origDepart = pars->depart;
418             while (pars->depart < time) {
419                 if (pars->repetitionEnd <= pars->depart) {
420                     break;
421                 }
422                 // only call rand if all other conditions are met
423                 if (RandHelper::rand() < (pars->repetitionProbability * TS)) {
424                     SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
425                     newPars->id = pars->id + "." + toString(pars->repetitionsDone);
426                     newPars->depart = pars->depart;
427                     for (std::vector<SUMOVehicleParameter::Stop>::iterator stop = newPars->stops.begin(); stop != newPars->stops.end(); ++stop) {
428                         if (stop->until >= 0) {
429                             stop->until += pars->depart - origDepart;
430                         }
431                     }
432                     pars->repetitionsDone++;
433                     // try to build the vehicle
434                     SUMOVTypeParameter* type = getVehicleTypeSecure(pars->vtypeid);
435                     if (!myKeepVTypeDist) {
436                         // fix the type id in case we used a distribution
437                         newPars->vtypeid = type->id;
438                     }
439                     const SUMOTime stopOffset = pars->routeid[0] == '!' ? pars->depart - origDepart : pars->depart;
440                     RORouteDef* route = getRouteDef(pars->routeid)->copy("!" + newPars->id, stopOffset);
441                     ROVehicle* veh = new ROVehicle(*newPars, route, type, this, errorHandler);
442                     addVehicle(newPars->id, veh);
443                     delete newPars;
444                 }
445                 pars->depart += DELTA_T;
446             }
447         } else {
448             while (pars->repetitionsDone < pars->repetitionNumber) {
449                 myHaveActiveFlows = true;
450                 SUMOTime depart = static_cast<SUMOTime>(pars->depart + pars->repetitionsDone * pars->repetitionOffset);
451                 if (myDepartures.find(pars->id) != myDepartures.end()) {
452                     depart = myDepartures[pars->id].back();
453                 }
454                 if (depart >= time + DELTA_T) {
455                     break;
456                 }
457                 if (myDepartures.find(pars->id) != myDepartures.end()) {
458                     myDepartures[pars->id].pop_back();
459                 }
460                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
461                 newPars->id = pars->id + "." + toString(pars->repetitionsDone);
462                 newPars->depart = depart;
463                 for (std::vector<SUMOVehicleParameter::Stop>::iterator stop = newPars->stops.begin(); stop != newPars->stops.end(); ++stop) {
464                     if (stop->until >= 0) {
465                         stop->until += depart - pars->depart;
466                     }
467                 }
468                 pars->repetitionsDone++;
469                 // try to build the vehicle
470                 SUMOVTypeParameter* type = getVehicleTypeSecure(pars->vtypeid);
471                 if (type == nullptr) {
472                     type = getVehicleTypeSecure(DEFAULT_VTYPE_ID);
473                 } else {
474                     // fix the type id in case we used a distribution
475                     newPars->vtypeid = type->id;
476                 }
477                 const SUMOTime stopOffset = pars->routeid[0] == '!' ? depart - pars->depart : depart;
478                 RORouteDef* route = getRouteDef(pars->routeid)->copy("!" + newPars->id, stopOffset);
479                 ROVehicle* veh = new ROVehicle(*newPars, route, type, this, errorHandler);
480                 addVehicle(newPars->id, veh);
481                 delete newPars;
482             }
483         }
484     }
485 }
486 
487 
488 void
createBulkRouteRequests(const RORouterProvider & provider,const SUMOTime time,const bool removeLoops)489 RONet::createBulkRouteRequests(const RORouterProvider& provider, const SUMOTime time, const bool removeLoops) {
490     std::map<const int, std::vector<RORoutable*> > bulkVehs;
491     for (RoutablesMap::const_iterator i = myRoutables.begin(); i != myRoutables.end(); ++i) {
492         if (i->first >= time) {
493             break;
494         }
495         for (RORoutable* const routable : i->second) {
496             const ROEdge* const depEdge = routable->getDepartEdge();
497             bulkVehs[depEdge->getNumericalID()].push_back(routable);
498             RORoutable* const first = bulkVehs[depEdge->getNumericalID()].front();
499             if (first->getMaxSpeed() != routable->getMaxSpeed()) {
500                 WRITE_WARNING("Bulking different maximum speeds ('" + first->getID() + "' and '" + routable->getID() + "') may lead to suboptimal routes.");
501             }
502             if (first->getVClass() != routable->getVClass()) {
503                 WRITE_WARNING("Bulking different vehicle classes ('" + first->getID() + "' and '" + routable->getID() + "') may lead to invalid routes.");
504             }
505         }
506     }
507     int workerIndex = 0;
508     for (std::map<const int, std::vector<RORoutable*> >::const_iterator i = bulkVehs.begin(); i != bulkVehs.end(); ++i) {
509 #ifdef HAVE_FOX
510         if (myThreadPool.size() > 0) {
511             RORoutable* const first = i->second.front();
512             myThreadPool.add(new RoutingTask(first, removeLoops, myErrorHandler), workerIndex);
513             myThreadPool.add(new BulkmodeTask(true), workerIndex);
514             for (std::vector<RORoutable*>::const_iterator j = i->second.begin() + 1; j != i->second.end(); ++j) {
515                 myThreadPool.add(new RoutingTask(*j, removeLoops, myErrorHandler), workerIndex);
516             }
517             myThreadPool.add(new BulkmodeTask(false), workerIndex);
518             workerIndex++;
519             if (workerIndex == (int)myThreadPool.size()) {
520                 workerIndex = 0;
521             }
522             continue;
523         }
524 #endif
525         for (std::vector<RORoutable*>::const_iterator j = i->second.begin(); j != i->second.end(); ++j) {
526             (*j)->computeRoute(provider, removeLoops, myErrorHandler);
527             provider.getVehicleRouter().setBulkMode(true);
528         }
529         provider.getVehicleRouter().setBulkMode(false);
530     }
531 }
532 
533 
534 SUMOTime
saveAndRemoveRoutesUntil(OptionsCont & options,const RORouterProvider & provider,SUMOTime time)535 RONet::saveAndRemoveRoutesUntil(OptionsCont& options, const RORouterProvider& provider,
536                                 SUMOTime time) {
537     MsgHandler* mh = (options.getBool("ignore-errors") ?
538                       MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance());
539     if (myHaveActiveFlows) {
540         checkFlows(time, mh);
541     }
542     SUMOTime lastTime = -1;
543     const bool removeLoops = options.getBool("remove-loops");
544     const int maxNumThreads = options.getInt("routing-threads");
545     if (myRoutables.size() != 0) {
546         if (options.getBool("bulk-routing")) {
547 #ifdef HAVE_FOX
548             while ((int)myThreadPool.size() < maxNumThreads) {
549                 new WorkerThread(myThreadPool, provider);
550             }
551 #endif
552             createBulkRouteRequests(provider, time, removeLoops);
553         } else {
554             for (RoutablesMap::const_iterator i = myRoutables.begin(); i != myRoutables.end(); ++i) {
555                 if (i->first >= time) {
556                     break;
557                 }
558                 for (RORoutable* const routable : i->second) {
559 #ifdef HAVE_FOX
560                     // add task
561                     if (maxNumThreads > 0) {
562                         const int numThreads = (int)myThreadPool.size();
563                         if (numThreads == 0) {
564                             // This is the very first routing. Since at least the CHRouter needs initialization
565                             // before it gets cloned, we do not do this in parallel
566                             routable->computeRoute(provider, removeLoops, myErrorHandler);
567                             new WorkerThread(myThreadPool, provider);
568                         } else {
569                             // add thread if necessary
570                             if (numThreads < maxNumThreads && myThreadPool.isFull()) {
571                                 new WorkerThread(myThreadPool, provider);
572                             }
573                             myThreadPool.add(new RoutingTask(routable, removeLoops, myErrorHandler));
574                         }
575                         continue;
576                     }
577 #endif
578                     routable->computeRoute(provider, removeLoops, myErrorHandler);
579                 }
580             }
581         }
582 #ifdef HAVE_FOX
583         myThreadPool.waitAll();
584 #endif
585     }
586     // write all vehicles (and additional structures)
587     while (myRoutables.size() != 0 || myContainers.size() != 0) {
588         // get the next vehicle, person or container
589         RoutablesMap::iterator routables = myRoutables.begin();
590         const SUMOTime routableTime = routables == myRoutables.end() ? SUMOTime_MAX : routables->first;
591         ContainerMap::iterator container = myContainers.begin();
592         const SUMOTime containerTime = container == myContainers.end() ? SUMOTime_MAX : container->first;
593         // check whether it shall not yet be computed
594         if (routableTime >= time && containerTime >= time) {
595             lastTime = MIN2(routableTime, containerTime);
596             break;
597         }
598         const SUMOTime minTime = MIN2(routableTime, containerTime);
599         if (routableTime == minTime) {
600             // check whether to print the output
601             if (lastTime != routableTime && lastTime != -1) {
602                 // report writing progress
603                 if (options.getInt("stats-period") >= 0 && ((int)routableTime % options.getInt("stats-period")) == 0) {
604                     WRITE_MESSAGE("Read: " + toString(myVehIDs.size()) + ",  Discarded: " + toString(myDiscardedRouteNo) + ",  Written: " + toString(myWrittenRouteNo));
605                 }
606             }
607             lastTime = routableTime;
608             for (const RORoutable* const r : routables->second) {
609                 // ok, check whether it has been routed
610                 if (r->getRoutingSuccess()) {
611                     // write the route
612                     r->write(*myRoutesOutput, myRouteAlternativesOutput, myTypesOutput, options);
613                     myWrittenRouteNo++;
614                 } else {
615                     myDiscardedRouteNo++;
616                 }
617                 // delete routes and the vehicle
618                 if (!r->isPublicTransport() || r->isPartOfFlow()) {
619                     const ROVehicle* const veh = dynamic_cast<const ROVehicle*>(r);
620                     if (veh != nullptr && veh->getRouteDefinition()->getID()[0] == '!') {
621                         if (!myRoutes.remove(veh->getRouteDefinition()->getID())) {
622                             delete veh->getRouteDefinition();
623                         }
624                     }
625                     delete r;
626                 }
627             }
628             myRoutables.erase(routables);
629         }
630         if (containerTime == minTime) {
631             myRoutesOutput->writePreformattedTag(container->second);
632             if (myRouteAlternativesOutput != nullptr) {
633                 myRouteAlternativesOutput->writePreformattedTag(container->second);
634             }
635             myContainers.erase(container);
636         }
637     }
638     return lastTime;
639 }
640 
641 
642 bool
furtherStored()643 RONet::furtherStored() {
644     return myRoutables.size() > 0 || (myFlows.size() > 0 && myHaveActiveFlows) || myContainers.size() > 0;
645 }
646 
647 
648 int
getEdgeNumber() const649 RONet::getEdgeNumber() const {
650     return myEdges.size();
651 }
652 
653 
654 int
getInternalEdgeNumber() const655 RONet::getInternalEdgeNumber() const {
656     return myNumInternalEdges;
657 }
658 
659 
660 void
adaptIntermodalRouter(ROIntermodalRouter & router)661 RONet::adaptIntermodalRouter(ROIntermodalRouter& router) {
662     // add access to all parking areas
663     for (const auto& i : myInstance->myStoppingPlaces[SUMO_TAG_PARKING_AREA]) {
664         router.getNetwork()->addAccess(i.first, myInstance->getEdgeForLaneID(i.second->lane), (i.second->startPos + i.second->endPos) / 2., 0., SUMO_TAG_PARKING_AREA);
665     }
666     // add access to all public transport stops
667     for (const auto& stop : myInstance->myStoppingPlaces[SUMO_TAG_BUS_STOP]) {
668         router.getNetwork()->addAccess(stop.first, myInstance->getEdgeForLaneID(stop.second->lane), (stop.second->startPos + stop.second->endPos) / 2., 0., SUMO_TAG_BUS_STOP);
669         for (const auto& a : stop.second->accessPos) {
670             router.getNetwork()->addAccess(stop.first, myInstance->getEdgeForLaneID(std::get<0>(a)), std::get<1>(a), std::get<2>(a), SUMO_TAG_BUS_STOP);
671         }
672     }
673     // fill the public transport router with pre-parsed public transport lines
674     for (const auto& i : myInstance->myFlows) {
675         if (i.second->line != "") {
676             const RORouteDef* const route = myInstance->getRouteDef(i.second->routeid);
677             const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr;
678             if (route != nullptr && route->getFirstRoute() != nullptr) {
679                 addStops = &route->getFirstRoute()->getStops();
680             }
681             router.getNetwork()->addSchedule(*i.second, addStops);
682         }
683     }
684     for (const RORoutable* const veh : myInstance->myPTVehicles) {
685         // add single vehicles with line attribute which are not part of a flow
686         // no need to add route stops here, they have been added to the vehicle before
687         router.getNetwork()->addSchedule(veh->getParameter());
688     }
689 }
690 
691 
692 bool
hasPermissions() const693 RONet::hasPermissions() const {
694     return myHavePermissions;
695 }
696 
697 
698 void
setPermissionsFound()699 RONet::setPermissionsFound() {
700     myHavePermissions = true;
701 }
702 
703 const std::string
getStoppingPlaceName(const std::string & id) const704 RONet::getStoppingPlaceName(const std::string& id) const {
705     for (const auto& mapItem : myStoppingPlaces) {
706         SUMOVehicleParameter::Stop* stop = mapItem.second.get(id);
707         if (stop != nullptr) {
708             // see RONetHandler::parseStoppingPlace
709             return stop->busstop;
710         }
711     }
712     return "";
713 }
714 
715 #ifdef HAVE_FOX
716 // ---------------------------------------------------------------------------
717 // RONet::RoutingTask-methods
718 // ---------------------------------------------------------------------------
719 void
run(FXWorkerThread * context)720 RONet::RoutingTask::run(FXWorkerThread* context) {
721     myRoutable->computeRoute(*static_cast<WorkerThread*>(context), myRemoveLoops, myErrorHandler);
722 }
723 #endif
724 
725 
726 /****************************************************************************/
727 
728