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    MSRouteHandler.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Sascha Krieg
14 /// @author  Michael Behrisch
15 /// @date    Mon, 9 Jul 2001
16 /// @version $Id$
17 ///
18 // Parser and container for routes during their loading
19 /****************************************************************************/
20 
21 
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26 
27 #include <string>
28 #include <map>
29 #include <vector>
30 #include <microsim/MSRoute.h>
31 #include <microsim/MSEdge.h>
32 #include <microsim/MSJunction.h>
33 #include <microsim/MSVehicleType.h>
34 #include <microsim/MSVehicle.h>
35 #include <microsim/MSInsertionControl.h>
36 #include <microsim/MSVehicleControl.h>
37 #include <microsim/MSLane.h>
38 #include "MSRouteHandler.h"
39 #include "MSTransportableControl.h"
40 #include <utils/xml/SUMOSAXHandler.h>
41 #include <utils/xml/SUMOXMLDefinitions.h>
42 #include <utils/common/MsgHandler.h>
43 #include <utils/common/StringUtils.h>
44 #include <utils/common/StringTokenizer.h>
45 #include <utils/common/UtilExceptions.h>
46 #include <utils/options/OptionsCont.h>
47 #include <utils/router/IntermodalRouter.h>
48 #include <utils/router/PedestrianRouter.h>
49 #include "MSNet.h"
50 
51 #include "MSParkingArea.h"
52 #include "MSStoppingPlace.h"
53 #include <microsim/MSGlobals.h>
54 #include <microsim/trigger/MSChargingStation.h>
55 #include <utils/vehicle/SUMOVehicleParserHelper.h>
56 
57 
58 // ===========================================================================
59 // static members
60 // ===========================================================================
61 std::mt19937 MSRouteHandler::myParsingRNG;
62 
63 
64 // ===========================================================================
65 // method definitions
66 // ===========================================================================
MSRouteHandler(const std::string & file,bool addVehiclesDirectly)67 MSRouteHandler::MSRouteHandler(const std::string& file,
68                                bool addVehiclesDirectly) :
69     SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes"),
70     myActivePlan(nullptr),
71     myActiveContainerPlan(nullptr),
72     myAddVehiclesDirectly(addVehiclesDirectly),
73     myCurrentVTypeDistribution(nullptr),
74     myCurrentRouteDistribution(nullptr),
75     myAmLoadingState(false) {
76     myActiveRoute.reserve(100);
77 }
78 
79 
~MSRouteHandler()80 MSRouteHandler::~MSRouteHandler() {
81 }
82 
83 void
deleteActivePlans()84 MSRouteHandler::deleteActivePlans() {
85     MSTransportable::MSTransportablePlan::iterator i;
86     if (myActivePlan != nullptr) {
87         for (i = myActivePlan->begin(); i != myActivePlan->end(); i++) {
88             delete *i;
89         }
90         delete myActivePlan;
91         myActivePlan = nullptr;
92     }
93     if (myActiveContainerPlan != nullptr) {
94         for (i = myActiveContainerPlan->begin(); i != myActiveContainerPlan->end(); i++) {
95             delete *i;
96         }
97         delete myActiveContainerPlan;
98         myActivePlan = nullptr;
99     }
100 }
101 
102 
103 void
parseFromViaTo(std::string element,const SUMOSAXAttributes & attrs)104 MSRouteHandler::parseFromViaTo(std::string element,
105                                const SUMOSAXAttributes& attrs) {
106     myActiveRoute.clear();
107     bool useTaz = OptionsCont::getOptions().getBool("with-taz");
108     if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
109         WRITE_WARNING("Taz usage was requested but no taz present in " + element + " '" + myVehicleParameter->id + "'!");
110         useTaz = false;
111     }
112     bool ok = true;
113     if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_FROM)) && myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET)) {
114         const MSEdge* fromTaz = MSEdge::dictionary(myVehicleParameter->fromTaz + "-source");
115         if (fromTaz == nullptr) {
116             throw ProcessError("Source taz '" + myVehicleParameter->fromTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
117         } else if (fromTaz->getNumSuccessors() == 0) {
118             throw ProcessError("Source taz '" + myVehicleParameter->fromTaz + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
119         } else {
120             myActiveRoute.push_back(fromTaz);
121         }
122     } else {
123         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok, "", true),
124                                myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
125     }
126     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
127         myInsertStopEdgesAt = (int)myActiveRoute.size();
128     }
129     MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true),
130                            myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
131     myVehicleParameter->via = StringTokenizer(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true)).getVector();
132     if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_TO)) && myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
133         const MSEdge* toTaz = MSEdge::dictionary(myVehicleParameter->toTaz + "-sink");
134         if (toTaz == nullptr) {
135             throw ProcessError("Sink taz '" + myVehicleParameter->toTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
136         } else if (toTaz->getNumPredecessors() == 0) {
137             throw ProcessError("Sink taz '" + myVehicleParameter->toTaz + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
138         } else {
139             myActiveRoute.push_back(toTaz);
140         }
141     } else {
142         MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
143                                myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
144     }
145     myActiveRouteID = "!" + myVehicleParameter->id;
146     if (myVehicleParameter->routeid == "") {
147         myVehicleParameter->routeid = myActiveRouteID;
148     }
149 }
150 
151 
152 void
myStartElement(int element,const SUMOSAXAttributes & attrs)153 MSRouteHandler::myStartElement(int element,
154                                const SUMOSAXAttributes& attrs) {
155     SUMORouteHandler::myStartElement(element, attrs);
156     try {
157         switch (element) {
158             case SUMO_TAG_PERSON:
159             case SUMO_TAG_PERSONFLOW:
160                 if (!MSNet::getInstance()->getVehicleControl().hasVType(myVehicleParameter->vtypeid)) {
161                     const std::string error = "The type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.";
162                     delete myVehicleParameter;
163                     myVehicleParameter = nullptr;
164                     throw ProcessError(error);
165                 }
166                 myActivePlan = new MSTransportable::MSTransportablePlan();
167                 break;
168             case SUMO_TAG_CONTAINER:
169                 myActiveContainerPlan = new MSTransportable::MSTransportablePlan();
170                 break;
171             case SUMO_TAG_RIDE: {
172                 const std::string pid = myVehicleParameter->id;
173                 bool ok = true;
174                 MSEdge* from = nullptr;
175                 const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
176                 StringTokenizer st(desc);
177                 std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
178                 MSStoppingPlace* bs = nullptr;
179                 MSEdge* to = nullptr;
180                 if (bsID != "") {
181                     bs = MSNet::getInstance()->getStoppingPlace(bsID, SUMO_TAG_BUS_STOP);
182                     if (bs == nullptr) {
183                         throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
184                     }
185                     to = &bs->getLane().getEdge();
186                 }
187                 double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
188                                     bs == nullptr ? -NUMERICAL_EPS : bs->getEndLanePosition());
189                 if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
190                     const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, pid.c_str(), ok);
191                     from = MSEdge::dictionary(fromID);
192                     if (from == nullptr) {
193                         throw ProcessError("The from edge '" + fromID + "' within a ride of person '" + pid + "' is not known.");
194                     }
195                     if (!myActivePlan->empty() && myActivePlan->back()->getDestination() != from) {
196                         throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActivePlan->back()->getDestination()->getID() + ").");
197                     }
198                     if (myActivePlan->empty()) {
199                         myActivePlan->push_back(new MSTransportable::Stage_Waiting(
200                                                     from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
201                     }
202                 } else if (myActivePlan->empty()) {
203                     throw ProcessError("The start edge for person '" + pid + "' is not known.");
204                 }
205                 if (to == nullptr) {
206                     const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, pid.c_str(), ok);
207                     to = MSEdge::dictionary(toID);
208                     if (to == nullptr) {
209                         throw ProcessError("The to edge '" + toID + "' within a ride of person '" + pid + "' is not known.");
210                     }
211                 }
212                 const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
213                 const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
214                 myActivePlan->push_back(new MSPerson::MSPersonStage_Driving(to, bs, arrivalPos, st.getVector(), intendedVeh, intendedDepart));
215                 break;
216             }
217             case SUMO_TAG_TRANSPORT:
218                 try {
219                     const std::string containerId = myVehicleParameter->id;
220                     bool ok = true;
221                     MSEdge* from = nullptr;
222                     const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, containerId.c_str(), ok);
223                     StringTokenizer st(desc);
224                     std::string csID = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
225                     MSStoppingPlace* cs = nullptr;
226                     if (csID != "") {
227                         cs = MSNet::getInstance()->getStoppingPlace(csID, SUMO_TAG_CONTAINER_STOP);
228                         if (cs == nullptr) {
229                             throw ProcessError("Unknown container stop '" + csID + "' for container '" + myVehicleParameter->id + "'.");
230                         }
231                     }
232                     double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
233                                         cs == nullptr ? -NUMERICAL_EPS : cs->getEndLanePosition());
234                     if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
235                         const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, containerId.c_str(), ok);
236                         from = MSEdge::dictionary(fromID);
237                         if (from == nullptr) {
238                             throw ProcessError("The from edge '" + fromID + "' within a transport of container '" + containerId + "' is not known.");
239                         }
240                         if (!myActiveContainerPlan->empty() && myActiveContainerPlan->back()->getDestination() != from) {
241                             throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
242                         }
243                         if (myActiveContainerPlan->empty()) {
244                             myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
245                                                                  from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
246                         }
247                     } else if (myActiveContainerPlan->empty()) {
248                         throw ProcessError("The start edge within a transport of container '" + containerId + "' is not known.");
249                     }
250                     const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, containerId.c_str(), ok);
251                     MSEdge* to = MSEdge::dictionary(toID);
252                     if (to == nullptr) {
253                         throw ProcessError("The to edge '" + toID + "' within a transport of container '" + containerId + "' is not known.");
254                     }
255                     myActiveContainerPlan->push_back(new MSContainer::MSContainerStage_Driving(to, cs, arrivalPos, st.getVector()));
256 
257                 } catch (ProcessError&) {
258                     deleteActivePlans();
259                     throw;
260                 }
261                 break;
262             case SUMO_TAG_TRANSHIP: {
263                 myActiveRoute.clear();
264                 bool ok = true;
265                 double departPos = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS, myVehicleParameter->id.c_str(), ok, 0);
266                 double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok, -NUMERICAL_EPS);
267                 double speed = DEFAULT_CONTAINER_TRANSHIP_SPEED;
268                 const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
269                 // need to check for explicitly set speed since we might have // DEFAULT_VEHTYPE
270                 if (vtype != nullptr && vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
271                     speed = vtype->getMaxSpeed();
272                 }
273                 speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, speed);
274                 if (speed <= 0) {
275                     throw ProcessError("Non-positive tranship speed for container  '" + myVehicleParameter->id + "'.");
276                 }
277                 std::string csID = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
278                 MSStoppingPlace* cs = nullptr;
279                 if (csID != "") {
280                     cs = MSNet::getInstance()->getStoppingPlace(csID, SUMO_TAG_CONTAINER_STOP);
281                     if (cs == nullptr) {
282                         throw ProcessError("Unknown container stop '" + csID + "' for container '" + myVehicleParameter->id + "'.");
283                     }
284                     arrivalPos = cs->getEndLanePosition();
285                 }
286                 if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
287                     MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
288                 } else {
289                     if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
290                         const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok);
291                         MSEdge* from = MSEdge::dictionary(fromID);
292                         if (from == nullptr) {
293                             throw ProcessError("The from edge '" + fromID + "' within a tranship of container '" + myVehicleParameter->id + "' is not known.");
294                         }
295                         const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok);
296                         MSEdge* to = MSEdge::dictionary(toID);
297                         if (to == nullptr) {
298                             throw ProcessError("The to edge '" + toID + "' within a tranship of container '" + myVehicleParameter->id + "' is not known.");
299                         }
300                         //the route of the container's tranship stage consists only of the 'from' and the 'to' edge
301                         myActiveRoute.push_back(from);
302                         myActiveRoute.push_back(to);
303                         if (myActiveRoute.empty()) {
304                             const std::string error = "No connection found between '" + from->getID() + "' and '" + to->getID() + "' for container '" + myVehicleParameter->id + "'.";
305                             if (!MSGlobals::gCheckRoutes) {
306                                 myActiveRoute.push_back(from);
307                             } else {
308                                 WRITE_ERROR(error);
309                             }
310                         }
311                     }
312                 }
313                 if (myActiveRoute.empty()) {
314                     throw ProcessError("No edges to tranship container '" + myVehicleParameter->id + "'.");
315                 }
316                 if (!myActiveContainerPlan->empty() && myActiveContainerPlan->back()->getDestination() != myActiveRoute.front()) {
317                     throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
318                 }
319                 if (myActiveContainerPlan->empty()) {
320                     myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
321                                                          myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, departPos, "start", true));
322                 }
323                 myActiveContainerPlan->push_back(new MSContainer::MSContainerStage_Tranship(myActiveRoute, cs, speed, departPos, arrivalPos));
324                 myActiveRoute.clear();
325                 break;
326             }
327             case SUMO_TAG_FLOW:
328                 parseFromViaTo("flow", attrs);
329                 break;
330             case SUMO_TAG_TRIP:
331                 parseFromViaTo("trip", attrs);
332                 break;
333             default:
334                 break;
335         }
336     } catch (ProcessError&) {
337         delete myVehicleParameter;
338         myVehicleParameter = nullptr;
339         throw;
340     }
341 }
342 
343 
344 void
openVehicleTypeDistribution(const SUMOSAXAttributes & attrs)345 MSRouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
346     bool ok = true;
347     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
348     if (ok) {
349         myCurrentVTypeDistribution = new RandomDistributor<MSVehicleType*>();
350         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
351             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
352             StringTokenizer st(vTypes);
353             while (st.hasNext()) {
354                 std::string vtypeID = st.next();
355                 MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(vtypeID, &myParsingRNG);
356                 if (type == nullptr) {
357                     throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
358                 }
359                 myCurrentVTypeDistribution->add(type, type->getDefaultProbability());
360             }
361         }
362     }
363 }
364 
365 
366 void
closeVehicleTypeDistribution()367 MSRouteHandler::closeVehicleTypeDistribution() {
368     if (myCurrentVTypeDistribution != nullptr) {
369         if (MSGlobals::gStateLoaded && MSNet::getInstance()->getVehicleControl().hasVTypeDistribution(myCurrentVTypeDistributionID)) {
370             delete myCurrentVTypeDistribution;
371             return;
372         }
373         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
374             delete myCurrentVTypeDistribution;
375             throw ProcessError("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
376         }
377         if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
378             delete myCurrentVTypeDistribution;
379             throw ProcessError("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
380         }
381         myCurrentVTypeDistribution = nullptr;
382     }
383 }
384 
385 
386 void
openRoute(const SUMOSAXAttributes & attrs)387 MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
388     myActiveRoute.clear();
389     myInsertStopEdgesAt = -1;
390     // check whether the id is really necessary
391     std::string rid;
392     if (myCurrentRouteDistribution != nullptr) {
393         myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
394         rid =  "distribution '" + myCurrentRouteDistributionID + "'";
395     } else if (myVehicleParameter != nullptr) {
396         // ok, a vehicle is wrapping the route,
397         //  we may use this vehicle's id as default
398         myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
399         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
400             WRITE_WARNING("Ids of internal routes are ignored (vehicle '" + myVehicleParameter->id + "').");
401         }
402     } else {
403         bool ok = true;
404         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
405         if (!ok) {
406             return;
407         }
408         rid = "'" + myActiveRouteID + "'";
409     }
410     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
411         rid =  "for vehicle '" + myVehicleParameter->id + "'";
412     }
413     bool ok = true;
414     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
415         MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
416     }
417     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
418     if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
419         WRITE_ERROR("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
420     }
421     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
422     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
423     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
424     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
425         WRITE_ERROR("Invalid cost for route '" + myActiveRouteID + "'.");
426     }
427 }
428 
429 
430 void
openTrip(const SUMOSAXAttributes &)431 MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
432 }
433 
434 
435 void
closeRoute(const bool mayBeDisconnected)436 MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
437     std::string type = "vehicle";
438     if (mayBeDisconnected) {
439         if (myVehicleParameter->repetitionNumber >= 0) {
440             type = "flow";
441         } else {
442             type = "trip";
443         }
444     }
445 
446     try {
447         if (myActiveRoute.size() == 0) {
448             delete myActiveRouteColor;
449             myActiveRouteColor = nullptr;
450             if (myActiveRouteRefID != "" && myCurrentRouteDistribution != nullptr) {
451                 const MSRoute* route = MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG);
452                 if (route != nullptr) {
453                     if (myCurrentRouteDistribution->add(route, myActiveRouteProbability)) {
454                         route->addReference();
455                     }
456                 }
457                 myActiveRouteID = "";
458                 myActiveRouteRefID = "";
459                 return;
460             }
461             if (myVehicleParameter != nullptr) {
462                 throw ProcessError("The route for " + type + " '" + myVehicleParameter->id + "' has no edges.");
463             } else {
464                 throw ProcessError("Route '" + myActiveRouteID + "' has no edges.");
465             }
466         }
467         if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
468             throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
469         }
470         MSRoute* route = new MSRoute(myActiveRouteID, myActiveRoute,
471                                      myVehicleParameter == nullptr || myVehicleParameter->repetitionNumber >= 1,
472                                      myActiveRouteColor, myActiveRouteStops);
473         route->setCosts(myCurrentCosts);
474         myActiveRoute.clear();
475         if (!MSRoute::dictionary(myActiveRouteID, route)) {
476             delete route;
477             if (!MSGlobals::gStateLoaded) {
478                 if (myVehicleParameter != nullptr) {
479                     if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == nullptr) {
480                         throw ProcessError("Another route for " + type + " '" + myVehicleParameter->id + "' exists.");
481                     } else {
482                         throw ProcessError("A vehicle with id '" + myVehicleParameter->id + "' already exists.");
483                     }
484                 } else {
485                     throw ProcessError("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
486                 }
487             }
488         } else {
489             if (myCurrentRouteDistribution != nullptr) {
490                 if (myCurrentRouteDistribution->add(route, myActiveRouteProbability)) {
491                     route->addReference();
492                 }
493             }
494         }
495         myActiveRouteID = "";
496         myActiveRouteColor = nullptr;
497         myActiveRouteStops.clear();
498     } catch (ProcessError&) {
499         delete myVehicleParameter;
500         throw;
501     }
502 }
503 
504 
505 void
openRouteDistribution(const SUMOSAXAttributes & attrs)506 MSRouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
507     // check whether the id is really necessary
508     bool ok = true;
509     if (myVehicleParameter != nullptr) {
510         // ok, a vehicle is wrapping the route,
511         //  we may use this vehicle's id as default
512         myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
513     } else {
514         myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
515         if (!ok) {
516             return;
517         }
518     }
519     myCurrentRouteDistribution = new RandomDistributor<const MSRoute*>();
520     std::vector<double> probs;
521     if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
522         bool ok = true;
523         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
524         while (st.hasNext()) {
525             probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
526         }
527     }
528     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
529         bool ok = true;
530         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
531         int probIndex = 0;
532         while (st.hasNext()) {
533             std::string routeID = st.next();
534             const MSRoute* route = MSRoute::dictionary(routeID, &myParsingRNG);
535             if (route == nullptr) {
536                 throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
537             }
538             const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : 1.0);
539             if (myCurrentRouteDistribution->add(route, prob, false)) {
540                 route->addReference();
541             }
542             probIndex++;
543         }
544         if (probs.size() > 0 && probIndex != (int)probs.size()) {
545             WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
546                           " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
547         }
548     }
549 }
550 
551 
552 void
closeRouteDistribution()553 MSRouteHandler::closeRouteDistribution() {
554     if (myCurrentRouteDistribution != nullptr) {
555         const bool haveSameID = MSRoute::dictionary(myCurrentRouteDistributionID, &myParsingRNG) != nullptr;
556         if (MSGlobals::gStateLoaded && haveSameID) {
557             delete myCurrentRouteDistribution;
558             return;
559         }
560         if (haveSameID) {
561             delete myCurrentRouteDistribution;
562             throw ProcessError("Another route (or distribution) with the id '" + myCurrentRouteDistributionID + "' exists.");
563         }
564         if (myCurrentRouteDistribution->getOverallProb() == 0) {
565             delete myCurrentRouteDistribution;
566             throw ProcessError("Route distribution '" + myCurrentRouteDistributionID + "' is empty.");
567         }
568         MSRoute::dictionary(myCurrentRouteDistributionID, myCurrentRouteDistribution, myVehicleParameter == nullptr);
569         myCurrentRouteDistribution = nullptr;
570     }
571 }
572 
573 
574 void
closeVehicle()575 MSRouteHandler::closeVehicle() {
576     // get nested route
577     const MSRoute* route = MSRoute::dictionary("!" + myVehicleParameter->id, &myParsingRNG);
578     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
579     if (myVehicleParameter->departProcedure == DEPART_GIVEN) {
580         // let's check whether this vehicle had to depart before the simulation starts
581         if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
582             if (route != nullptr) {
583                 route->addReference();
584                 route->release();
585             }
586             return;
587         }
588     }
589 
590     // get the vehicle's type
591     MSVehicleType* vtype = nullptr;
592 
593     try {
594         if (myVehicleParameter->vtypeid != "") {
595             vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
596             if (vtype == nullptr) {
597                 throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
598             }
599             if (vtype->getVehicleClass() == SVC_PEDESTRIAN) {
600                 WRITE_WARNING("Vehicle type '" + vtype->getID() + "' with vClass=pedestrian should only be used for persons and not for vehicle '" + myVehicleParameter->id + "'.");
601             }
602         } else {
603             // there should be one (at least the default one)
604             vtype = vehControl.getVType(DEFAULT_VTYPE_ID, &myParsingRNG);
605         }
606         if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
607             // if the route id was given, prefer that one
608             if (route != nullptr && !myAmLoadingState) {
609                 WRITE_WARNING("Ignoring child element 'route' for vehicle '" + myVehicleParameter->id + "' because attribute 'route' is set.");
610             }
611             route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
612         }
613         if (route == nullptr) {
614             // nothing found? -> error
615             if (myVehicleParameter->routeid != "") {
616                 throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
617             } else {
618                 throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
619             }
620         }
621         myActiveRouteID = "";
622 
623     } catch (ProcessError&) {
624         delete myVehicleParameter;
625         throw;
626     }
627 
628     // try to build the vehicle
629     SUMOVehicle* vehicle = nullptr;
630     if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
631         try {
632             vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype, !MSGlobals::gCheckRoutes);
633         } catch (const ProcessError& e) {
634             if (!MSGlobals::gCheckRoutes) {
635                 WRITE_WARNING(e.what());
636                 vehControl.deleteVehicle(nullptr, true);
637                 myVehicleParameter = nullptr;
638                 vehicle = nullptr;
639                 return;
640             } else {
641                 throw e;
642             }
643         }
644         const SUMOTime origDepart = myVehicleParameter->depart;
645         // maybe we do not want this vehicle to be inserted due to scaling
646         int quota = myAmLoadingState ? 1 : vehControl.getQuota();
647         if (quota > 0) {
648             registerLastDepart();
649             myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
650             vehControl.addVehicle(myVehicleParameter->id, vehicle);
651             for (int i = 1; i < quota; i++) {
652                 MSNet::getInstance()->getInsertionControl().add(vehicle);
653                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*myVehicleParameter);
654                 newPars->id = myVehicleParameter->id + "." + toString(i);
655                 newPars->depart = origDepart + MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
656                 vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
657                 vehControl.addVehicle(newPars->id, vehicle);
658             }
659             myVehicleParameter = nullptr;
660         } else {
661             vehControl.deleteVehicle(vehicle, true);
662             myVehicleParameter = nullptr;
663             vehicle = nullptr;
664         }
665     } else {
666         // strange: another vehicle with the same id already exists
667         if (!MSGlobals::gStateLoaded) {
668             // and was not loaded while loading a simulation state
669             // -> error
670             std::string veh_id = myVehicleParameter->id;
671             delete myVehicleParameter;
672             myVehicleParameter = nullptr;
673             throw ProcessError("Another vehicle with the id '" + veh_id + "' exists.");
674         } else {
675             // ok, it seems to be loaded previously while loading a simulation state
676             vehicle = nullptr;
677         }
678     }
679     // check whether the vehicle shall be added directly to the network or
680     //  shall stay in the internal buffer
681     if (vehicle != nullptr) {
682         if (vehicle->getParameter().departProcedure == DEPART_GIVEN) {
683             MSNet::getInstance()->getInsertionControl().add(vehicle);
684         }
685     }
686 }
687 
688 
689 void
closePerson()690 MSRouteHandler::closePerson() {
691     if (myActivePlan->size() == 0) {
692         const std::string error = "Person '" + myVehicleParameter->id + "' has no plan.";
693         delete myVehicleParameter;
694         myVehicleParameter = nullptr;
695         deleteActivePlans();
696         throw ProcessError(error);
697     }
698     // let's check whether this person had to depart before the simulation starts
699     if (!(myAddVehiclesDirectly || checkLastDepart())
700             || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
701         delete myVehicleParameter;
702         myVehicleParameter = nullptr;
703         deleteActivePlans();
704         return;
705     }
706     // type existence has been checked on opening
707     MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
708     MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(myVehicleParameter, type, myActivePlan, &myParsingRNG);
709     // @todo: consider myScale?
710     if (MSNet::getInstance()->getPersonControl().add(person)) {
711         registerLastDepart();
712     } else {
713         ProcessError error("Another person with the id '" + myVehicleParameter->id + "' exists.");
714         delete person;
715         throw error;
716     }
717     myVehicleParameter = nullptr;
718     myActivePlan = nullptr;
719 }
720 
721 
722 void
closePersonFlow()723 MSRouteHandler::closePersonFlow() {
724     if (myActivePlan->size() == 0) {
725         const std::string error = "personFlow '" + myVehicleParameter->id + "' has no plan.";
726         delete myVehicleParameter;
727         myVehicleParameter = nullptr;
728         deleteActivePlans();
729         throw ProcessError(error);
730     }
731     // let's check whether this person had to depart before the simulation starts
732     if (!(myAddVehiclesDirectly || checkLastDepart())
733             || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
734         delete myVehicleParameter;
735         myVehicleParameter = nullptr;
736         deleteActivePlans();
737         return;
738     }
739     // type existence has been checked on opening
740     MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
741     // instantiate all persons of this flow
742     int i = 0;
743     registerLastDepart();
744     std::string baseID = myVehicleParameter->id;
745     if (myVehicleParameter->repetitionProbability > 0) {
746         if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
747             throw ProcessError("probabilistic personFlow '" + myVehicleParameter->id + "' must specify end time");
748         } else {
749             for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
750                 if (RandHelper::rand(&myParsingRNG) < myVehicleParameter->repetitionProbability) {
751                     addFlowPerson(t, type, baseID, i++);
752                 }
753             }
754         }
755     } else {
756         SUMOTime depart = myVehicleParameter->depart;
757         for (; i < myVehicleParameter->repetitionNumber; i++) {
758             addFlowPerson(depart, type, baseID, i);
759             depart += myVehicleParameter->repetitionOffset;
760         }
761     }
762 
763     myVehicleParameter = nullptr;
764     myActivePlan = nullptr;
765 }
766 
767 void
addFlowPerson(SUMOTime depart,MSVehicleType * type,const std::string & baseID,int i)768 MSRouteHandler::addFlowPerson(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
769     if (i > 0) {
770         // copy parameter and plan because the person takes over responsibility
771         SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
772         *copyParam = *myVehicleParameter;
773         myVehicleParameter = copyParam;
774         MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
775         for (MSTransportable::Stage* s : *myActivePlan) {
776             copyPlan->push_back(s->clone());
777         }
778         myActivePlan = copyPlan;
779     }
780     myVehicleParameter->id = baseID + "." + toString(i);
781     myVehicleParameter->depart = depart;
782     MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(myVehicleParameter, type, myActivePlan, &myParsingRNG);
783     if (!MSNet::getInstance()->getPersonControl().add(person)) {
784         ProcessError error("Another person with the id '" + myVehicleParameter->id + "' exists.");
785         delete person;
786         throw error;
787     }
788 }
789 
790 void
closeVType()791 MSRouteHandler::closeVType() {
792     MSVehicleType* vehType = MSVehicleType::build(*myCurrentVType);
793     delete myCurrentVType;
794     myCurrentVType = nullptr;
795     if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
796         const std::string id = vehType->getID();
797         delete vehType;
798         if (!MSGlobals::gStateLoaded) {
799             throw ProcessError("Another vehicle type (or distribution) with the id '" + id + "' exists.");
800         }
801     } else {
802         if (myCurrentVTypeDistribution != nullptr) {
803             myCurrentVTypeDistribution->add(vehType, vehType->getDefaultProbability());
804         }
805     }
806 }
807 
808 
809 void
closeContainer()810 MSRouteHandler::closeContainer() {
811     if (myActiveContainerPlan->size() == 0) {
812         throw ProcessError("Container '" + myVehicleParameter->id + "' has no plan.");
813     }
814     MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
815     if (type == nullptr) {
816         throw ProcessError("The type '" + myVehicleParameter->vtypeid + "' for container '" + myVehicleParameter->id + "' is not known.");
817     }
818     MSTransportable* container = MSNet::getInstance()->getContainerControl().buildContainer(myVehicleParameter, type, myActiveContainerPlan);
819     // @todo: consider myScale?
820     if (myAddVehiclesDirectly || checkLastDepart()) {
821         if (MSNet::getInstance()->getContainerControl().add(container)) {
822             registerLastDepart();
823         } else {
824             ProcessError error("Another container with the id '" + myVehicleParameter->id + "' exists.");
825             delete container;
826             throw error;
827         }
828     } else {
829         // warning already given
830         delete container;
831     }
832     myVehicleParameter = nullptr;
833     myActiveContainerPlan = nullptr;
834 }
835 
836 
837 void
closeFlow()838 MSRouteHandler::closeFlow() {
839     myInsertStopEdgesAt = -1;
840     if (myVehicleParameter->repetitionNumber == 0) {
841         delete myVehicleParameter;
842         myVehicleParameter = nullptr;
843         return;
844     }
845     // let's check whether vehicles had to depart before the simulation starts
846     myVehicleParameter->repetitionsDone = 0;
847     if (myVehicleParameter->repetitionProbability < 0) {
848         const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
849         while (myVehicleParameter->repetitionsDone * myVehicleParameter->repetitionOffset < offsetToBegin) {
850             myVehicleParameter->repetitionsDone++;
851             if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
852                 delete myVehicleParameter;
853                 myVehicleParameter = nullptr;
854                 return;
855             }
856         }
857     }
858     if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG) == nullptr) {
859         throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
860     }
861     if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
862         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
863         closeRoute(true);
864     }
865     if (MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
866         throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
867     }
868     myActiveRouteID = "";
869 
870     // check whether the vehicle shall be added directly to the network or
871     //  shall stay in the internal buffer
872     if (myAddVehiclesDirectly || checkLastDepart()) {
873         if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
874             registerLastDepart();
875         } else {
876             throw ProcessError("Another flow with the id '" + myVehicleParameter->id + "' exists.");
877         }
878     }
879     myVehicleParameter = nullptr;
880 }
881 
882 
883 void
closeTrip()884 MSRouteHandler::closeTrip() {
885     myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
886     closeRoute(true);
887     closeVehicle();
888 }
889 
890 
891 void
addStop(const SUMOSAXAttributes & attrs)892 MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
893     std::string errorSuffix;
894     if (myActivePlan != nullptr) {
895         errorSuffix = " in person '" + myVehicleParameter->id + "'.";
896     } else if (myVehicleParameter != nullptr) {
897         errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
898     } else if (myActiveContainerPlan != nullptr) {
899         errorSuffix = " in container '" + myVehicleParameter->id + "'.";
900     } else {
901         errorSuffix = " in route '" + myActiveRouteID + "'.";
902     }
903     SUMOVehicleParameter::Stop stop;
904     bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
905     if (!ok) {
906         return;
907     }
908     const MSEdge* edge = nullptr;
909     MSStoppingPlace* toStop = nullptr;
910     // try to parse the assigned bus stop
911     if (stop.busstop != "") {
912         // ok, we have a bus stop
913         MSStoppingPlace* bs = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
914         if (bs == nullptr) {
915             WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
916             return;
917         }
918         toStop = bs;
919         const MSLane& l = bs->getLane();
920         stop.lane = l.getID();
921         stop.endPos = bs->getEndLanePosition();
922         stop.startPos = bs->getBeginLanePosition();
923         edge = &l.getEdge();
924     } //try to parse the assigned container stop
925     else if (stop.containerstop != "") {
926         // ok, we have obviously a container stop
927         MSStoppingPlace* cs = MSNet::getInstance()->getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
928         if (cs == nullptr) {
929             WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
930             return;
931         }
932         toStop = cs;
933         const MSLane& l = cs->getLane();
934         stop.lane = l.getID();
935         stop.endPos = cs->getEndLanePosition();
936         stop.startPos = cs->getBeginLanePosition();
937         edge = &l.getEdge();
938     } //try to parse the assigned parking area
939     else if (stop.parkingarea != "") {
940         // ok, we have obviously a parking area
941         MSStoppingPlace* pa = MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
942         if (pa == nullptr) {
943             WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
944             return;
945         }
946         toStop = pa;
947         const MSLane& l = pa->getLane();
948         stop.lane = l.getID();
949         stop.endPos = pa->getEndLanePosition();
950         stop.startPos = pa->getBeginLanePosition();
951         edge = &l.getEdge();
952     } else if (stop.chargingStation != "") {
953         // ok, we have a charging station
954         MSStoppingPlace* cs = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
955         if (cs == nullptr) {
956             WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
957             return;
958         }
959         toStop = cs;
960         const MSLane& l = cs->getLane();
961         stop.lane = l.getID();
962         stop.endPos = cs->getEndLanePosition();
963         stop.startPos = cs->getBeginLanePosition();
964         edge = &l.getEdge();
965     } else {
966         // no, the lane and the position should be given
967         // get the lane
968         stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
969         if (ok && stop.lane != "") {
970             if (MSLane::dictionary(stop.lane) == nullptr) {
971                 WRITE_ERROR("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
972                 return;
973             }
974         } else {
975             if (myActivePlan && !myActivePlan->empty()) {
976                 const MSStoppingPlace* bs = myActivePlan->back()->getDestinationStop();
977                 if (bs != nullptr) {
978                     edge = &bs->getLane().getEdge();
979                     stop.lane = bs->getLane().getID();
980                     stop.endPos = bs->getEndLanePosition();
981                     stop.startPos = bs->getBeginLanePosition();
982                 } else {
983                     edge = myActivePlan->back()->getDestination();
984                     stop.lane = edge->getLanes()[0]->getID();
985                     stop.endPos = myActivePlan->back()->getArrivalPos();
986                     stop.startPos = stop.endPos - POSITION_EPS;
987                 }
988             } else {
989                 WRITE_ERROR("A stop must be placed on a busStop, a chargingStation, a containerStop a parkingArea or a lane" + errorSuffix);
990                 return;
991             }
992         }
993         edge = &MSLane::dictionary(stop.lane)->getEdge();
994         stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, MSLane::dictionary(stop.lane)->getLength());
995         if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
996             WRITE_WARNING("Deprecated attribute 'pos' in description of stop" + errorSuffix);
997             stop.endPos = attrs.getOpt<double>(SUMO_ATTR_POSITION, nullptr, ok, stop.endPos);
998         }
999         stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - 2 * POSITION_EPS));
1000         const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
1001         if (!ok || !checkStopPos(stop.startPos, stop.endPos, MSLane::dictionary(stop.lane)->getLength(), POSITION_EPS, friendlyPos)) {
1002             WRITE_ERROR("Invalid start or end position for stop on lane '" + stop.lane + "'" + errorSuffix);
1003             return;
1004         }
1005     }
1006     if (myActivePlan) {
1007         if (myActivePlan->empty()) {
1008             double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1009                                ? myVehicleParameter->departPos
1010                                : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1011             myActivePlan->push_back(new MSTransportable::Stage_Waiting(
1012                                         edge, toStop, -1, myVehicleParameter->depart, departPos, "start", true));
1013         } else if (myActivePlan->back()->getDestination() != edge) {
1014             throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + edge->getID() + "!=" + myActivePlan->back()->getDestination()->getID() + ").");
1015         }
1016         std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "waiting");
1017         double pos = (stop.startPos + stop.endPos) / 2.;
1018         if (!myActivePlan->empty()) {
1019             pos = myActivePlan->back()->getArrivalPos();
1020         }
1021         myActivePlan->push_back(new MSTransportable::Stage_Waiting(
1022                                     &MSLane::dictionary(stop.lane)->getEdge(), toStop, stop.duration, stop.until, pos, actType, false));
1023 
1024     } else if (myActiveContainerPlan) {
1025         if (myActiveContainerPlan->empty()) {
1026             double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1027                                ? myVehicleParameter->departPos
1028                                : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1029             myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
1030                                                  &MSLane::dictionary(stop.lane)->getEdge(), toStop, -1, myVehicleParameter->depart, departPos, "start", true));
1031         } else if (myActiveContainerPlan->back()->getDestination() != &MSLane::dictionary(stop.lane)->getEdge()) {
1032             throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + MSLane::dictionary(stop.lane)->getEdge().getID() + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
1033         }
1034         std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "waiting");
1035         myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
1036                                              &MSLane::dictionary(stop.lane)->getEdge(), toStop, stop.duration, stop.until, stop.startPos, actType, false));
1037     } else if (myVehicleParameter != nullptr) {
1038         myVehicleParameter->stops.push_back(stop);
1039     } else {
1040         myActiveRouteStops.push_back(stop);
1041     }
1042     if (myInsertStopEdgesAt >= 0) {
1043         //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
1044         myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
1045         myInsertStopEdgesAt++;
1046     }
1047 }
1048 
1049 
1050 void
parseWalkPositions(const SUMOSAXAttributes & attrs,const std::string & personID,const MSEdge * fromEdge,const MSEdge * & toEdge,double & departPos,double & arrivalPos,MSStoppingPlace * & bs,const MSTransportable::Stage * const lastStage,bool & ok)1051 MSRouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
1052                                    const MSEdge* fromEdge, const MSEdge*& toEdge,
1053                                    double& departPos, double& arrivalPos, MSStoppingPlace*& bs,
1054                                    const MSTransportable::Stage* const lastStage, bool& ok) {
1055     const std::string description = "person '" + personID + "' walking from " + fromEdge->getID();
1056 
1057     if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1058         WRITE_WARNING("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops.");
1059     }
1060     departPos = 0.;
1061     if (lastStage != nullptr) {
1062         if (lastStage->getDestinationStop() != nullptr) {
1063             departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge);
1064         } else if (lastStage->getDestination() == fromEdge) {
1065             departPos = lastStage->getArrivalPos();
1066         } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
1067             departPos = fromEdge->getLength();
1068         }
1069     }
1070 
1071     std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1072     if (bsID != "") {
1073         bs = MSNet::getInstance()->getStoppingPlace(bsID, SUMO_TAG_BUS_STOP);
1074         if (bs == nullptr) {
1075             throw ProcessError("Unknown bus stop '" + bsID + "' for " + description + ".");
1076         }
1077         arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
1078         if (arrivalPos < 0) {
1079             throw ProcessError("Bus stop '" + bsID + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
1080         }
1081         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1082             const double length = toEdge != nullptr ? toEdge->getLength() : bs->getLane().getLength();
1083             const double arrPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, description, length,
1084                                   attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1085             if (arrPos >= bs->getBeginLanePosition() && arrPos < bs->getEndLanePosition()) {
1086                 arrivalPos = arrPos;
1087             } else {
1088                 WRITE_WARNING("Ignoring arrivalPos for " + description + " because it is outside the given stop '" + toString(SUMO_ATTR_BUS_STOP) + "'.");
1089                 arrivalPos = bs->getAccessPos(&bs->getLane().getEdge());
1090             }
1091         }
1092     } else {
1093         if (toEdge == nullptr) {
1094             throw ProcessError("No destination edge for " + description + ".");
1095         }
1096         if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1097             arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, description, toEdge->getLength(),
1098                          attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1099         } else {
1100             arrivalPos = toEdge->getLength() / 2.;
1101         }
1102     }
1103 }
1104 
1105 
1106 void
addPersonTrip(const SUMOSAXAttributes & attrs)1107 MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1108     myActiveRoute.clear();
1109     bool ok = true;
1110     MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
1111     const char* const id = myVehicleParameter->id.c_str();
1112     const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1113     if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1114         throw ProcessError("Non-positive walking duration for  '" + myVehicleParameter->id + "'.");
1115     }
1116     const std::string fromID = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id, ok, "");
1117     const MSEdge* from = fromID != "" || myActivePlan->empty() ? MSEdge::dictionary(fromID) : myActivePlan->back()->getDestination();
1118     if (from == nullptr) {
1119         throw ProcessError("The from edge '" + fromID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
1120     }
1121     const std::string toID = attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "");
1122     const MSEdge* to = MSEdge::dictionary(toID);
1123     if (toID != "" && to == nullptr) {
1124         throw ProcessError("The to edge '" + toID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
1125     }
1126     double departPos = 0;
1127     double arrivalPos = 0;
1128     MSStoppingPlace* stoppingPlace = nullptr;
1129     parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
1130 
1131     const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1132     SVCPermissions modeSet = 0;
1133     for (StringTokenizer st(modes); st.hasNext();) {
1134         const std::string mode = st.next();
1135         if (mode == "car") {
1136             modeSet |= SVC_PASSENGER;
1137         } else if (mode == "bicycle") {
1138             modeSet |= SVC_BICYCLE;
1139         } else if (mode == "public") {
1140             modeSet |= SVC_BUS;
1141         } else {
1142             throw InvalidArgument("Unknown person mode '" + mode + "'.");
1143         }
1144     }
1145     const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
1146     for (StringTokenizer st(types); st.hasNext();) {
1147         const std::string vtypeid = st.next();
1148         if (vehControl.getVType(vtypeid) == nullptr) {
1149             throw InvalidArgument("The vehicle type '" + vtypeid + "' in a trip for person '" + myVehicleParameter->id + "' is not known.");
1150         }
1151         modeSet |= SVC_PASSENGER;
1152     }
1153     const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
1154     if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1155         throw ProcessError("Non-positive walking speed for  '" + myVehicleParameter->id + "'.");
1156     }
1157     const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1158     const double departPosLat = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, 0);
1159     if (ok) {
1160         myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1161         MSStoppingPlace* fromStop = myActivePlan->empty() ? nullptr : myActivePlan->back()->getDestinationStop();
1162         myActivePlan->push_back(new MSTransportable::Stage_Trip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to, stoppingPlace, duration, modeSet, types, speed, walkFactor, departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
1163     }
1164     myActiveRoute.clear();
1165 }
1166 
1167 
1168 void
addWalk(const SUMOSAXAttributes & attrs)1169 MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1170     try {
1171         myActiveRoute.clear();
1172         bool ok = true;
1173         const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
1174         if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1175             throw ProcessError("Non-positive walking duration for  '" + myVehicleParameter->id + "'.");
1176         }
1177         double speed = -1; // default to vType speed
1178         if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
1179             speed = attrs.get<double>(SUMO_ATTR_SPEED, nullptr, ok);
1180             if (speed <= 0) {
1181                 throw ProcessError("Non-positive walking speed for  '" + myVehicleParameter->id + "'.");
1182             }
1183         }
1184         double departPos = 0;
1185         double arrivalPos = 0;
1186         MSStoppingPlace* bs = nullptr;
1187         if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1188             const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1189             const MSRoute* route = MSRoute::dictionary(routeID, &myParsingRNG);
1190             if (route == nullptr) {
1191                 throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1192             }
1193             myActiveRoute = route->getEdges();
1194         } else {
1195             MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
1196         }
1197         if (myActivePlan->empty()) {
1198             double initialDepartPos = myVehicleParameter->departPos;
1199             if (myVehicleParameter->departPosProcedure == DEPART_POS_RANDOM) {
1200                 initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
1201             }
1202             myActivePlan->push_back(new MSTransportable::Stage_Waiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1203         }
1204         parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActivePlan->back(), ok);
1205         if (myActiveRoute.empty()) {
1206             throw ProcessError("No edges to walk for person '" + myVehicleParameter->id + "'.");
1207         }
1208         if (myActivePlan->back()->getDestination() != myActiveRoute.front() &&
1209                 myActivePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getFromJunction() &&
1210                 myActivePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getToJunction()) {
1211             if (myActivePlan->back()->getDestinationStop() == nullptr || myActivePlan->back()->getDestinationStop()->getAccessPos(myActiveRoute.front()) < 0.) {
1212                 throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + " not connected to " + myActivePlan->back()->getDestination()->getID() + ").");
1213             }
1214         }
1215         const double departPosLat = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, 0);
1216         myActivePlan->push_back(new MSPerson::MSPersonStage_Walking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat));
1217         myActiveRoute.clear();
1218     } catch (ProcessError&) {
1219         deleteActivePlans();
1220         throw;
1221     }
1222 }
1223 
1224 
1225 void
addPerson(const SUMOSAXAttributes &)1226 MSRouteHandler::addPerson(const SUMOSAXAttributes& /*attrs*/) {
1227 }
1228 
1229 
1230 void
addContainer(const SUMOSAXAttributes &)1231 MSRouteHandler::addContainer(const SUMOSAXAttributes& /*attrs*/) {
1232 }
1233 
1234 
1235 void
addRide(const SUMOSAXAttributes &)1236 MSRouteHandler::addRide(const SUMOSAXAttributes& /*attrs*/) {
1237 }
1238 
1239 
1240 void
addTransport(const SUMOSAXAttributes &)1241 MSRouteHandler::addTransport(const SUMOSAXAttributes& /*attrs*/) {
1242 }
1243 
1244 
1245 void
addTranship(const SUMOSAXAttributes &)1246 MSRouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
1247 }
1248 
1249 /****************************************************************************/
1250