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