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    SUMORouteHandler.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 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 <utils/common/MsgHandler.h>
31 #include <utils/common/ToString.h>
32 #include <utils/common/UtilExceptions.h>
33 #include <utils/options/OptionsCont.h>
34 #include <utils/vehicle/SUMOVehicleParameter.h>
35 #include <utils/vehicle/SUMOVTypeParameter.h>
36 #include <utils/xml/SUMOSAXHandler.h>
37 #include <utils/vehicle/SUMOVehicleParserHelper.h>
38 #include <utils/xml/SUMOXMLDefinitions.h>
39 #include <utils/xml/XMLSubSys.h>
40 #include "SUMORouteHandler.h"
41 
42 
43 // ===========================================================================
44 // method definitions
45 // ===========================================================================
SUMORouteHandler(const std::string & file,const std::string & expectedRoot)46 SUMORouteHandler::SUMORouteHandler(const std::string& file, const std::string& expectedRoot) :
47     SUMOSAXHandler(file, XMLSubSys::isValidating() ? expectedRoot : ""),
48     myVehicleParameter(nullptr),
49     myLastDepart(-1),
50     myActiveRouteColor(nullptr),
51     myCurrentCosts(0.),
52     myCurrentVType(nullptr),
53     myBeginDefault(string2time(OptionsCont::getOptions().getString("begin"))),
54     myEndDefault(string2time(OptionsCont::getOptions().getString("end"))),
55     myFirstDepart(-1), myInsertStopEdgesAt(-1) {
56 }
57 
58 
~SUMORouteHandler()59 SUMORouteHandler::~SUMORouteHandler() {
60     delete myCurrentVType;
61 }
62 
63 
64 SUMOTime
getLastDepart() const65 SUMORouteHandler::getLastDepart() const {
66     return myLastDepart;
67 }
68 
69 
70 bool
checkLastDepart()71 SUMORouteHandler::checkLastDepart() {
72     if (myVehicleParameter->departProcedure == DEPART_GIVEN) {
73         if (myVehicleParameter->depart < myLastDepart) {
74             WRITE_WARNING("Route file should be sorted by departure time, ignoring '" + myVehicleParameter->id + "'!");
75             return false;
76         }
77     }
78     return true;
79 }
80 
81 
82 void
registerLastDepart()83 SUMORouteHandler::registerLastDepart() {
84     // register only non public transport to parse all public transport lines in advance
85     if (myVehicleParameter->line == "" && myVehicleParameter->departProcedure == DEPART_GIVEN) {
86         myLastDepart = myVehicleParameter->depart;
87         if (myFirstDepart == -1) {
88             myFirstDepart = myLastDepart;
89         }
90     }
91     // else: we don't know when this vehicle will depart. keep the previous known depart time
92 }
93 
94 
95 void
myStartElement(int element,const SUMOSAXAttributes & attrs)96 SUMORouteHandler::myStartElement(int element,
97                                  const SUMOSAXAttributes& attrs) {
98     switch (element) {
99         case SUMO_TAG_VEHICLE:
100             delete myVehicleParameter;
101             myVehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(attrs);
102             break;
103         case SUMO_TAG_PERSON:
104             delete myVehicleParameter;
105             myVehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(attrs, false, false, true);
106             addPerson(attrs);
107             break;
108         case SUMO_TAG_CONTAINER:
109             delete myVehicleParameter;
110             myVehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(attrs);
111             addContainer(attrs);
112             break;
113         case SUMO_TAG_FLOW:
114             delete myVehicleParameter;
115             myVehicleParameter = SUMOVehicleParserHelper::parseFlowAttributes(attrs, myBeginDefault, myEndDefault);
116             break;
117         case SUMO_TAG_PERSONFLOW:
118             delete myVehicleParameter;
119             myVehicleParameter = SUMOVehicleParserHelper::parseFlowAttributes(attrs, myBeginDefault, myEndDefault, true);
120             break;
121         case SUMO_TAG_VTYPE:
122             // XXX: Where is this deleted? Delegated to subclasses?! MSRouteHandler takes care of this, in case of RORouteHandler this is not obvious. Consider introduction of a shared_ptr
123             myCurrentVType = SUMOVehicleParserHelper::beginVTypeParsing(attrs, getFileName());
124             break;
125         case SUMO_TAG_VTYPE_DISTRIBUTION:
126             openVehicleTypeDistribution(attrs);
127             break;
128         case SUMO_TAG_ROUTE:
129             openRoute(attrs);
130             break;
131         case SUMO_TAG_ROUTE_DISTRIBUTION:
132             openRouteDistribution(attrs);
133             break;
134         case SUMO_TAG_STOP:
135             addStop(attrs);
136             break;
137         case SUMO_TAG_TRIP: {
138             myVehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(attrs, true);
139             if (myVehicleParameter->id == "") {
140                 WRITE_WARNING("Omitting trip ids is deprecated!");
141                 myVehicleParameter->id = myIdSupplier.getNext();
142             }
143             myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
144             myActiveRouteID = "!" + myVehicleParameter->id;
145             // open trip
146             openTrip(attrs);
147             break;
148         }
149         case SUMO_TAG_PERSONTRIP:
150         case SUMO_TAG_WALK:
151             if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
152                 addWalk(attrs);
153             } else {
154                 addPersonTrip(attrs);
155             }
156             break;
157         case SUMO_TAG_INTERVAL: {
158             bool ok;
159             myBeginDefault = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok);
160             myEndDefault = attrs.getSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok);
161             break;
162         }
163         case SUMO_TAG_RIDE:
164             addRide(attrs);
165             break;
166         case SUMO_TAG_TRANSPORT:
167             addTransport(attrs);
168             break;
169         case SUMO_TAG_TRANSHIP:
170             addTranship(attrs);
171             break;
172         case SUMO_TAG_PARAM:
173             addParam(attrs);
174             break;
175         default:
176             // parse embedded car following model information
177             if (myCurrentVType != nullptr) {
178                 WRITE_WARNING("Defining car following parameters in a nested element is deprecated in vType '" + myCurrentVType->id + "', use attributes instead!");
179                 SUMOVehicleParserHelper::parseVTypeEmbedded(*myCurrentVType, (SumoXMLTag)element, attrs);
180             }
181             break;
182     }
183 }
184 
185 
186 void
myEndElement(int element)187 SUMORouteHandler::myEndElement(int element) {
188     switch (element) {
189         case SUMO_TAG_ROUTE:
190             closeRoute();
191             break;
192         case SUMO_TAG_VTYPE:
193             closeVType();
194             break;
195         case SUMO_TAG_PERSON:
196             closePerson();
197             delete myVehicleParameter;
198             myVehicleParameter = nullptr;
199             break;
200         case SUMO_TAG_PERSONFLOW:
201             closePersonFlow();
202             delete myVehicleParameter;
203             myVehicleParameter = nullptr;
204             break;
205         case SUMO_TAG_CONTAINER:
206             closeContainer();
207             delete myVehicleParameter;
208             myVehicleParameter = nullptr;
209             break;
210         case SUMO_TAG_VEHICLE:
211             if (myVehicleParameter->repetitionNumber > 0) {
212                 myVehicleParameter->repetitionNumber++; // for backwards compatibility
213                 // it is a flow, thus no break here
214                 FALLTHROUGH;
215             } else {
216                 closeVehicle();
217                 delete myVehicleParameter;
218                 myVehicleParameter = nullptr;
219                 break;
220             }
221         case SUMO_TAG_FLOW:
222             closeFlow();
223             break;
224         case SUMO_TAG_TRIP:
225             closeTrip();
226             delete myVehicleParameter;
227             myVehicleParameter = nullptr;
228             myInsertStopEdgesAt = -1;
229             break;
230         case SUMO_TAG_VTYPE_DISTRIBUTION:
231             closeVehicleTypeDistribution();
232             break;
233         case SUMO_TAG_ROUTE_DISTRIBUTION:
234             closeRouteDistribution();
235             break;
236         case SUMO_TAG_INTERVAL:
237             myBeginDefault = string2time(OptionsCont::getOptions().getString("begin"));
238             myEndDefault = string2time(OptionsCont::getOptions().getString("end"));
239             break;
240         default:
241             break;
242     }
243 }
244 
245 
246 bool
checkStopPos(double & startPos,double & endPos,const double laneLength,const double minLength,const bool friendlyPos)247 SUMORouteHandler::checkStopPos(double& startPos, double& endPos, const double laneLength,
248                                const double minLength, const bool friendlyPos) {
249     if (minLength > laneLength) {
250         return false;
251     }
252     if (startPos < 0) {
253         startPos += laneLength;
254     }
255     if (endPos < 0) {
256         endPos += laneLength;
257     }
258     if (endPos < minLength || endPos > laneLength) {
259         if (!friendlyPos) {
260             return false;
261         }
262         if (endPos < minLength) {
263             endPos = minLength;
264         }
265         if (endPos > laneLength) {
266             endPos = laneLength;
267         }
268     }
269     if (startPos < 0 || startPos > endPos - minLength) {
270         if (!friendlyPos) {
271             return false;
272         }
273         if (startPos < 0) {
274             startPos = 0;
275         }
276         if (startPos > endPos - minLength) {
277             startPos = endPos - minLength;
278         }
279     }
280     return true;
281 }
282 
283 
284 void
addParam(const SUMOSAXAttributes & attrs)285 SUMORouteHandler::addParam(const SUMOSAXAttributes& attrs) {
286     bool ok = true;
287     const std::string key = attrs.get<std::string>(SUMO_ATTR_KEY, nullptr, ok);
288     // circumventing empty string test
289     const std::string val = attrs.hasAttribute(SUMO_ATTR_VALUE) ? attrs.getString(SUMO_ATTR_VALUE) : "";
290     if (myVehicleParameter != nullptr) {
291         myVehicleParameter->setParameter(key, val);
292     } else if (myCurrentVType != nullptr) {
293         myCurrentVType->setParameter(key, val);
294     }
295 }
296 
297 
298 bool
parseStop(SUMOVehicleParameter::Stop & stop,const SUMOSAXAttributes & attrs,std::string errorSuffix,MsgHandler * const errorOutput)299 SUMORouteHandler::parseStop(SUMOVehicleParameter::Stop& stop, const SUMOSAXAttributes& attrs, std::string errorSuffix, MsgHandler* const errorOutput) {
300     stop.parametersSet = 0;
301     if (attrs.hasAttribute(SUMO_ATTR_ENDPOS)) {
302         stop.parametersSet |= STOP_END_SET;
303     }
304     if (attrs.hasAttribute(SUMO_ATTR_STARTPOS)) {
305         stop.parametersSet |= STOP_START_SET;
306     }
307     if (attrs.hasAttribute(SUMO_ATTR_TRIGGERED)) {
308         stop.parametersSet |= STOP_TRIGGER_SET;
309     }
310     if (attrs.hasAttribute(SUMO_ATTR_CONTAINER_TRIGGERED)) {
311         stop.parametersSet |= STOP_CONTAINER_TRIGGER_SET;
312     }
313     if (attrs.hasAttribute(SUMO_ATTR_PARKING)) {
314         stop.parametersSet |= STOP_PARKING_SET;
315     }
316     if (attrs.hasAttribute(SUMO_ATTR_EXPECTED)) {
317         stop.parametersSet |= STOP_EXPECTED_SET;
318     }
319     if (attrs.hasAttribute(SUMO_ATTR_EXPECTED_CONTAINERS)) {
320         stop.parametersSet |= STOP_EXPECTED_CONTAINERS_SET;
321     }
322     if (attrs.hasAttribute(SUMO_ATTR_TRIP_ID)) {
323         stop.parametersSet |= STOP_TRIP_ID_SET;
324     }
325     bool ok = true;
326     stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
327     stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
328     stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
329     stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
330     if (stop.busstop != "") {
331         errorSuffix = " at '" + stop.busstop + "'" + errorSuffix;
332     } else if (stop.chargingStation != "") {
333         errorSuffix = " at '" + stop.chargingStation + "'" + errorSuffix;
334     } else if (stop.containerstop != "") {
335         errorSuffix = " at '" + stop.containerstop + "'" + errorSuffix;
336     } else if (stop.parkingarea != "") {
337         errorSuffix = " at '" + stop.parkingarea + "'" + errorSuffix;
338     } else {
339         errorSuffix = " on lane '" + stop.lane + "'" + errorSuffix;
340     }
341     // get the standing duration
342     if (!attrs.hasAttribute(SUMO_ATTR_DURATION) && !attrs.hasAttribute(SUMO_ATTR_UNTIL)) {
343         if (attrs.hasAttribute(SUMO_ATTR_CONTAINER_TRIGGERED)) {
344             stop.containerTriggered = attrs.getOpt<bool>(SUMO_ATTR_CONTAINER_TRIGGERED, nullptr, ok, true);
345             stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, nullptr, ok, false);
346         } else {
347             stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, nullptr, ok, true);
348             stop.containerTriggered = attrs.getOpt<bool>(SUMO_ATTR_CONTAINER_TRIGGERED, nullptr, ok, false);
349         }
350         stop.duration = -1;
351         stop.until = -1;
352     } else {
353         stop.duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
354         stop.until = attrs.getOptSUMOTimeReporting(SUMO_ATTR_UNTIL, nullptr, ok, -1);
355         if (!ok || (stop.duration < 0 && stop.until < 0)) {
356             errorOutput->inform("Invalid duration or end time is given for a stop" + errorSuffix);
357             return false;
358         }
359         stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, nullptr, ok, false);
360         stop.containerTriggered = attrs.getOpt<bool>(SUMO_ATTR_CONTAINER_TRIGGERED, nullptr, ok, false);
361     }
362     stop.parking = attrs.getOpt<bool>(SUMO_ATTR_PARKING, nullptr, ok, stop.triggered || stop.containerTriggered || stop.parkingarea != "");
363     if (stop.parkingarea != "" && !stop.parking) {
364         ok = false;
365     }
366     if (!ok) {
367         errorOutput->inform("Invalid bool for 'triggered', 'containerTriggered' or 'parking' for stop" + errorSuffix);
368         return false;
369     }
370 
371     // expected persons
372     const std::vector<std::string>& expected = attrs.getOptStringVector(SUMO_ATTR_EXPECTED, nullptr, ok);
373     stop.awaitedPersons.insert(expected.begin(), expected.end());
374     if (stop.awaitedPersons.size() > 0 && (stop.parametersSet & STOP_TRIGGER_SET) == 0) {
375         stop.triggered = true;
376         if ((stop.parametersSet & STOP_PARKING_SET) == 0) {
377             stop.parking = true;
378         }
379     }
380 
381     // expected containers
382     const std::vector<std::string>& expectedContainers = attrs.getOptStringVector(SUMO_ATTR_EXPECTED_CONTAINERS, nullptr, ok);
383     stop.awaitedContainers.insert(expectedContainers.begin(), expectedContainers.end());
384     if (stop.awaitedContainers.size() > 0 && (stop.parametersSet & STOP_CONTAINER_TRIGGER_SET) == 0) {
385         stop.containerTriggered = true;
386         if ((stop.parametersSet & STOP_PARKING_SET) == 0) {
387             stop.parking = true;
388         }
389     }
390     // public transport trip id
391     stop.tripId = attrs.getOpt<std::string>(SUMO_ATTR_TRIP_ID, nullptr, ok, "");
392 
393     const std::string idx = attrs.getOpt<std::string>(SUMO_ATTR_INDEX, nullptr, ok, "end");
394     if (idx == "end") {
395         stop.index = STOP_INDEX_END;
396     } else if (idx == "fit") {
397         stop.index = STOP_INDEX_FIT;
398     } else {
399         stop.index = attrs.get<int>(SUMO_ATTR_INDEX, nullptr, ok);
400         if (!ok || stop.index < 0) {
401             errorOutput->inform("Invalid 'index' for stop" + errorSuffix);
402             return false;
403         }
404     }
405     return true;
406 }
407 
408 /****************************************************************************/
409