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    RORouteHandler.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 <iostream>
31 #include <utils/iodevices/OutputDevice.h>
32 #include <utils/xml/SUMOSAXHandler.h>
33 #include <utils/xml/SUMOXMLDefinitions.h>
34 #include <utils/geom/GeoConvHelper.h>
35 #include <utils/common/MsgHandler.h>
36 #include <utils/common/StringTokenizer.h>
37 #include <utils/common/UtilExceptions.h>
38 #include <utils/options/OptionsCont.h>
39 #include <utils/vehicle/SUMOVehicleParserHelper.h>
40 #include <utils/xml/SUMOSAXReader.h>
41 #include <utils/xml/XMLSubSys.h>
42 #include <utils/iodevices/OutputDevice_String.h>
43 #include "ROPerson.h"
44 #include "RONet.h"
45 #include "ROEdge.h"
46 #include "ROLane.h"
47 #include "RORouteDef.h"
48 #include "RORouteHandler.h"
49 
50 
51 // ===========================================================================
52 // method definitions
53 // ===========================================================================
RORouteHandler(RONet & net,const std::string & file,const bool tryRepair,const bool emptyDestinationsAllowed,const bool ignoreErrors,const bool checkSchema)54 RORouteHandler::RORouteHandler(RONet& net, const std::string& file,
55                                const bool tryRepair,
56                                const bool emptyDestinationsAllowed,
57                                const bool ignoreErrors,
58                                const bool checkSchema) :
59     SUMORouteHandler(file, checkSchema ? "routes" : ""),
60     myNet(net),
61     myActivePerson(nullptr),
62     myActiveContainerPlan(nullptr),
63     myActiveContainerPlanSize(0),
64     myTryRepair(tryRepair),
65     myEmptyDestinationsAllowed(emptyDestinationsAllowed),
66     myErrorOutput(ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
67     myBegin(string2time(OptionsCont::getOptions().getString("begin"))),
68     myKeepVTypeDist(OptionsCont::getOptions().getBool("keep-vtype-distributions")),
69     myCurrentVTypeDistribution(nullptr),
70     myCurrentAlternatives(nullptr),
71     myLaneTree(nullptr) {
72     myActiveRoute.reserve(100);
73 }
74 
75 
~RORouteHandler()76 RORouteHandler::~RORouteHandler() {
77 }
78 
79 
80 void
parseFromViaTo(std::string element,const SUMOSAXAttributes & attrs)81 RORouteHandler::parseFromViaTo(std::string element,
82                                const SUMOSAXAttributes& attrs) {
83     myActiveRoute.clear();
84     bool useTaz = OptionsCont::getOptions().getBool("with-taz");
85     if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
86         WRITE_WARNING("Taz usage was requested but no taz present in " + element + " '" + myVehicleParameter->id + "'!");
87         useTaz = false;
88     }
89     bool ok = true;
90     const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
91     if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_FROM)) && myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET)) {
92         const ROEdge* fromTaz = myNet.getEdge(myVehicleParameter->fromTaz + "-source");
93         if (fromTaz == nullptr) {
94             myErrorOutput->inform("Source taz '" + myVehicleParameter->fromTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
95         } else if (fromTaz->getNumSuccessors() == 0) {
96             myErrorOutput->inform("Source taz '" + myVehicleParameter->fromTaz + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
97         } else {
98             myActiveRoute.push_back(fromTaz);
99         }
100     } else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
101         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok, true), false, myActiveRoute, rid);
102     } else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
103         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok, true), true, myActiveRoute, rid);
104     } else {
105         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid);
106     }
107     if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_VIALONLAT) && !attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
108         myInsertStopEdgesAt = (int)myActiveRoute.size();
109     }
110     ConstROEdgeVector viaEdges;
111     if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
112         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok, true), false, viaEdges, rid);
113     } else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
114         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok, true), true, viaEdges, rid);
115     } else {
116         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true), viaEdges, rid);
117     }
118     for (ConstROEdgeVector::const_iterator i = viaEdges.begin(); i != viaEdges.end(); ++i) {
119         myActiveRoute.push_back(*i);
120         myVehicleParameter->via.push_back((*i)->getID());
121     }
122 
123     if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_TO)) && myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
124         const ROEdge* toTaz = myNet.getEdge(myVehicleParameter->toTaz + "-sink");
125         if (toTaz == nullptr) {
126             myErrorOutput->inform("Sink taz '" + myVehicleParameter->toTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
127         } else if (toTaz->getNumPredecessors() == 0) {
128             myErrorOutput->inform("Sink taz '" + myVehicleParameter->toTaz + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
129         } else {
130             myActiveRoute.push_back(toTaz);
131         }
132     } else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
133         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, myActiveRoute, rid);
134     } else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
135         parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, myActiveRoute, rid);
136     } else {
137         parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid);
138     }
139     myActiveRouteID = "!" + myVehicleParameter->id;
140     if (myVehicleParameter->routeid == "") {
141         myVehicleParameter->routeid = myActiveRouteID;
142     }
143 }
144 
145 
146 void
myStartElement(int element,const SUMOSAXAttributes & attrs)147 RORouteHandler::myStartElement(int element,
148                                const SUMOSAXAttributes& attrs) {
149     SUMORouteHandler::myStartElement(element, attrs);
150     switch (element) {
151         case SUMO_TAG_PERSON:
152         case SUMO_TAG_PERSONFLOW: {
153             SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
154             if (type == nullptr) {
155                 myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
156                 type = myNet.getVehicleTypeSecure(DEFAULT_PEDTYPE_ID);
157             }
158             myActivePerson = new ROPerson(*myVehicleParameter, type);
159             break;
160         }
161         case SUMO_TAG_RIDE: {
162             std::vector<ROPerson::PlanItem*>& plan = myActivePerson->getPlan();
163             const std::string pid = myVehicleParameter->id;
164             bool ok = true;
165             ROEdge* from = nullptr;
166             if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
167                 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, pid.c_str(), ok);
168                 from = myNet.getEdge(fromID);
169                 if (from == nullptr) {
170                     throw ProcessError("The from edge '" + fromID + "' within a ride of person '" + pid + "' is not known.");
171                 }
172                 if (!plan.empty() && plan.back()->getDestination() != from) {
173                     throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + fromID + "!=" + plan.back()->getDestination()->getID() + ").");
174                 }
175             } else if (plan.empty()) {
176                 throw ProcessError("The start edge for person '" + pid + "' is not known.");
177             }
178             ROEdge* to = nullptr;
179             const SUMOVehicleParameter::Stop* stop = nullptr;
180             const std::string toID = attrs.getOpt<std::string>(SUMO_ATTR_TO, pid.c_str(), ok, "");
181             const std::string busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, pid.c_str(), ok, "");
182             if (toID != "") {
183                 to = myNet.getEdge(toID);
184                 if (to == nullptr) {
185                     throw ProcessError("The to edge '" + toID + "' within a ride of person '" + pid + "' is not known.");
186                 }
187             } else if (busStopID != "") {
188                 stop = myNet.getStoppingPlace(busStopID, SUMO_TAG_BUS_STOP);
189                 if (stop == nullptr) {
190                     throw ProcessError("Unknown bus stop '" + busStopID + "' within a ride of '" + myVehicleParameter->id + "'.");
191                 }
192                 to = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop->lane));
193             } else {
194                 throw ProcessError("The to edge '' within a ride of '" + myVehicleParameter->id + "' is not known.");
195             }
196             double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
197                                 stop == nullptr ? -NUMERICAL_EPS : stop->endPos);
198             const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
199             myActivePerson->addRide(from, to, desc, arrivalPos, busStopID);
200             break;
201         }
202         case SUMO_TAG_CONTAINER:
203             myActiveContainerPlan = new OutputDevice_String(false, 1);
204             myActiveContainerPlanSize = 0;
205             myActiveContainerPlan->openTag(SUMO_TAG_CONTAINER);
206             (*myActiveContainerPlan) << attrs;
207             break;
208         case SUMO_TAG_TRANSPORT: {
209             myActiveContainerPlan->openTag(SUMO_TAG_TRANSPORT);
210             (*myActiveContainerPlan) << attrs;
211             myActiveContainerPlan->closeTag();
212             myActiveContainerPlanSize++;
213             break;
214         }
215         case SUMO_TAG_TRANSHIP: {
216             if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
217                 // copy walk as it is
218                 // XXX allow --repair?
219                 myActiveContainerPlan->openTag(SUMO_TAG_TRANSHIP);
220                 (*myActiveContainerPlan) << attrs;
221                 myActiveContainerPlan->closeTag();
222                 myActiveContainerPlanSize++;
223             } else {
224                 //routePerson(attrs, *myActiveContainerPlan);
225             }
226             break;
227         }
228         case SUMO_TAG_FLOW:
229             myActiveRouteProbability = DEFAULT_VEH_PROB;
230             parseFromViaTo("flow", attrs);
231             break;
232         case SUMO_TAG_TRIP:
233             myActiveRouteProbability = DEFAULT_VEH_PROB;
234             parseFromViaTo("trip", attrs);
235             break;
236         default:
237             break;
238     }
239 }
240 
241 
242 void
openVehicleTypeDistribution(const SUMOSAXAttributes & attrs)243 RORouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
244     bool ok = true;
245     myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
246     if (ok) {
247         myCurrentVTypeDistribution = new RandomDistributor<SUMOVTypeParameter*>();
248         if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
249             const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
250             StringTokenizer st(vTypes);
251             while (st.hasNext()) {
252                 const std::string typeID = st.next();
253                 SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
254                 if (type == nullptr) {
255                     myErrorOutput->inform("Unknown vehicle type '" + typeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
256                 } else {
257                     myCurrentVTypeDistribution->add(type, 1.);
258                 }
259             }
260         }
261     }
262 }
263 
264 
265 void
closeVehicleTypeDistribution()266 RORouteHandler::closeVehicleTypeDistribution() {
267     if (myCurrentVTypeDistribution != nullptr) {
268         if (myCurrentVTypeDistribution->getOverallProb() == 0) {
269             delete myCurrentVTypeDistribution;
270             myErrorOutput->inform("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
271         } else if (!myNet.addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
272             delete myCurrentVTypeDistribution;
273             myErrorOutput->inform("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
274         }
275         myCurrentVTypeDistribution = nullptr;
276     }
277 }
278 
279 
280 void
openRoute(const SUMOSAXAttributes & attrs)281 RORouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
282     myActiveRoute.clear();
283     myInsertStopEdgesAt = -1;
284     // check whether the id is really necessary
285     std::string rid;
286     if (myCurrentAlternatives != nullptr) {
287         myActiveRouteID = myCurrentAlternatives->getID();
288         rid =  "distribution '" + myCurrentAlternatives->getID() + "'";
289     } else if (myVehicleParameter != nullptr) {
290         // ok, a vehicle is wrapping the route,
291         //  we may use this vehicle's id as default
292         myVehicleParameter->routeid = myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
293         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
294             WRITE_WARNING("Ids of internal routes are ignored (vehicle '" + myVehicleParameter->id + "').");
295         }
296     } else {
297         bool ok = true;
298         myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
299         if (!ok) {
300             return;
301         }
302         rid = "'" + myActiveRouteID + "'";
303     }
304     if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
305         rid =  "for vehicle '" + myVehicleParameter->id + "'";
306     }
307     bool ok = true;
308     if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
309         parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
310     }
311     myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
312     if (myActiveRouteRefID != "" && myNet.getRouteDef(myActiveRouteRefID) == nullptr) {
313         myErrorOutput->inform("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
314     }
315     if (myCurrentAlternatives != nullptr && !attrs.hasAttribute(SUMO_ATTR_PROB)) {
316         WRITE_WARNING("No probability for a route in '" + rid + "', using default.");
317     }
318     myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
319     if (ok && myActiveRouteProbability < 0) {
320         myErrorOutput->inform("Invalid probability for route '" + myActiveRouteID + "'.");
321     }
322     myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
323     ok = true;
324     myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
325     if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
326         myErrorOutput->inform("Invalid cost for route '" + myActiveRouteID + "'.");
327     }
328 }
329 
330 
331 void
openTrip(const SUMOSAXAttributes &)332 RORouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
333 }
334 
335 
336 void
closeRoute(const bool mayBeDisconnected)337 RORouteHandler::closeRoute(const bool mayBeDisconnected) {
338     if (myActiveRoute.size() == 0) {
339         if (myActiveRouteRefID != "" && myCurrentAlternatives != nullptr) {
340             myCurrentAlternatives->addAlternativeDef(myNet.getRouteDef(myActiveRouteRefID));
341             myActiveRouteID = "";
342             myActiveRouteRefID = "";
343             return;
344         }
345         if (myVehicleParameter != nullptr) {
346             myErrorOutput->inform("The route for vehicle '" + myVehicleParameter->id + "' has no edges.");
347         } else {
348             myErrorOutput->inform("Route '" + myActiveRouteID + "' has no edges.");
349         }
350         myActiveRouteID = "";
351         myActiveRouteStops.clear();
352         return;
353     }
354     if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
355         myErrorOutput->inform("The routing information for vehicle '" + myVehicleParameter->id + "' is insufficient.");
356         myActiveRouteID = "";
357         myActiveRouteStops.clear();
358         return;
359     }
360     if (!mayBeDisconnected && OptionsCont::getOptions().exists("no-internal-links") && !OptionsCont::getOptions().getBool("no-internal-links")) {
361         // fix internal edges which did not get parsed
362         const ROEdge* last = nullptr;
363         ConstROEdgeVector fullRoute;
364         for (const ROEdge* roe : myActiveRoute) {
365             if (last != nullptr) {
366                 for (const ROEdge* intern : last->getSuccessors()) {
367                     if (intern->isInternal() && intern->getSuccessors().size() == 1 && intern->getSuccessors().front() == roe) {
368                         fullRoute.push_back(intern);
369                     }
370                 }
371             }
372             fullRoute.push_back(roe);
373             last = roe;
374         }
375         myActiveRoute = fullRoute;
376     }
377     RORoute* route = new RORoute(myActiveRouteID, myCurrentCosts, myActiveRouteProbability, myActiveRoute,
378                                  myActiveRouteColor, myActiveRouteStops);
379     myActiveRoute.clear();
380     if (myCurrentAlternatives == nullptr) {
381         if (myNet.getRouteDef(myActiveRouteID) != nullptr) {
382             delete route;
383             if (myVehicleParameter != nullptr) {
384                 myErrorOutput->inform("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
385             } else {
386                 myErrorOutput->inform("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
387             }
388             myActiveRouteID = "";
389             myActiveRouteStops.clear();
390             return;
391         } else {
392             myCurrentAlternatives = new RORouteDef(myActiveRouteID, 0, mayBeDisconnected || myTryRepair, mayBeDisconnected);
393             myCurrentAlternatives->addLoadedAlternative(route);
394             myNet.addRouteDef(myCurrentAlternatives);
395             myCurrentAlternatives = nullptr;
396         }
397     } else {
398         myCurrentAlternatives->addLoadedAlternative(route);
399     }
400     myActiveRouteID = "";
401     myActiveRouteStops.clear();
402 }
403 
404 
405 void
openRouteDistribution(const SUMOSAXAttributes & attrs)406 RORouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
407     // check whether the id is really necessary
408     bool ok = true;
409     std::string id;
410     if (myVehicleParameter != nullptr) {
411         // ok, a vehicle is wrapping the route,
412         //  we may use this vehicle's id as default
413         myVehicleParameter->routeid = id = "!" + myVehicleParameter->id; // !!! document this
414         if (attrs.hasAttribute(SUMO_ATTR_ID)) {
415             WRITE_WARNING("Ids of internal route distributions are ignored (vehicle '" + myVehicleParameter->id + "').");
416         }
417     } else {
418         id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
419         if (!ok) {
420             return;
421         }
422     }
423     // try to get the index of the last element
424     int index = attrs.getOpt<int>(SUMO_ATTR_LAST, id.c_str(), ok, 0);
425     if (ok && index < 0) {
426         myErrorOutput->inform("Negative index of a route alternative (id='" + id + "').");
427         return;
428     }
429     // build the alternative cont
430     myCurrentAlternatives = new RORouteDef(id, index, myTryRepair, false);
431     if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
432         ok = true;
433         StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, id.c_str(), ok));
434         while (st.hasNext()) {
435             const std::string routeID = st.next();
436             const RORouteDef* route = myNet.getRouteDef(routeID);
437             if (route == nullptr) {
438                 myErrorOutput->inform("Unknown route '" + routeID + "' in distribution '" + id + "'.");
439             } else {
440                 myCurrentAlternatives->addAlternativeDef(route);
441             }
442         }
443     }
444 }
445 
446 
447 void
closeRouteDistribution()448 RORouteHandler::closeRouteDistribution() {
449     if (myCurrentAlternatives != nullptr) {
450         if (myCurrentAlternatives->getOverallProb() == 0) {
451             myErrorOutput->inform("Route distribution '" + myCurrentAlternatives->getID() + "' is empty.");
452             delete myCurrentAlternatives;
453         } else if (!myNet.addRouteDef(myCurrentAlternatives)) {
454             myErrorOutput->inform("Another route (or distribution) with the id '" + myCurrentAlternatives->getID() + "' exists.");
455             delete myCurrentAlternatives;
456         }
457         myCurrentAlternatives = nullptr;
458     }
459 }
460 
461 
462 void
closeVehicle()463 RORouteHandler::closeVehicle() {
464     // get the vehicle id
465     if (myVehicleParameter->departProcedure == DEPART_GIVEN && myVehicleParameter->depart < myBegin) {
466         return;
467     }
468     // get vehicle type
469     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
470     if (type == nullptr) {
471         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
472         type = myNet.getVehicleTypeSecure(DEFAULT_VTYPE_ID);
473     } else {
474         if (!myKeepVTypeDist) {
475             // fix the type id in case we used a distribution
476             myVehicleParameter->vtypeid = type->id;
477         }
478     }
479     if (type->vehicleClass == SVC_PEDESTRIAN) {
480         WRITE_WARNING("Vehicle type '" + type->id + "' with vClass=pedestrian should only be used for persons and not for vehicle '" + myVehicleParameter->id + "'.");
481     }
482     // get the route
483     RORouteDef* route = myNet.getRouteDef(myVehicleParameter->routeid);
484     if (route == nullptr) {
485         myErrorOutput->inform("The route of the vehicle '" + myVehicleParameter->id + "' is not known.");
486         return;
487     }
488     if (route->getID()[0] != '!') {
489         route = route->copy("!" + myVehicleParameter->id, myVehicleParameter->depart);
490     }
491     // build the vehicle
492     if (!MsgHandler::getErrorInstance()->wasInformed()) {
493         ROVehicle* veh = new ROVehicle(*myVehicleParameter, route, type, &myNet, myErrorOutput);
494         if (myNet.addVehicle(myVehicleParameter->id, veh)) {
495             registerLastDepart();
496         }
497     }
498 }
499 
500 
501 void
closeVType()502 RORouteHandler::closeVType() {
503     if (myNet.addVehicleType(myCurrentVType)) {
504         if (myCurrentVTypeDistribution != nullptr) {
505             myCurrentVTypeDistribution->add(myCurrentVType, myCurrentVType->defaultProbability);
506         }
507     }
508     myCurrentVType = nullptr;
509 }
510 
511 
512 void
closePerson()513 RORouteHandler::closePerson() {
514     if (myActivePerson->getPlan().empty()) {
515         WRITE_WARNING("Discarding person '" + myVehicleParameter->id + "' because it's plan is empty");
516     } else {
517         if (myNet.addPerson(myActivePerson)) {
518             registerLastDepart();
519         }
520     }
521     delete myVehicleParameter;
522     myVehicleParameter = nullptr;
523     myActivePerson = nullptr;
524 }
525 
526 
527 void
closePersonFlow()528 RORouteHandler::closePersonFlow() {
529     if (myActivePerson->getPlan().empty()) {
530         WRITE_WARNING("Discarding person '" + myVehicleParameter->id + "' because it's plan is empty");
531     } else {
532         // instantiate all persons of this flow
533         int i = 0;
534         std::string baseID = myVehicleParameter->id;
535         if (myVehicleParameter->repetitionProbability > 0) {
536             if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
537                 throw ProcessError("probabilistic personFlow '" + myVehicleParameter->id + "' must specify end time");
538             } else {
539                 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
540                     if (RandHelper::rand() < myVehicleParameter->repetitionProbability) {
541                         addFlowPerson(t, baseID, i++);
542                     }
543                 }
544             }
545         } else {
546             SUMOTime depart = myVehicleParameter->depart;
547             for (; i < myVehicleParameter->repetitionNumber; i++) {
548                 addFlowPerson(depart, baseID, i);
549                 depart += myVehicleParameter->repetitionOffset;
550             }
551         }
552     }
553     delete myVehicleParameter;
554     myVehicleParameter = nullptr;
555     myActivePerson = nullptr;
556 }
557 
558 
559 void
addFlowPerson(SUMOTime depart,const std::string & baseID,int i)560 RORouteHandler::addFlowPerson(SUMOTime depart, const std::string& baseID, int i) {
561     SUMOVehicleParameter pars = myActivePerson->getParameter();
562     pars.id = baseID + "." + toString(i);
563     pars.depart = depart;
564     ROPerson* copyPerson = new ROPerson(pars, myActivePerson->getType());
565     for (ROPerson::PlanItem* item : myActivePerson->getPlan()) {
566         copyPerson->getPlan().push_back(item->clone());
567     }
568     if (i == 0) {
569         delete myActivePerson;
570     }
571     myActivePerson = copyPerson;
572     if (myNet.addPerson(myActivePerson)) {
573         if (i == 0) {
574             registerLastDepart();
575         }
576     }
577 }
578 
579 void
closeContainer()580 RORouteHandler::closeContainer() {
581     myActiveContainerPlan->closeTag();
582     if (myActiveContainerPlanSize > 0) {
583         myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
584         registerLastDepart();
585     } else {
586         WRITE_WARNING("Discarding container '" + myVehicleParameter->id + "' because it's plan is empty");
587     }
588     delete myVehicleParameter;
589     myVehicleParameter = nullptr;
590     delete myActiveContainerPlan;
591     myActiveContainerPlan = nullptr;
592     myActiveContainerPlanSize = 0;
593 }
594 
595 
596 void
closeFlow()597 RORouteHandler::closeFlow() {
598     // @todo: consider myScale?
599     if (myVehicleParameter->repetitionNumber == 0) {
600         delete myVehicleParameter;
601         myVehicleParameter = nullptr;
602         return;
603     }
604     // let's check whether vehicles had to depart before the simulation starts
605     myVehicleParameter->repetitionsDone = 0;
606     const SUMOTime offsetToBegin = myBegin - myVehicleParameter->depart;
607     while (myVehicleParameter->repetitionsDone * myVehicleParameter->repetitionOffset < offsetToBegin) {
608         myVehicleParameter->repetitionsDone++;
609         if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
610             delete myVehicleParameter;
611             myVehicleParameter = nullptr;
612             return;
613         }
614     }
615     if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
616         myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
617     }
618     if (myVehicleParameter->routeid[0] == '!' && myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
619         closeRoute(true);
620     }
621     if (myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
622         myErrorOutput->inform("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
623         delete myVehicleParameter;
624         myVehicleParameter = nullptr;
625         return;
626     }
627     myActiveRouteID = "";
628     if (!MsgHandler::getErrorInstance()->wasInformed()) {
629         if (myNet.addFlow(myVehicleParameter, OptionsCont::getOptions().getBool("randomize-flows"))) {
630             registerLastDepart();
631         } else {
632             myErrorOutput->inform("Another flow with the id '" + myVehicleParameter->id + "' exists.");
633         }
634     } else {
635         delete myVehicleParameter;
636     }
637     myVehicleParameter = nullptr;
638     myInsertStopEdgesAt = -1;
639 }
640 
641 
642 void
closeTrip()643 RORouteHandler::closeTrip() {
644     closeRoute(true);
645     closeVehicle();
646 }
647 
648 
649 void
addStop(const SUMOSAXAttributes & attrs)650 RORouteHandler::addStop(const SUMOSAXAttributes& attrs) {
651     if (myActiveContainerPlan != nullptr) {
652         myActiveContainerPlan->openTag(SUMO_TAG_STOP);
653         (*myActiveContainerPlan) << attrs;
654         myActiveContainerPlan->closeTag();
655         myActiveContainerPlanSize++;
656         return;
657     }
658     std::string errorSuffix;
659     if (myVehicleParameter != nullptr) {
660         errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
661     } else {
662         errorSuffix = " in route '" + myActiveRouteID + "'.";
663     }
664     SUMOVehicleParameter::Stop stop;
665     bool ok = parseStop(stop, attrs, errorSuffix, myErrorOutput);
666     if (!ok) {
667         return;
668     }
669     // try to parse the assigned bus stop
670     ROEdge* edge = nullptr;
671     if (stop.busstop != "") {
672         const SUMOVehicleParameter::Stop* busstop = myNet.getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
673         if (busstop == nullptr) {
674             myErrorOutput->inform("Unknown bus stop '" + stop.busstop + "'" + errorSuffix);
675             return;
676         }
677         stop.lane = busstop->lane;
678         stop.endPos = busstop->endPos;
679         stop.startPos = busstop->startPos;
680         edge = myNet.getEdge(stop.lane.substr(0, stop.lane.rfind('_')));
681     } // try to parse the assigned container stop
682     else if (stop.containerstop != "") {
683         const SUMOVehicleParameter::Stop* containerstop = myNet.getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
684         if (containerstop == nullptr) {
685             myErrorOutput->inform("Unknown container stop '" + stop.containerstop + "'" + errorSuffix);
686             return;
687         }
688         stop.lane = containerstop->lane;
689         stop.endPos = containerstop->endPos;
690         stop.startPos = containerstop->startPos;
691         edge = myNet.getEdge(stop.lane.substr(0, stop.lane.rfind('_')));
692     } // try to parse the assigned parking area
693     else if (stop.parkingarea != "") {
694         const SUMOVehicleParameter::Stop* parkingarea = myNet.getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
695         if (parkingarea == nullptr) {
696             myErrorOutput->inform("Unknown parking area '" + stop.parkingarea + "'" + errorSuffix);
697             return;
698         }
699         stop.lane = parkingarea->lane;
700         stop.endPos = parkingarea->endPos;
701         stop.startPos = parkingarea->startPos;
702         edge = myNet.getEdge(stop.lane.substr(0, stop.lane.rfind('_')));
703     } else {
704         // no, the lane and the position should be given
705         stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
706         if (!ok || stop.lane == "") {
707             myErrorOutput->inform("A stop must be placed on a bus stop, a container stop, a parking area or a lane" + errorSuffix);
708             return;
709         }
710         edge = myNet.getEdge(stop.lane.substr(0, stop.lane.rfind('_')));
711         if (edge == nullptr) {
712             myErrorOutput->inform("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
713             return;
714         }
715         stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
716         stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, stop.endPos - 2 * POSITION_EPS);
717         const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
718         const double endPosOffset = edge->isInternal() ? edge->getNormalBefore()->getLength() : 0;
719         if (!ok || !checkStopPos(stop.startPos, stop.endPos, edge->getLength() + endPosOffset, POSITION_EPS, friendlyPos)) {
720             myErrorOutput->inform("Invalid start or end position for stop" + errorSuffix);
721             return;
722         }
723     }
724     if (myActivePerson != nullptr) {
725         myActivePerson->addStop(stop, edge);
726     } else if (myVehicleParameter != nullptr) {
727         myVehicleParameter->stops.push_back(stop);
728     } else {
729         myActiveRouteStops.push_back(stop);
730     }
731     if (myInsertStopEdgesAt >= 0) {
732         myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
733         myInsertStopEdgesAt++;
734     }
735 }
736 
737 
738 void
addPerson(const SUMOSAXAttributes &)739 RORouteHandler::addPerson(const SUMOSAXAttributes& /*attrs*/) {
740 }
741 
742 
743 void
addContainer(const SUMOSAXAttributes &)744 RORouteHandler::addContainer(const SUMOSAXAttributes& /*attrs*/) {
745 }
746 
747 
748 void
addRide(const SUMOSAXAttributes &)749 RORouteHandler::addRide(const SUMOSAXAttributes& /*attrs*/) {
750 }
751 
752 
753 void
addTransport(const SUMOSAXAttributes &)754 RORouteHandler::addTransport(const SUMOSAXAttributes& /*attrs*/) {
755 }
756 
757 
758 void
addTranship(const SUMOSAXAttributes &)759 RORouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
760 }
761 
762 
763 void
parseEdges(const std::string & desc,ConstROEdgeVector & into,const std::string & rid)764 RORouteHandler::parseEdges(const std::string& desc, ConstROEdgeVector& into,
765                            const std::string& rid) {
766     if (desc[0] == BinaryFormatter::BF_ROUTE) {
767         std::istringstream in(desc, std::ios::binary);
768         char c;
769         in >> c;
770         FileHelpers::readEdgeVector(in, into, rid);
771     } else {
772         for (StringTokenizer st(desc); st.hasNext();) {
773             const std::string id = st.next();
774             const ROEdge* edge = myNet.getEdge(id);
775             if (edge == nullptr) {
776                 myErrorOutput->inform("The edge '" + id + "' within the route " + rid + " is not known.");
777             } else {
778                 into.push_back(edge);
779             }
780         }
781     }
782 }
783 
784 void
parseGeoEdges(const PositionVector & positions,bool geo,ConstROEdgeVector & into,const std::string & rid)785 RORouteHandler::parseGeoEdges(const PositionVector& positions, bool geo,
786                               ConstROEdgeVector& into, const std::string& rid) {
787     const double range = 100;
788     NamedRTree* t = getLaneTree();
789     if (geo && !GeoConvHelper::getFinal().usingGeoProjection()) {
790         WRITE_ERROR("Cannot convert geo-positions because the network has no geo-reference");
791         return;
792     }
793     SUMOVehicleClass vClass = SVC_PASSENGER;
794     SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
795     if (type != nullptr) {
796         vClass = type->vehicleClass;
797     }
798     for (Position pos : positions) {
799         Position orig = pos;
800         if (geo) {
801             GeoConvHelper::getFinal().x2cartesian_const(pos);
802         }
803         Boundary b;
804         b.add(pos);
805         b.grow(range);
806         const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
807         const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
808         std::set<const Named*> lanes;
809         Named::StoringVisitor sv(lanes);
810         t->Search(cmin, cmax, sv);
811         // use closest
812         double minDist = std::numeric_limits<double>::max();
813         const ROLane* best = nullptr;
814         for (const Named* o : lanes) {
815             const ROLane* cand = static_cast<const ROLane*>(o);
816             if (!cand->allowsVehicleClass(vClass)) {
817                 continue;
818             }
819             double dist = cand->getShape().distance2D(pos);
820             if (dist < minDist) {
821                 minDist = dist;
822                 best = cand;
823             }
824         }
825         if (best == nullptr) {
826             myErrorOutput->inform("No edge found near position " + toString(orig, geo ? gPrecisionGeo : gPrecision) + " within the route " + rid + ".");
827         } else {
828             const ROEdge* bestEdge = &best->getEdge();
829             while (bestEdge->isInternal()) {
830                 bestEdge = bestEdge->getSuccessors().front();
831             }
832             into.push_back(bestEdge);
833         }
834     }
835 }
836 
837 
838 void
addPersonTrip(const SUMOSAXAttributes & attrs)839 RORouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
840     bool ok = true;
841     const char* const id = myVehicleParameter->id.c_str();
842     assert(!attrs.hasAttribute(SUMO_ATTR_EDGES));
843     const std::string fromID = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id, ok, "");
844     std::string toID = attrs.getOpt<std::string>(SUMO_ATTR_TO, id, ok, "");
845     const std::string busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, id, ok, "");
846 
847     const ROEdge* from = fromID != "" || myActivePerson->getPlan().empty() ? myNet.getEdge(fromID) : myActivePerson->getPlan().back()->getDestination();
848     if (from == nullptr) {
849         myErrorOutput->inform("The edge '" + fromID + "' within a walk or personTrip of '" + myVehicleParameter->id + "' is not known."
850                               + "\n The route can not be build.");
851         ok = false;
852     }
853     double arrivalPos = 0;
854     const ROEdge* to = myNet.getEdge(toID);
855     if (toID != "" && to == nullptr) {
856         myErrorOutput->inform("The edge '" + toID + "' within a walk or personTrip of '" + myVehicleParameter->id + "' is not known."
857                               + "\n The route can not be build.");
858         ok = false;
859     }
860     if (toID == "" && busStopID == "") {
861         myErrorOutput->inform("Either a destination edge or busStop must be define within a walk or personTrip of '" + myVehicleParameter->id + "'."
862                               + "\n The route can not be build.");
863         ok = false;
864     }
865 
866     if (toID == "") {
867         const SUMOVehicleParameter::Stop* stop = myNet.getStoppingPlace(busStopID, SUMO_TAG_BUS_STOP);
868         if (stop == nullptr) {
869             myErrorOutput->inform("Unknown bus stop '" + busStopID + "' within a walk or personTrip of '" + myVehicleParameter->id + "'.");
870             ok = false;
871         } else {
872             to = myNet.getEdge(stop->lane.substr(0, stop->lane.rfind('_')));
873             arrivalPos = (stop->startPos + stop->endPos) / 2;
874         }
875     }
876 
877     double departPos = myActivePerson->getParameter().departPos;
878     if (!myActivePerson->getPlan().empty()) {
879         departPos = myActivePerson->getPlan().back()->getDestinationPos();
880     }
881 
882     if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
883         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.");
884     }
885     if (to != nullptr && attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
886         arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, id, to->getLength(),
887                      attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, id, ok));
888     }
889 
890     const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
891     SVCPermissions modeSet = 0;
892     for (StringTokenizer st(modes); st.hasNext();) {
893         const std::string mode = st.next();
894         if (mode == "car") {
895             modeSet |= SVC_PASSENGER;
896         } else if (mode == "bicycle") {
897             modeSet |= SVC_BICYCLE;
898         } else if (mode == "public") {
899             modeSet |= SVC_BUS;
900         } else {
901             throw InvalidArgument("Unknown person mode '" + mode + "'.");
902         }
903     }
904     const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
905     double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
906     if (ok) {
907         myActivePerson->addTrip(from, to, modeSet, types, departPos, arrivalPos, busStopID, walkFactor);
908     }
909 }
910 
911 
912 void
addWalk(const SUMOSAXAttributes & attrs)913 RORouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
914     // XXX allow --repair?
915     bool ok = true;
916     if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
917         const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
918         RORouteDef* routeDef = myNet.getRouteDef(routeID);
919         const RORoute* route = routeDef != nullptr ? routeDef->getFirstRoute() : nullptr;
920         if (route == nullptr) {
921             throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
922         }
923         myActiveRoute = route->getEdgeVector();
924     } else {
925         myActiveRoute.clear();
926         parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, " walk for person '" + myVehicleParameter->id + "'");
927     }
928     const char* const objId = myVehicleParameter->id.c_str();
929     const double duration = attrs.getOpt<double>(SUMO_ATTR_DURATION, objId, ok, -1);
930     if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
931         throw ProcessError("Non-positive walking duration for  '" + myVehicleParameter->id + "'.");
932     }
933     const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, objId, ok, -1.);
934     if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
935         throw ProcessError("Non-positive walking speed for  '" + myVehicleParameter->id + "'.");
936     }
937     double departPos = 0.;
938     double arrivalPos = 0.;
939     if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
940         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.");
941     }
942     if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
943         arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, objId, myActiveRoute.back()->getLength(),
944                      attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, objId, ok));
945     }
946     const std::string busStop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, objId, ok, "");
947     if (ok) {
948         myActivePerson->addWalk(myActiveRoute, duration, speed, departPos, arrivalPos, busStop);
949     }
950 }
951 
952 
953 NamedRTree*
getLaneTree()954 RORouteHandler::getLaneTree() {
955     if (myLaneTree == nullptr) {
956         myLaneTree = new NamedRTree();
957         for (const auto& edgeItem : myNet.getEdgeMap()) {
958             for (ROLane* lane : edgeItem.second->getLanes()) {
959                 Boundary b = lane->getShape().getBoxBoundary();
960                 const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
961                 const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
962                 myLaneTree->Insert(cmin, cmax, lane);
963             }
964         }
965     }
966     return myLaneTree;
967 }
968 
969 
970 /****************************************************************************/
971