Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/router/RORouteHandler.cpp
193833 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file RORouteHandler.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @date Mon, 9 Jul 2001
20
///
21
// Parser and container for routes during their loading
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <string>
26
#include <map>
27
#include <vector>
28
#include <iostream>
29
#include <utils/iodevices/OutputDevice.h>
30
#include <utils/xml/SUMOSAXHandler.h>
31
#include <utils/xml/SUMOXMLDefinitions.h>
32
#include <utils/geom/GeoConvHelper.h>
33
#include <utils/common/FileHelpers.h>
34
#include <utils/common/MsgHandler.h>
35
#include <utils/common/StringTokenizer.h>
36
#include <utils/common/UtilExceptions.h>
37
#include <utils/options/OptionsCont.h>
38
#include <utils/vehicle/SUMOVehicleParserHelper.h>
39
#include <utils/xml/SUMOSAXReader.h>
40
#include <utils/xml/XMLSubSys.h>
41
#include <utils/iodevices/OutputDevice_String.h>
42
#include "RONet.h"
43
#include "ROEdge.h"
44
#include "ROLane.h"
45
#include "RORouteDef.h"
46
#include "RORouteHandler.h"
47
48
// ===========================================================================
49
// method definitions
50
// ===========================================================================
51
RORouteHandler::RORouteHandler(RONet& net, const std::string& file,
52
const bool tryRepair,
53
const bool emptyDestinationsAllowed,
54
const bool ignoreErrors,
55
const bool checkSchema) :
56
SUMORouteHandler(file, checkSchema ? "routes" : "", true),
57
MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
58
OptionsCont::getOptions().getBool("mapmatch.taz"),
59
OptionsCont::getOptions().getFloat("mapmatch.distance"),
60
ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
61
myNet(net),
62
myActiveRouteRepeat(0),
63
myActiveRoutePeriod(0),
64
myActivePlan(nullptr),
65
myActiveContainerPlan(nullptr),
66
myActiveContainerPlanSize(0),
67
myTryRepair(tryRepair),
68
myEmptyDestinationsAllowed(emptyDestinationsAllowed),
69
myErrorOutput(ignoreErrors ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
70
myBegin(string2time(OptionsCont::getOptions().getString("begin"))),
71
myKeepVTypeDist(OptionsCont::getOptions().getBool("keep-vtype-distributions")),
72
myUnsortedInput(OptionsCont::getOptions().exists("unsorted-input") && OptionsCont::getOptions().getBool("unsorted-input")),
73
myWriteFlows(OptionsCont::getOptions().exists("keep-flows") && OptionsCont::getOptions().getBool("keep-flows")),
74
myCurrentVTypeDistribution(nullptr),
75
myCurrentAlternatives(nullptr),
76
myUseTaz(OptionsCont::getOptions().getBool("with-taz")),
77
myWriteJunctions(OptionsCont::getOptions().exists("write-trips")
78
&& OptionsCont::getOptions().getBool("write-trips")
79
&& OptionsCont::getOptions().getBool("write-trips.junctions")) {
80
myActiveRoute.reserve(100);
81
}
82
83
84
RORouteHandler::~RORouteHandler() {
85
delete myCurrentAlternatives;
86
}
87
88
89
void
90
RORouteHandler::deleteActivePlanAndVehicleParameter() {
91
if (myActivePlan != nullptr) {
92
for (ROPerson::PlanItem* const it : *myActivePlan) {
93
delete it;
94
}
95
delete myActivePlan;
96
myActivePlan = nullptr;
97
}
98
delete myActiveContainerPlan;
99
myActiveContainerPlan = nullptr;
100
delete myVehicleParameter;
101
myVehicleParameter = nullptr;
102
}
103
104
105
void
106
RORouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs, bool& ok) {
107
const std::string element = toString(tag);
108
myActiveRoute.clear();
109
bool useTaz = myUseTaz;
110
if (myUseTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
111
WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
112
useTaz = false;
113
}
114
SUMOVehicleClass vClass = SVC_PASSENGER;
115
if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
116
SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
117
if (type != nullptr) {
118
vClass = type->vehicleClass;
119
}
120
}
121
// from-attributes
122
const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
123
if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
124
(attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
125
const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
126
const std::string tazType = useJunction ? "junction" : "taz";
127
const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok, true);
128
const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
129
if (fromTaz == nullptr) {
130
myErrorOutput->inform("Source " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
131
+ (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
132
ok = false;
133
} else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
134
myErrorOutput->inform("Source " + tazType + " '" + tazID + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
135
ok = false;
136
} else {
137
myActiveRoute.push_back(fromTaz);
138
if (useJunction && tag != SUMO_TAG_PERSON && !myWriteJunctions) {
139
myVehicleParameter->fromTaz = tazID;
140
myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
141
}
142
}
143
} else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
144
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok), false, vClass, myActiveRoute, rid, true, ok);
145
if (myMapMatchTAZ && ok) {
146
myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
147
myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
148
}
149
} else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
150
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, myActiveRoute, rid, true, ok);
151
if (myMapMatchTAZ && ok) {
152
myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
153
myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
154
}
155
} else {
156
parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok), myActiveRoute, rid, ok);
157
}
158
if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_VIALONLAT) && !attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
159
myInsertStopEdgesAt = (int)myActiveRoute.size();
160
}
161
162
// via-attributes
163
ConstROEdgeVector viaEdges;
164
if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
165
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok), false, vClass, viaEdges, rid, false, ok);
166
} else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
167
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, viaEdges, rid, false, ok);
168
} else if (attrs.hasAttribute(SUMO_ATTR_VIAJUNCTIONS)) {
169
for (std::string junctionID : attrs.get<std::vector<std::string> >(SUMO_ATTR_VIAJUNCTIONS, myVehicleParameter->id.c_str(), ok)) {
170
const ROEdge* viaSink = myNet.getEdge(junctionID + "-sink");
171
if (viaSink == nullptr) {
172
myErrorOutput->inform("Junction-taz '" + junctionID + "' not found." + JUNCTION_TAZ_MISSING_HELP);
173
ok = false;
174
} else {
175
viaEdges.push_back(viaSink);
176
}
177
}
178
} else {
179
parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true), viaEdges, rid, ok);
180
}
181
for (const ROEdge* e : viaEdges) {
182
myActiveRoute.push_back(e);
183
myVehicleParameter->via.push_back(e->getID());
184
}
185
186
// to-attributes
187
if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
188
(attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
189
const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
190
const std::string tazType = useJunction ? "junction" : "taz";
191
const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
192
const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
193
if (toTaz == nullptr) {
194
myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' not known for " + element + " '" + myVehicleParameter->id + "'!"
195
+ (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
196
ok = false;
197
} else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
198
myErrorOutput->inform("Sink " + tazType + " '" + tazID + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
199
ok = false;
200
} else {
201
myActiveRoute.push_back(toTaz);
202
if (useJunction && tag != SUMO_TAG_PERSON && !myWriteJunctions) {
203
myVehicleParameter->toTaz = tazID;
204
myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
205
}
206
}
207
} else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
208
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, vClass, myActiveRoute, rid, false, ok);
209
if (myMapMatchTAZ && ok) {
210
myVehicleParameter->toTaz = myActiveRoute.back()->getID();
211
myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
212
}
213
} else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
214
parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, vClass, myActiveRoute, rid, false, ok);
215
if (myMapMatchTAZ && ok) {
216
myVehicleParameter->toTaz = myActiveRoute.back()->getID();
217
myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
218
}
219
} else {
220
parseEdges(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true), myActiveRoute, rid, ok);
221
}
222
myActiveRouteID = "!" + myVehicleParameter->id;
223
if (myVehicleParameter->routeid == "") {
224
myVehicleParameter->routeid = myActiveRouteID;
225
}
226
}
227
228
229
void
230
RORouteHandler::myStartElement(int element,
231
const SUMOSAXAttributes& attrs) {
232
try {
233
if (myActivePlan != nullptr && myActivePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_RIDE && element != SUMO_TAG_PARAM) {
234
throw ProcessError(TLF("Triggered departure for person '%' requires starting with a ride.", myVehicleParameter->id));
235
} else if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED && element != SUMO_TAG_TRANSPORT && element != SUMO_TAG_PARAM) {
236
throw ProcessError(TLF("Triggered departure for container '%' requires starting with a transport.", myVehicleParameter->id));
237
}
238
SUMORouteHandler::myStartElement(element, attrs);
239
bool ok = true;
240
switch (element) {
241
case SUMO_TAG_PERSON:
242
case SUMO_TAG_PERSONFLOW: {
243
myActivePlan = new std::vector<ROPerson::PlanItem*>();
244
break;
245
}
246
case SUMO_TAG_RIDE:
247
break; // handled in addRide, called from SUMORouteHandler::myStartElement
248
case SUMO_TAG_CONTAINER:
249
case SUMO_TAG_CONTAINERFLOW:
250
myActiveContainerPlan = new OutputDevice_String(1);
251
myActiveContainerPlanSize = 0;
252
myActiveContainerPlan->openTag((SumoXMLTag)element);
253
(*myActiveContainerPlan) << attrs;
254
break;
255
case SUMO_TAG_TRANSPORT:
256
case SUMO_TAG_TRANSHIP:
257
if (myActiveContainerPlan == nullptr) {
258
throw ProcessError(TLF("Found % outside container element", toString((SumoXMLTag)element)));
259
}
260
// copy container elements
261
myActiveContainerPlan->openTag((SumoXMLTag)element);
262
(*myActiveContainerPlan) << attrs;
263
myActiveContainerPlan->closeTag();
264
myActiveContainerPlanSize++;
265
break;
266
case SUMO_TAG_FLOW:
267
myActiveRouteProbability = DEFAULT_VEH_PROB;
268
parseFromViaTo((SumoXMLTag)element, attrs, ok);
269
break;
270
case SUMO_TAG_TRIP:
271
myActiveRouteProbability = DEFAULT_VEH_PROB;
272
parseFromViaTo((SumoXMLTag)element, attrs, ok);
273
break;
274
default:
275
break;
276
}
277
} catch (ProcessError&) {
278
deleteActivePlanAndVehicleParameter();
279
throw;
280
}
281
}
282
283
284
void
285
RORouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
286
bool ok = true;
287
myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
288
if (ok) {
289
myCurrentVTypeDistribution = new RandomDistributor<SUMOVTypeParameter*>();
290
if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
291
std::vector<double> probs;
292
if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
293
StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
294
while (st.hasNext()) {
295
probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
296
}
297
}
298
const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
299
StringTokenizer st(vTypes);
300
int probIndex = 0;
301
while (st.hasNext()) {
302
const std::string typeID = st.next();
303
const RandomDistributor<SUMOVTypeParameter*>* const dist = myNet.getVTypeDistribution(typeID);
304
if (dist != nullptr) {
305
const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
306
std::vector<double>::const_iterator probIt = dist->getProbs().begin();
307
for (SUMOVTypeParameter* const type : dist->getVals()) {
308
myCurrentVTypeDistribution->add(type, distProb * *probIt);
309
probIt++;
310
}
311
} else {
312
SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
313
if (type == nullptr) {
314
myErrorOutput->inform("Unknown vehicle type '" + typeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
315
} else {
316
const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->defaultProbability);
317
myCurrentVTypeDistribution->add(type, prob);
318
}
319
}
320
probIndex++;
321
}
322
if (probs.size() > 0 && probIndex != (int)probs.size()) {
323
WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
324
" types in vTypeDistribution '" + myCurrentVTypeDistributionID + "'");
325
}
326
}
327
}
328
}
329
330
331
void
332
RORouteHandler::closeVehicleTypeDistribution() {
333
if (myCurrentVTypeDistribution != nullptr) {
334
if (myCurrentVTypeDistribution->getOverallProb() == 0) {
335
delete myCurrentVTypeDistribution;
336
myErrorOutput->inform("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
337
} else if (!myNet.addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
338
delete myCurrentVTypeDistribution;
339
myErrorOutput->inform("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
340
}
341
myCurrentVTypeDistribution = nullptr;
342
}
343
}
344
345
346
void
347
RORouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
348
myActiveRoute.clear();
349
myInsertStopEdgesAt = -1;
350
// check whether the id is really necessary
351
std::string rid;
352
if (myCurrentAlternatives != nullptr) {
353
myActiveRouteID = myCurrentAlternatives->getID();
354
rid = "distribution '" + myCurrentAlternatives->getID() + "'";
355
} else if (myVehicleParameter != nullptr) {
356
// ok, a vehicle is wrapping the route,
357
// we may use this vehicle's id as default
358
myVehicleParameter->routeid = myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
359
if (attrs.hasAttribute(SUMO_ATTR_ID)) {
360
WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
361
}
362
} else {
363
bool ok = true;
364
myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
365
if (!ok) {
366
return;
367
}
368
rid = "'" + myActiveRouteID + "'";
369
}
370
if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
371
rid = "for vehicle '" + myVehicleParameter->id + "'";
372
}
373
bool ok = true;
374
if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
375
parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid, ok);
376
}
377
myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
378
if (myActiveRouteRefID != "" && myNet.getRouteDef(myActiveRouteRefID) == nullptr) {
379
myErrorOutput->inform("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
380
}
381
if (myCurrentAlternatives != nullptr && !attrs.hasAttribute(SUMO_ATTR_PROB)) {
382
WRITE_WARNINGF(TL("No probability for route %, using default."), rid);
383
}
384
myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
385
if (ok && myActiveRouteProbability < 0) {
386
myErrorOutput->inform("Invalid probability for route '" + myActiveRouteID + "'.");
387
}
388
myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
389
ok = true;
390
myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
391
myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok, 0);
392
if (myActiveRouteRepeat > 0) {
393
SUMOVehicleClass vClass = SVC_IGNORING;
394
if (myVehicleParameter != nullptr) {
395
SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
396
if (type != nullptr) {
397
vClass = type->vehicleClass;
398
}
399
}
400
if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
401
myErrorOutput->inform("Disconnected route " + rid + " when repeating.");
402
}
403
}
404
myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
405
if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
406
myErrorOutput->inform("Invalid cost for route '" + myActiveRouteID + "'.");
407
}
408
}
409
410
411
void
412
RORouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
413
// currently unused
414
}
415
416
417
void
418
RORouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
419
// currently unused
420
}
421
422
423
void
424
RORouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
425
// currently unused
426
}
427
428
429
void
430
RORouteHandler::closeRoute(const bool mayBeDisconnected) {
431
const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
432
if (mustReroute) {
433
// implicit route from stops
434
for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
435
ROEdge* edge = myNet.getEdge(stop.edge);
436
myActiveRoute.push_back(edge);
437
}
438
}
439
if (myActiveRoute.size() == 0) {
440
if (myActiveRouteRefID != "" && myCurrentAlternatives != nullptr) {
441
myCurrentAlternatives->addAlternativeDef(myNet.getRouteDef(myActiveRouteRefID));
442
myActiveRouteID = "";
443
myActiveRouteRefID = "";
444
return;
445
}
446
if (myVehicleParameter != nullptr) {
447
myErrorOutput->inform("The route for vehicle '" + myVehicleParameter->id + "' has no edges.");
448
} else {
449
myErrorOutput->inform("Route '" + myActiveRouteID + "' has no edges.");
450
}
451
myActiveRouteID = "";
452
myActiveRouteStops.clear();
453
return;
454
}
455
if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
456
myErrorOutput->inform("The routing information for vehicle '" + myVehicleParameter->id + "' is insufficient.");
457
myActiveRouteID = "";
458
myActiveRouteStops.clear();
459
return;
460
}
461
if (!mayBeDisconnected && OptionsCont::getOptions().exists("no-internal-links") && !OptionsCont::getOptions().getBool("no-internal-links")) {
462
// fix internal edges which did not get parsed
463
const ROEdge* last = nullptr;
464
ConstROEdgeVector fullRoute;
465
for (const ROEdge* roe : myActiveRoute) {
466
if (last != nullptr) {
467
for (const ROEdge* intern : last->getSuccessors()) {
468
if (intern->isInternal() && intern->getSuccessors().size() == 1 && intern->getSuccessors().front() == roe) {
469
fullRoute.push_back(intern);
470
}
471
}
472
}
473
fullRoute.push_back(roe);
474
last = roe;
475
}
476
myActiveRoute = fullRoute;
477
}
478
if (myActiveRouteRepeat > 0) {
479
// duplicate route
480
ConstROEdgeVector tmpEdges = myActiveRoute;
481
auto tmpStops = myActiveRouteStops;
482
for (int i = 0; i < myActiveRouteRepeat; i++) {
483
myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
484
for (SUMOVehicleParameter::Stop stop : tmpStops) {
485
if (stop.until > 0) {
486
if (myActiveRoutePeriod <= 0) {
487
const std::string description = myVehicleParameter != nullptr
488
? "for vehicle '" + myVehicleParameter->id + "'"
489
: "'" + myActiveRouteID + "'";
490
throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
491
}
492
stop.until += myActiveRoutePeriod * (i + 1);
493
stop.arrival += myActiveRoutePeriod * (i + 1);
494
}
495
myActiveRouteStops.push_back(stop);
496
}
497
}
498
}
499
RORoute* route = new RORoute(myActiveRouteID, myCurrentCosts, myActiveRouteProbability, myActiveRoute,
500
myActiveRouteColor, myActiveRouteStops);
501
myActiveRoute.clear();
502
if (myCurrentAlternatives == nullptr) {
503
if (myNet.getRouteDef(myActiveRouteID) != nullptr) {
504
delete route;
505
if (myVehicleParameter != nullptr) {
506
myErrorOutput->inform("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
507
} else {
508
myErrorOutput->inform("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
509
}
510
myActiveRouteID = "";
511
myActiveRouteStops.clear();
512
return;
513
} else {
514
myCurrentAlternatives = new RORouteDef(myActiveRouteID, 0, mayBeDisconnected || myTryRepair, mayBeDisconnected);
515
myCurrentAlternatives->addLoadedAlternative(route);
516
myNet.addRouteDef(myCurrentAlternatives);
517
myCurrentAlternatives = nullptr;
518
}
519
} else {
520
myCurrentAlternatives->addLoadedAlternative(route);
521
}
522
myActiveRouteID = "";
523
myActiveRouteStops.clear();
524
}
525
526
527
void
528
RORouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
529
// check whether the id is really necessary
530
bool ok = true;
531
std::string id;
532
if (myVehicleParameter != nullptr) {
533
// ok, a vehicle is wrapping the route,
534
// we may use this vehicle's id as default
535
myVehicleParameter->routeid = id = "!" + myVehicleParameter->id; // !!! document this
536
if (attrs.hasAttribute(SUMO_ATTR_ID)) {
537
WRITE_WARNINGF(TL("Ids of internal route distributions are ignored (vehicle '%')."), myVehicleParameter->id);
538
}
539
} else {
540
id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
541
if (!ok) {
542
return;
543
}
544
}
545
// try to get the index of the last element
546
int index = attrs.getOpt<int>(SUMO_ATTR_LAST, id.c_str(), ok, 0);
547
if (ok && index < 0) {
548
myErrorOutput->inform("Negative index of a route alternative (id='" + id + "').");
549
return;
550
}
551
// build the alternative cont
552
myCurrentAlternatives = new RORouteDef(id, index, myTryRepair, false);
553
if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
554
ok = true;
555
StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, id.c_str(), ok));
556
while (st.hasNext()) {
557
const std::string routeID = st.next();
558
const RORouteDef* route = myNet.getRouteDef(routeID);
559
if (route == nullptr) {
560
myErrorOutput->inform("Unknown route '" + routeID + "' in distribution '" + id + "'.");
561
} else {
562
myCurrentAlternatives->addAlternativeDef(route);
563
}
564
}
565
}
566
}
567
568
569
void
570
RORouteHandler::closeRouteDistribution() {
571
if (myCurrentAlternatives != nullptr) {
572
if (myCurrentAlternatives->getOverallProb() == 0) {
573
myErrorOutput->inform("Route distribution '" + myCurrentAlternatives->getID() + "' is empty.");
574
delete myCurrentAlternatives;
575
} else if (!myNet.addRouteDef(myCurrentAlternatives)) {
576
myErrorOutput->inform("Another route (or distribution) with the id '" + myCurrentAlternatives->getID() + "' exists.");
577
delete myCurrentAlternatives;
578
} else {
579
if (myVehicleParameter != nullptr
580
&& (myUseTaz || OptionsCont::getOptions().getBool("junction-taz"))
581
&& (myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) ||
582
myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET))) {
583
// we are loading a rou.alt.xml, permit rerouting between taz
584
bool ok = true;
585
ConstROEdgeVector edges;
586
if (myVehicleParameter->fromTaz != "") {
587
const std::string tazID = myVehicleParameter->fromTaz;
588
const ROEdge* fromTaz = myNet.getEdge(tazID + "-source");
589
if (fromTaz == nullptr) {
590
myErrorOutput->inform("Source taz '" + tazID + "' not known for vehicle '" + myVehicleParameter->id + "'!");
591
ok = false;
592
} else if (fromTaz->getNumSuccessors() == 0) {
593
myErrorOutput->inform("Source taz '" + tazID + "' has no outgoing edges for vehicle '" + myVehicleParameter->id + "'!");
594
ok = false;
595
} else {
596
edges.push_back(fromTaz);
597
}
598
} else {
599
edges.push_back(myCurrentAlternatives->getOrigin());
600
}
601
if (myVehicleParameter->toTaz != "") {
602
const std::string tazID = myVehicleParameter->toTaz;
603
const ROEdge* toTaz = myNet.getEdge(tazID + "-sink");
604
if (toTaz == nullptr) {
605
myErrorOutput->inform("Sink taz '" + tazID + "' not known for vehicle '" + myVehicleParameter->id + "'!");
606
ok = false;
607
} else if (toTaz->getNumPredecessors() == 0) {
608
myErrorOutput->inform("Sink taz '" + tazID + "' has no incoming edges for vehicle '" + myVehicleParameter->id + "'!");
609
ok = false;
610
} else {
611
edges.push_back(toTaz);
612
}
613
} else {
614
edges.push_back(myCurrentAlternatives->getDestination());
615
}
616
if (ok) {
617
// negative probability indicates that this route should not be written
618
RORoute* route = new RORoute(myCurrentAlternatives->getID(), 0, -1, edges, nullptr, myActiveRouteStops);
619
myCurrentAlternatives->addLoadedAlternative(route);
620
}
621
}
622
}
623
myCurrentAlternatives = nullptr;
624
}
625
}
626
627
628
void
629
RORouteHandler::closeVehicle() {
630
checkLastDepart();
631
// get the vehicle id
632
if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN && myVehicleParameter->depart < myBegin) {
633
mySkippedVehicles.insert(myVehicleParameter->id);
634
return;
635
}
636
// get vehicle type
637
SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
638
if (type == nullptr) {
639
myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
640
type = myNet.getVehicleTypeSecure(DEFAULT_VTYPE_ID);
641
} else {
642
if (!myKeepVTypeDist) {
643
// fix the type id in case we used a distribution
644
myVehicleParameter->vtypeid = type->id;
645
}
646
}
647
if (type->vehicleClass == SVC_PEDESTRIAN) {
648
WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), type->id, myVehicleParameter->id);
649
}
650
// get the route
651
RORouteDef* route = myNet.getRouteDef(myVehicleParameter->routeid);
652
if (route == nullptr) {
653
myErrorOutput->inform("The route of the vehicle '" + myVehicleParameter->id + "' is not known.");
654
return;
655
}
656
if (MsgHandler::getErrorInstance()->wasInformed()) {
657
return;
658
}
659
const bool needCopy = route->getID()[0] != '!';
660
if (needCopy) {
661
route = route->copy("!" + myVehicleParameter->id, myVehicleParameter->depart);
662
}
663
// build the vehicle
664
ROVehicle* veh = new ROVehicle(*myVehicleParameter, route, type, &myNet, myErrorOutput);
665
if (myNet.addVehicle(myVehicleParameter->id, veh)) {
666
registerLastDepart();
667
} else if (needCopy) {
668
delete route;
669
}
670
delete myVehicleParameter;
671
myVehicleParameter = nullptr;
672
}
673
674
675
void
676
RORouteHandler::closeVType() {
677
if (myNet.addVehicleType(myCurrentVType)) {
678
if (myCurrentVTypeDistribution != nullptr) {
679
myCurrentVTypeDistribution->add(myCurrentVType, myCurrentVType->defaultProbability);
680
}
681
}
682
if (OptionsCont::getOptions().isSet("restriction-params")) {
683
const std::vector<std::string> paramKeys = OptionsCont::getOptions().getStringVector("restriction-params");
684
myCurrentVType->cacheParamRestrictions(paramKeys);
685
}
686
myCurrentVType = nullptr;
687
}
688
689
690
void
691
RORouteHandler::closePerson() {
692
SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
693
if (type == nullptr) {
694
myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
695
type = myNet.getVehicleTypeSecure(DEFAULT_PEDTYPE_ID);
696
}
697
if (myActivePlan == nullptr || myActivePlan->empty()) {
698
WRITE_WARNINGF(TL("Discarding person '%' because her plan is empty"), myVehicleParameter->id);
699
} else if (myVehicleParameter->departProcedure != DepartDefinition::GIVEN || myVehicleParameter->depart >= myBegin) {
700
ROPerson* person = new ROPerson(*myVehicleParameter, type);
701
for (ROPerson::PlanItem* item : *myActivePlan) {
702
person->getPlan().push_back(item);
703
}
704
if (myNet.addPerson(person)) {
705
checkLastDepart();
706
registerLastDepart();
707
}
708
}
709
delete myVehicleParameter;
710
myVehicleParameter = nullptr;
711
delete myActivePlan;
712
myActivePlan = nullptr;
713
}
714
715
716
void
717
RORouteHandler::closePersonFlow() {
718
std::string typeID = DEFAULT_PEDTYPE_ID;
719
if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
720
myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for personFlow '" + myVehicleParameter->id + "' is not known.");
721
} else {
722
typeID = myVehicleParameter->vtypeid;
723
}
724
if (myActivePlan == nullptr || myActivePlan->empty()) {
725
WRITE_WARNINGF(TL("Discarding personFlow '%' because their plan is empty"), myVehicleParameter->id);
726
} else {
727
checkLastDepart();
728
// instantiate all persons of this flow
729
int i = 0;
730
std::string baseID = myVehicleParameter->id;
731
if (myWriteFlows) {
732
addFlowPerson(typeID, myVehicleParameter->depart, baseID, i);
733
} else if (myVehicleParameter->repetitionProbability > 0) {
734
if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
735
throw ProcessError(TLF("probabilistic personFlow '%' must specify end time", myVehicleParameter->id));
736
} else {
737
for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
738
if (RandHelper::rand() < myVehicleParameter->repetitionProbability) {
739
addFlowPerson(typeID, t, baseID, i++);
740
}
741
}
742
}
743
} else {
744
SUMOTime depart = myVehicleParameter->depart;
745
// uniform sampling of departures from range is equivalent to poisson flow (encoded by negative offset)
746
if (OptionsCont::getOptions().getBool("randomize-flows") && myVehicleParameter->repetitionOffset >= 0) {
747
std::vector<SUMOTime> departures;
748
const SUMOTime range = myVehicleParameter->repetitionNumber * myVehicleParameter->repetitionOffset;
749
for (int j = 0; j < myVehicleParameter->repetitionNumber; ++j) {
750
departures.push_back(depart + RandHelper::rand(range));
751
}
752
std::sort(departures.begin(), departures.end());
753
std::reverse(departures.begin(), departures.end());
754
for (; i < myVehicleParameter->repetitionNumber; i++) {
755
addFlowPerson(typeID, departures[i], baseID, i);
756
depart += myVehicleParameter->repetitionOffset;
757
}
758
} else {
759
const bool triggered = myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED;
760
if (myVehicleParameter->repetitionOffset < 0) {
761
// poisson: randomize first depart
762
myVehicleParameter->incrementFlow(1);
763
}
764
for (; i < myVehicleParameter->repetitionNumber && (triggered || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
765
addFlowPerson(typeID, depart + myVehicleParameter->repetitionTotalOffset, baseID, i);
766
if (myVehicleParameter->departProcedure != DepartDefinition::TRIGGERED) {
767
myVehicleParameter->incrementFlow(1);
768
}
769
}
770
}
771
}
772
}
773
delete myVehicleParameter;
774
myVehicleParameter = nullptr;
775
for (ROPerson::PlanItem* const it : *myActivePlan) {
776
delete it;
777
}
778
delete myActivePlan;
779
myActivePlan = nullptr;
780
}
781
782
783
void
784
RORouteHandler::addFlowPerson(const std::string& typeID, SUMOTime depart, const std::string& baseID, int i) {
785
SUMOVehicleParameter pars = *myVehicleParameter;
786
pars.id = baseID + "." + toString(i);
787
pars.depart = depart;
788
const SUMOVTypeParameter* const type = myNet.getVehicleTypeSecure(typeID);
789
if (!myKeepVTypeDist) {
790
pars.vtypeid = type->id;
791
}
792
ROPerson* person = new ROPerson(pars, type);
793
for (ROPerson::PlanItem* item : *myActivePlan) {
794
person->getPlan().push_back(item->clone());
795
}
796
if (myNet.addPerson(person)) {
797
if (i == 0) {
798
registerLastDepart();
799
}
800
}
801
}
802
803
804
void
805
RORouteHandler::closeContainer() {
806
myActiveContainerPlan->closeTag();
807
if (myActiveContainerPlanSize > 0) {
808
myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
809
checkLastDepart();
810
registerLastDepart();
811
} else {
812
WRITE_WARNINGF(TL("Discarding container '%' because its plan is empty"), myVehicleParameter->id);
813
}
814
delete myVehicleParameter;
815
myVehicleParameter = nullptr;
816
delete myActiveContainerPlan;
817
myActiveContainerPlan = nullptr;
818
myActiveContainerPlanSize = 0;
819
}
820
821
822
void RORouteHandler::closeContainerFlow() {
823
myActiveContainerPlan->closeTag();
824
if (myActiveContainerPlanSize > 0) {
825
myNet.addContainer(myVehicleParameter->depart, myActiveContainerPlan->getString());
826
checkLastDepart();
827
registerLastDepart();
828
} else {
829
WRITE_WARNINGF(TL("Discarding containerFlow '%' because its plan is empty"), myVehicleParameter->id);
830
}
831
delete myVehicleParameter;
832
myVehicleParameter = nullptr;
833
delete myActiveContainerPlan;
834
myActiveContainerPlan = nullptr;
835
myActiveContainerPlanSize = 0;
836
}
837
838
839
void
840
RORouteHandler::closeFlow() {
841
checkLastDepart();
842
// @todo: consider myScale?
843
if (myVehicleParameter->repetitionNumber == 0) {
844
delete myVehicleParameter;
845
myVehicleParameter = nullptr;
846
return;
847
}
848
// let's check whether vehicles had to depart before the simulation starts
849
myVehicleParameter->repetitionsDone = 0;
850
const SUMOTime offsetToBegin = myBegin - myVehicleParameter->depart;
851
while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
852
myVehicleParameter->incrementFlow(1);
853
if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
854
delete myVehicleParameter;
855
myVehicleParameter = nullptr;
856
return;
857
}
858
}
859
if (myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid) == nullptr) {
860
myErrorOutput->inform("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
861
}
862
if (myVehicleParameter->routeid[0] == '!' && myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
863
closeRoute(true);
864
}
865
if (myNet.getRouteDef(myVehicleParameter->routeid) == nullptr) {
866
myErrorOutput->inform("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
867
delete myVehicleParameter;
868
myVehicleParameter = nullptr;
869
return;
870
}
871
myActiveRouteID = "";
872
if (!MsgHandler::getErrorInstance()->wasInformed()) {
873
if (myNet.addFlow(myVehicleParameter, OptionsCont::getOptions().getBool("randomize-flows"))) {
874
registerLastDepart();
875
} else {
876
myErrorOutput->inform("Another flow with the id '" + myVehicleParameter->id + "' exists.");
877
delete myVehicleParameter;
878
}
879
} else {
880
delete myVehicleParameter;
881
}
882
myVehicleParameter = nullptr;
883
myInsertStopEdgesAt = -1;
884
}
885
886
887
void
888
RORouteHandler::closeTrip() {
889
closeRoute(true);
890
closeVehicle();
891
}
892
893
894
const SUMOVehicleParameter::Stop*
895
RORouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, std::string& id, const SUMOVehicleParameter::Stop* stopParam) {
896
// dummy stop parameter to hold the attributes
897
SUMOVehicleParameter::Stop stop;
898
if (stopParam != nullptr) {
899
stop = *stopParam;
900
} else {
901
bool ok = true;
902
stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
903
stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
904
stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
905
stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
906
stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
907
stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
908
}
909
const SUMOVehicleParameter::Stop* toStop = nullptr;
910
if (stop.busstop != "") {
911
toStop = myNet.getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
912
id = stop.busstop;
913
if (toStop == nullptr) {
914
WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
915
}
916
} else if (stop.containerstop != "") {
917
toStop = myNet.getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
918
id = stop.containerstop;
919
if (toStop == nullptr) {
920
WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
921
}
922
} else if (stop.parkingarea != "") {
923
toStop = myNet.getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
924
id = stop.parkingarea;
925
if (toStop == nullptr) {
926
WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
927
}
928
} else if (stop.chargingStation != "") {
929
// ok, we have a charging station
930
toStop = myNet.getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
931
id = stop.chargingStation;
932
if (toStop == nullptr) {
933
WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
934
}
935
} else if (stop.overheadWireSegment != "") {
936
// ok, we have an overhead wire segment
937
toStop = myNet.getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
938
id = stop.overheadWireSegment;
939
if (toStop == nullptr) {
940
WRITE_ERROR("The overhead wire segment '" + stop.overheadWireSegment + "' is not known" + errorSuffix);
941
}
942
}
943
return toStop;
944
}
945
946
Parameterised*
947
RORouteHandler::addStop(const SUMOSAXAttributes& attrs) {
948
Parameterised* result = nullptr;
949
if (myActiveContainerPlan != nullptr) {
950
myActiveContainerPlan->openTag(SUMO_TAG_STOP);
951
(*myActiveContainerPlan) << attrs;
952
myActiveContainerPlan->closeTag();
953
myActiveContainerPlanSize++;
954
return result;
955
}
956
std::string errorSuffix;
957
if (myActivePlan != nullptr) {
958
errorSuffix = " in person '" + myVehicleParameter->id + "'.";
959
} else if (myActiveContainerPlan != nullptr) {
960
errorSuffix = " in container '" + myVehicleParameter->id + "'.";
961
} else if (myVehicleParameter != nullptr) {
962
errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
963
} else {
964
errorSuffix = " in route '" + myActiveRouteID + "'.";
965
}
966
SUMOVehicleParameter::Stop stop;
967
bool ok = parseStop(stop, attrs, errorSuffix, myErrorOutput);
968
if (!ok) {
969
return result;
970
}
971
// try to parse the assigned bus stop
972
const ROEdge* edge = nullptr;
973
std::string stoppingPlaceID;
974
const SUMOVehicleParameter::Stop* stoppingPlace = retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID, &stop);
975
bool hasPos = false;
976
if (stoppingPlace != nullptr) {
977
stop.lane = stoppingPlace->lane;
978
stop.endPos = stoppingPlace->endPos;
979
stop.startPos = stoppingPlace->startPos;
980
edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
981
} else {
982
// no, the lane and the position should be given
983
stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
984
stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
985
if (ok && stop.edge != "") {
986
edge = myNet.getEdge(stop.edge);
987
if (edge == nullptr) {
988
myErrorOutput->inform("The edge '" + stop.edge + "' for a stop is not known" + errorSuffix);
989
return result;
990
}
991
} else if (ok && stop.lane != "") {
992
edge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop.lane));
993
if (edge == nullptr) {
994
myErrorOutput->inform("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
995
return result;
996
}
997
} else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
998
|| (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
999
Position pos;
1000
bool geo = false;
1001
if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
1002
pos = Position(attrs.get<double>(SUMO_ATTR_X, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_Y, myVehicleParameter->id.c_str(), ok));
1003
} else {
1004
pos = Position(attrs.get<double>(SUMO_ATTR_LON, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_LAT, myVehicleParameter->id.c_str(), ok));
1005
geo = true;
1006
}
1007
PositionVector positions;
1008
positions.push_back(pos);
1009
ConstROEdgeVector geoEdges;
1010
SUMOVehicleClass vClass = SVC_PASSENGER;
1011
if (!myNet.getVTypeDistribution(myVehicleParameter->vtypeid)) {
1012
SUMOVTypeParameter* type = myNet.getVehicleTypeSecure(myVehicleParameter->vtypeid);
1013
if (type != nullptr) {
1014
vClass = type->vehicleClass;
1015
}
1016
}
1017
parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
1018
if (ok) {
1019
edge = geoEdges.front();
1020
hasPos = true;
1021
if (geo) {
1022
GeoConvHelper::getFinal().x2cartesian_const(pos);
1023
}
1024
stop.parametersSet |= STOP_END_SET;
1025
stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
1026
} else {
1027
return result;
1028
}
1029
} else if (!ok || (stop.lane == "" && stop.edge == "")) {
1030
myErrorOutput->inform("A stop must be placed on a bus stop, a container stop, a parking area, an edge or a lane" + errorSuffix);
1031
return result;
1032
}
1033
if (!hasPos) {
1034
stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
1035
}
1036
stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, stop.endPos - 2 * POSITION_EPS);
1037
const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS));
1038
const double endPosOffset = edge->isInternal() ? edge->getNormalBefore()->getLength() : 0;
1039
if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength() + endPosOffset, POSITION_EPS, friendlyPos) != SUMORouteHandler::StopPos::STOPPOS_VALID)) {
1040
myErrorOutput->inform("Invalid start or end position for stop" + errorSuffix);
1041
return result;
1042
}
1043
}
1044
stop.edge = edge->getID();
1045
if (myActivePlan != nullptr) {
1046
ROPerson::addStop(*myActivePlan, stop, edge);
1047
result = myActivePlan->back()->getStopParameters();
1048
} else if (myVehicleParameter != nullptr) {
1049
myVehicleParameter->stops.push_back(stop);
1050
result = &myVehicleParameter->stops.back();
1051
} else {
1052
myActiveRouteStops.push_back(stop);
1053
result = &myActiveRouteStops.back();
1054
}
1055
if (myInsertStopEdgesAt >= 0) {
1056
myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
1057
myInsertStopEdgesAt++;
1058
}
1059
return result;
1060
}
1061
1062
1063
void
1064
RORouteHandler::addRide(const SUMOSAXAttributes& attrs) {
1065
bool ok = true;
1066
std::vector<ROPerson::PlanItem*>& plan = *myActivePlan;
1067
const std::string pid = myVehicleParameter->id;
1068
1069
const ROEdge* from = nullptr;
1070
const ROEdge* to = nullptr;
1071
parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
1072
if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
1073
|| attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
1074
if (ok) {
1075
from = myActiveRoute.front();
1076
}
1077
} else if (plan.empty()) {
1078
myErrorOutput->inform("The start edge for person '" + pid + "' is not known.");
1079
return;
1080
}
1081
std::string stoppingPlaceID;
1082
const SUMOVehicleParameter::Stop* stop = retrieveStoppingPlace(attrs, " for ride of person '" + myVehicleParameter->id + "'", stoppingPlaceID);
1083
if (stop != nullptr) {
1084
to = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(stop->lane));
1085
} else {
1086
if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1087
|| attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
1088
to = myActiveRoute.back();
1089
} else {
1090
myErrorOutput->inform("The to edge is missing within a ride of '" + myVehicleParameter->id + "'.");
1091
return;
1092
}
1093
}
1094
double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
1095
stop == nullptr ? std::numeric_limits<double>::infinity() : stop->endPos);
1096
const std::string lines = attrs.getOpt<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok, LINE_ANY);
1097
const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, pid.c_str(), ok, "");
1098
1099
if (plan.empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
1100
StringTokenizer st(lines);
1101
if (st.size() != 1 || st.get(0) == LINE_ANY) {
1102
myErrorOutput->inform("Triggered departure for person '" + pid + "' requires a unique lines value.");
1103
return;
1104
}
1105
const std::string vehID = st.front();
1106
if (myNet.knowsVehicle(vehID)) {
1107
const SUMOTime vehDepart = myNet.getDeparture(vehID);
1108
if (vehDepart == -1) {
1109
myErrorOutput->inform("Cannot use triggered vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
1110
return;
1111
}
1112
myVehicleParameter->depart = vehDepart + 1; // write person after vehicle
1113
} else {
1114
if (mySkippedVehicles.count(vehID) == 0) {
1115
myErrorOutput->inform("Unknown vehicle '" + vehID + "' in triggered departure for person '" + pid + "'.");
1116
return;
1117
}
1118
myVehicleParameter->departProcedure = DepartDefinition::GIVEN; // make sure the person gets skipped due to depart time
1119
}
1120
}
1121
ROPerson::addRide(plan, from, to, lines, arrivalPos, stoppingPlaceID, group);
1122
}
1123
1124
1125
void
1126
RORouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
1127
if (myActiveContainerPlan != nullptr && myActiveContainerPlanSize == 0 && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
1128
bool ok = true;
1129
const std::string pid = myVehicleParameter->id;
1130
const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
1131
StringTokenizer st(desc);
1132
if (st.size() != 1) {
1133
throw ProcessError(TLF("Triggered departure for container '%' requires a unique lines value.", pid));
1134
}
1135
const std::string vehID = st.front();
1136
if (!myNet.knowsVehicle(vehID)) {
1137
if (mySkippedVehicles.count(vehID) == 0) {
1138
throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
1139
}
1140
return;
1141
}
1142
SUMOTime vehDepart = myNet.getDeparture(vehID);
1143
if (vehDepart == -1) {
1144
throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for container '" + pid + "'.");
1145
}
1146
myVehicleParameter->depart = vehDepart + 1; // write container after vehicle
1147
}
1148
}
1149
1150
1151
void
1152
RORouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
1153
}
1154
1155
1156
void
1157
RORouteHandler::parseEdges(const std::string& desc, ConstROEdgeVector& into,
1158
const std::string& rid, bool& ok) {
1159
for (StringTokenizer st(desc); st.hasNext();) {
1160
const std::string id = st.next();
1161
const ROEdge* edge = myNet.getEdge(id);
1162
if (edge == nullptr) {
1163
myErrorOutput->inform("The edge '" + id + "' within the route " + rid + " is not known.");
1164
ok = false;
1165
} else {
1166
into.push_back(edge);
1167
}
1168
}
1169
}
1170
1171
1172
void
1173
RORouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
1174
const ROEdge* /*fromEdge*/, const ROEdge*& toEdge,
1175
double& departPos, double& arrivalPos, std::string& busStopID,
1176
const ROPerson::PlanItem* const lastStage, bool& ok) {
1177
const std::string description = "walk or personTrip of '" + personID + "'.";
1178
if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1179
WRITE_WARNING(TL("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops."));
1180
}
1181
departPos = myVehicleParameter->departPos;
1182
if (lastStage != nullptr) {
1183
departPos = lastStage->getDestinationPos();
1184
}
1185
1186
busStopID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1187
1188
const SUMOVehicleParameter::Stop* bs = retrieveStoppingPlace(attrs, description, busStopID);
1189
if (bs != nullptr) {
1190
toEdge = myNet.getEdge(SUMOXMLDefinitions::getEdgeIDFromLane(bs->lane));
1191
arrivalPos = (bs->startPos + bs->endPos) / 2;
1192
}
1193
if (toEdge != nullptr) {
1194
if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1195
arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS,
1196
myHardFail, description, toEdge->getLength(),
1197
attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok));
1198
}
1199
} else {
1200
throw ProcessError(TLF("No destination edge for %.", description));
1201
}
1202
}
1203
1204
1205
void
1206
RORouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1207
bool ok = true;
1208
const char* const id = myVehicleParameter->id.c_str();
1209
assert(!attrs.hasAttribute(SUMO_ATTR_EDGES));
1210
const ROEdge* from = nullptr;
1211
const ROEdge* to = nullptr;
1212
parseFromViaTo(SUMO_TAG_PERSON, attrs, ok);
1213
myInsertStopEdgesAt = -1;
1214
if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
1215
|| attrs.hasAttribute(SUMO_ATTR_FROMLONLAT) || attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
1216
if (ok) {
1217
from = myActiveRoute.front();
1218
}
1219
} else if (myActivePlan->empty()) {
1220
throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
1221
} else {
1222
from = myActivePlan->back()->getDestination();
1223
}
1224
if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1225
|| attrs.hasAttribute(SUMO_ATTR_TOLONLAT) || attrs.hasAttribute(SUMO_ATTR_TOXY)) {
1226
to = myActiveRoute.back();
1227
} // else, to may also be derived from stopping place
1228
1229
const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1230
if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1231
throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1232
}
1233
1234
double departPos = 0;
1235
double arrivalPos = std::numeric_limits<double>::infinity();
1236
std::string busStopID;
1237
const ROPerson::PlanItem* const lastStage = myActivePlan->empty() ? nullptr : myActivePlan->back();
1238
parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, busStopID, lastStage, ok);
1239
1240
const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1241
const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, "");
1242
SVCPermissions modeSet = 0;
1243
for (StringTokenizer st(modes); st.hasNext();) {
1244
const std::string mode = st.next();
1245
if (mode == "car") {
1246
modeSet |= SVC_PASSENGER;
1247
} else if (mode == "taxi") {
1248
modeSet |= SVC_TAXI;
1249
} else if (mode == "bicycle") {
1250
modeSet |= SVC_BICYCLE;
1251
} else if (mode == "public") {
1252
modeSet |= SVC_BUS;
1253
} else {
1254
throw InvalidArgument("Unknown person mode '" + mode + "'.");
1255
}
1256
}
1257
const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
1258
double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1259
if (ok) {
1260
const std::string originStopID = myActivePlan->empty() ? "" : myActivePlan->back()->getStopDest();
1261
ROPerson::addTrip(*myActivePlan, myVehicleParameter->id, from, to, modeSet, types,
1262
departPos, originStopID, arrivalPos, busStopID, walkFactor, group);
1263
myParamStack.push_back(myActivePlan->back());
1264
}
1265
}
1266
1267
1268
void
1269
RORouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1270
// parse walks from->to as person trips
1271
if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1272
// XXX allow --repair?
1273
bool ok = true;
1274
if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1275
const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1276
RORouteDef* routeDef = myNet.getRouteDef(routeID);
1277
const RORoute* route = routeDef != nullptr ? routeDef->getFirstRoute() : nullptr;
1278
if (route == nullptr) {
1279
throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1280
}
1281
myActiveRoute = route->getEdgeVector();
1282
} else {
1283
myActiveRoute.clear();
1284
parseEdges(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, " walk for person '" + myVehicleParameter->id + "'", ok);
1285
}
1286
const char* const objId = myVehicleParameter->id.c_str();
1287
const double duration = attrs.getOpt<double>(SUMO_ATTR_DURATION, objId, ok, -1);
1288
if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1289
throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1290
}
1291
const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, objId, ok, -1.);
1292
if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1293
throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
1294
}
1295
double departPos = 0.;
1296
double arrivalPos = std::numeric_limits<double>::infinity();
1297
if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1298
WRITE_WARNING(TL("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops."));
1299
}
1300
if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1301
arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, objId, myActiveRoute.back()->getLength(), attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, objId, ok));
1302
}
1303
std::string stoppingPlaceID;
1304
const std::string errorSuffix = " for walk of person '" + myVehicleParameter->id + "'";
1305
retrieveStoppingPlace(attrs, errorSuffix, stoppingPlaceID);
1306
if (ok) {
1307
ROPerson::addWalk(*myActivePlan, myActiveRoute, duration, speed, departPos, arrivalPos, stoppingPlaceID);
1308
myParamStack.push_back(myActivePlan->back());
1309
}
1310
} else {
1311
addPersonTrip(attrs);
1312
}
1313
}
1314
1315
1316
void
1317
RORouteHandler::initLaneTree(NamedRTree* tree) {
1318
for (const auto& edgeItem : myNet.getEdgeMap()) {
1319
for (ROLane* lane : edgeItem.second->getLanes()) {
1320
Boundary b = lane->getShape().getBoxBoundary();
1321
const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
1322
const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
1323
tree->Insert(cmin, cmax, lane);
1324
}
1325
}
1326
}
1327
1328
1329
ROEdge*
1330
RORouteHandler::retrieveEdge(const std::string& id) {
1331
return myNet.getEdge(id);
1332
}
1333
1334
bool
1335
RORouteHandler::checkLastDepart() {
1336
if (!myUnsortedInput) {
1337
return SUMORouteHandler::checkLastDepart();
1338
}
1339
return true;
1340
}
1341
1342
/****************************************************************************/
1343
1344