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