Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 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 MSRouteHandler.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Sascha Krieg
18 : /// @author Michael Behrisch
19 : /// @author Johannes Rummel
20 : /// @date Mon, 9 Jul 2001
21 : ///
22 : // Parser and container for routes during their loading
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include "MSRouteHandler.h"
27 : #include <microsim/transportables/MSTransportableControl.h>
28 : #include <microsim/transportables/MSPModel.h>
29 : #include <microsim/transportables/MSStageDriving.h>
30 : #include <microsim/transportables/MSStageTranship.h>
31 : #include <microsim/transportables/MSStageTrip.h>
32 : #include <microsim/transportables/MSStageWaiting.h>
33 : #include <microsim/transportables/MSStageWalking.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/MSLane.h>
36 : #include <microsim/MSInsertionControl.h>
37 : #include <microsim/MSStoppingPlace.h>
38 : #include <microsim/MSVehicleControl.h>
39 : #include <microsim/MSEventControl.h>
40 : #include <microsim/Command_RouteReplacement.h>
41 : #include <utils/common/StringTokenizer.h>
42 : #include <utils/common/StringUtils.h>
43 : #include <utils/options/OptionsCont.h>
44 : #include <utils/vehicle/SUMOVehicleParserHelper.h>
45 :
46 : // ===========================================================================
47 : // static members
48 : // ===========================================================================
49 : SumoRNG MSRouteHandler::myParsingRNG("routehandler");
50 :
51 :
52 : // ===========================================================================
53 : // method definitions
54 : // ===========================================================================
55 79719 : MSRouteHandler::MSRouteHandler(const std::string& file, bool addVehiclesDirectly) :
56 : SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes", true),
57 159438 : MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
58 159438 : OptionsCont::getOptions().getBool("mapmatch.taz"),
59 79719 : OptionsCont::getOptions().getFloat("mapmatch.distance"),
60 : MsgHandler::getErrorInstance()),
61 79719 : myActiveRouteRepeat(0),
62 79719 : myActiveRoutePeriod(0),
63 79719 : myActiveRoutePermanent(false),
64 79719 : myActiveType(ObjectTypeEnum::UNDEFINED),
65 79719 : myHaveVia(false),
66 79719 : myActiveTransportablePlan(nullptr),
67 79719 : myAddVehiclesDirectly(addVehiclesDirectly),
68 79719 : myCurrentVTypeDistribution(nullptr),
69 79719 : myCurrentRouteDistribution(nullptr),
70 79719 : myAmLoadingState(false),
71 79719 : myScaleSuffix(OptionsCont::getOptions().getString("scale-suffix")),
72 79719 : myReplayRerouting(OptionsCont::getOptions().getBool("replay-rerouting")),
73 354360 : myStartTriggeredInFlow(false) {
74 79719 : myActiveRoute.reserve(100);
75 79719 : }
76 :
77 :
78 186168 : MSRouteHandler::~MSRouteHandler() {}
79 :
80 :
81 : void
82 1735 : MSRouteHandler::deleteActivePlanAndVehicleParameter() {
83 1735 : if (myActiveTransportablePlan != nullptr) {
84 365 : for (MSStage* const s : *myActiveTransportablePlan) {
85 212 : delete s;
86 : }
87 153 : delete myActiveTransportablePlan;
88 : }
89 1735 : delete myVehicleParameter;
90 1735 : resetActivePlanAndVehicleParameter();
91 1735 : }
92 :
93 :
94 : void
95 44327 : MSRouteHandler::resetActivePlanAndVehicleParameter() {
96 44327 : myVehicleParameter = nullptr;
97 44327 : myActiveTransportablePlan = nullptr;
98 44327 : myActiveType = ObjectTypeEnum::UNDEFINED;
99 44327 : }
100 :
101 :
102 : void
103 64684 : MSRouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs) {
104 64684 : const std::string element = toString(tag);
105 : myActiveRoute.clear();
106 64684 : bool useTaz = OptionsCont::getOptions().getBool("with-taz");
107 64684 : if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
108 1344 : WRITE_WARNINGF(TL("Taz usage was requested but no taz present in % '%'!"), element, myVehicleParameter->id);
109 : useTaz = false;
110 : }
111 64684 : bool ok = true;
112 64736 : const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
113 : SUMOVehicleClass vClass = SVC_PASSENGER;
114 64684 : auto& vc = MSNet::getInstance()->getVehicleControl();
115 64684 : if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
116 63888 : MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
117 63888 : if (type != nullptr) {
118 63888 : vClass = type->getParameter().vehicleClass;
119 : }
120 : }
121 : // from-attributes
122 82822 : if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
123 33639 : (attrs.hasAttribute(SUMO_ATTR_FROM_TAZ) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION))) {
124 3561 : const bool useJunction = attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION);
125 6198 : const std::string tazType = useJunction ? "junction" : "taz";
126 6198 : const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_FROM_JUNCTION : SUMO_ATTR_FROM_TAZ, myVehicleParameter->id.c_str(), ok);
127 3593 : const MSEdge* fromTaz = MSEdge::dictionary(tazID + "-source");
128 3561 : if (fromTaz == nullptr) {
129 21 : throw ProcessError("Source " + tazType + " '" + tazID + "' not known " + rid + "!"
130 42 : + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
131 3540 : } else if (fromTaz->getNumSuccessors() == 0 && tag != SUMO_TAG_PERSON) {
132 22 : throw ProcessError("Source " + tazType + " '" + tazID + "' has no outgoing edges " + rid + "!");
133 : } else {
134 3529 : myActiveRoute.push_back(fromTaz);
135 : }
136 61123 : } else if (attrs.hasAttribute(SUMO_ATTR_FROMXY)) {
137 168 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMXY, myVehicleParameter->id.c_str(), ok), false, vClass, myActiveRoute, rid, true, ok);
138 168 : if (myMapMatchTAZ && ok) {
139 7 : myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
140 7 : myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
141 : }
142 60955 : } else if (attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
143 28 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_FROMLONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, myActiveRoute, rid, true, ok);
144 28 : if (myMapMatchTAZ && ok) {
145 0 : myVehicleParameter->fromTaz = myActiveRoute.back()->getID();
146 0 : myVehicleParameter->parametersSet |= VEHPARS_FROM_TAZ_SET;
147 : }
148 : } else {
149 121906 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
150 60927 : myActiveRoute, rid);
151 : }
152 64652 : if (!ok) {
153 14 : throw ProcessError();
154 : }
155 :
156 : // via-attributes
157 64638 : if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
158 55114 : myInsertStopEdgesAt = (int)myActiveRoute.size();
159 : }
160 : ConstMSEdgeVector viaEdges;
161 64638 : if (attrs.hasAttribute(SUMO_ATTR_VIAXY)) {
162 56 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIAXY, myVehicleParameter->id.c_str(), ok), false, vClass, viaEdges, rid, false, ok);
163 64582 : } else if (attrs.hasAttribute(SUMO_ATTR_VIALONLAT)) {
164 7 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_VIALONLAT, myVehicleParameter->id.c_str(), ok), true, vClass, viaEdges, rid, false, ok);
165 64575 : } else if (attrs.hasAttribute(SUMO_ATTR_VIAJUNCTIONS)) {
166 42 : for (std::string junctionID : attrs.get<std::vector<std::string> >(SUMO_ATTR_VIAJUNCTIONS, myVehicleParameter->id.c_str(), ok)) {
167 28 : const MSEdge* viaSink = MSEdge::dictionary(junctionID + "-sink");
168 28 : if (viaSink == nullptr) {
169 0 : throw ProcessError("Junction-taz '" + junctionID + "' not found." + JUNCTION_TAZ_MISSING_HELP);
170 : } else {
171 28 : viaEdges.push_back(viaSink);
172 : }
173 14 : }
174 : } else {
175 129116 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok),
176 : viaEdges, rid);
177 : }
178 64632 : if (!viaEdges.empty()) {
179 612 : myHaveVia = true;
180 : }
181 67930 : for (const MSEdge* e : viaEdges) {
182 3298 : myActiveRoute.push_back(e);
183 3298 : myVehicleParameter->via.push_back(e->getID());
184 : }
185 :
186 : // to-attributes
187 84255 : if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
188 36300 : (attrs.hasAttribute(SUMO_ATTR_TO_TAZ) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION))) {
189 4186 : const bool useJunction = attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION);
190 7132 : const std::string tazType = useJunction ? "junction" : "taz";
191 7132 : const std::string tazID = attrs.get<std::string>(useJunction ? SUMO_ATTR_TO_JUNCTION : SUMO_ATTR_TO_TAZ, myVehicleParameter->id.c_str(), ok, true);
192 4186 : const MSEdge* toTaz = MSEdge::dictionary(tazID + "-sink");
193 4186 : if (toTaz == nullptr) {
194 0 : throw ProcessError("Sink " + tazType + " '" + tazID + "' not known " + rid + "!"
195 0 : + (useJunction ? JUNCTION_TAZ_MISSING_HELP : ""));
196 4186 : } else if (toTaz->getNumPredecessors() == 0 && tag != SUMO_TAG_PERSON) {
197 0 : throw ProcessError("Sink " + tazType + " '" + tazID + "' has no incoming edges " + rid + "!");
198 : } else {
199 4186 : myActiveRoute.push_back(toTaz);
200 : }
201 60446 : } else if (attrs.hasAttribute(SUMO_ATTR_TOXY)) {
202 168 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOXY, myVehicleParameter->id.c_str(), ok, true), false, vClass, myActiveRoute, rid, false, ok);
203 168 : if (myMapMatchTAZ && ok) {
204 7 : myVehicleParameter->toTaz = myActiveRoute.back()->getID();
205 7 : myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
206 : }
207 60278 : } else if (attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
208 35 : parseGeoEdges(attrs.get<PositionVector>(SUMO_ATTR_TOLONLAT, myVehicleParameter->id.c_str(), ok, true), true, vClass, myActiveRoute, rid, false, ok);
209 35 : if (myMapMatchTAZ && ok) {
210 0 : myVehicleParameter->toTaz = myActiveRoute.back()->getID();
211 0 : myVehicleParameter->parametersSet |= VEHPARS_TO_TAZ_SET;
212 : }
213 : } else {
214 120486 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
215 60243 : myActiveRoute, rid);
216 : }
217 64632 : myActiveRouteID = "!" + myVehicleParameter->id;
218 64632 : if (myVehicleParameter->routeid == "") {
219 : myVehicleParameter->routeid = myActiveRouteID;
220 : }
221 129270 : }
222 :
223 :
224 : void
225 10753775 : MSRouteHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
226 : try {
227 48911 : if (myActiveTransportablePlan != nullptr && myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED
228 10754641 : && element != SUMO_TAG_RIDE && element != SUMO_TAG_TRANSPORT && element != SUMO_TAG_PARAM) {
229 0 : const std::string mode = myActiveType == ObjectTypeEnum::PERSON ? "ride" : "transport";
230 0 : throw ProcessError("Triggered departure for " + myActiveTypeName + " '" + myVehicleParameter->id + "' requires starting with a " + mode + ".");
231 : }
232 10753775 : if (myVehicleParameter == nullptr) {
233 10547162 : myActiveTypeName = toString((SumoXMLTag)element);
234 10547162 : myHaveVia = false;
235 : }
236 10753775 : SUMORouteHandler::myStartElement(element, attrs);
237 10753394 : switch (element) {
238 1649 : case SUMO_TAG_PERSONFLOW:
239 1649 : addTransportable(attrs, true);
240 : break;
241 289 : case SUMO_TAG_CONTAINERFLOW:
242 289 : addTransportable(attrs, false);
243 : break;
244 18805 : case SUMO_TAG_FLOW:
245 18805 : if (myVehicleParameter) {
246 18805 : parseFromViaTo((SumoXMLTag)element, attrs);
247 : }
248 : break;
249 37830 : case SUMO_TAG_TRIP:
250 37830 : parseFromViaTo((SumoXMLTag)element, attrs);
251 : break;
252 : default:
253 : break;
254 : }
255 426 : } catch (ProcessError&) {
256 426 : deleteActivePlanAndVehicleParameter();
257 426 : throw;
258 426 : }
259 10753349 : }
260 :
261 :
262 : void
263 467 : MSRouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
264 467 : bool ok = true;
265 467 : myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
266 467 : if (ok) {
267 467 : myCurrentVTypeDistribution = new RandomDistributor<MSVehicleType*>();
268 467 : if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
269 : std::vector<double> probs;
270 94 : if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
271 79 : StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentVTypeDistributionID.c_str(), ok));
272 249 : while (st.hasNext()) {
273 340 : probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
274 : }
275 79 : }
276 94 : const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
277 94 : StringTokenizer st(vTypes);
278 94 : int probIndex = 0;
279 294 : while (st.hasNext()) {
280 200 : const std::string& vtypeID = st.next();
281 200 : const RandomDistributor<MSVehicleType*>* const dist = MSNet::getInstance()->getVehicleControl().getVTypeDistribution(vtypeID);
282 200 : if (dist != nullptr) {
283 14 : const double distProb = ((int)probs.size() > probIndex ? probs[probIndex] : 1.) / dist->getOverallProb();
284 : std::vector<double>::const_iterator probIt = dist->getProbs().begin();
285 42 : for (MSVehicleType* const type : dist->getVals()) {
286 28 : myCurrentVTypeDistribution->add(type, distProb * *probIt);
287 : probIt++;
288 : }
289 : } else {
290 186 : MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(vtypeID, &myParsingRNG);
291 186 : if (type == nullptr) {
292 0 : throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
293 : }
294 186 : const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : type->getDefaultProbability());
295 186 : myCurrentVTypeDistribution->add(type, prob);
296 : }
297 200 : probIndex++;
298 : }
299 94 : if (probs.size() > 0 && probIndex != (int)probs.size()) {
300 0 : WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
301 : " types in vTypeDistribution '" + myCurrentVTypeDistributionID + "'");
302 : }
303 188 : }
304 : }
305 467 : }
306 :
307 :
308 : void
309 467 : MSRouteHandler::closeVehicleTypeDistribution() {
310 467 : if (myCurrentVTypeDistribution != nullptr) {
311 467 : if (MSGlobals::gStateLoaded && MSNet::getInstance()->getVehicleControl().hasVTypeDistribution(myCurrentVTypeDistributionID)) {
312 52 : delete myCurrentVTypeDistribution;
313 26 : return;
314 : }
315 441 : if (myCurrentVTypeDistribution->getOverallProb() == 0) {
316 0 : delete myCurrentVTypeDistribution;
317 0 : throw ProcessError(TLF("Vehicle type distribution '%' is empty.", myCurrentVTypeDistributionID));
318 : }
319 441 : if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
320 0 : delete myCurrentVTypeDistribution;
321 0 : throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", myCurrentVTypeDistributionID));
322 : }
323 441 : myCurrentVTypeDistribution = nullptr;
324 : }
325 : }
326 :
327 :
328 : void
329 153269 : MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
330 : myActiveRoute.clear();
331 153269 : myInsertStopEdgesAt = -1;
332 : // check whether the id is really necessary
333 : std::string rid;
334 153269 : if (myCurrentRouteDistribution != nullptr) {
335 6760 : myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
336 10140 : rid = "distribution '" + myCurrentRouteDistributionID + "'";
337 149889 : } else if (myVehicleParameter != nullptr) {
338 : // ok, a vehicle is wrapping the route,
339 : // we may use this vehicle's id as default
340 110242 : myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
341 110242 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
342 24 : WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
343 : }
344 : } else {
345 39647 : bool ok = true;
346 39647 : myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
347 39647 : if (!ok) {
348 0 : return;
349 : }
350 118941 : rid = "'" + myActiveRouteID + "'";
351 : }
352 153269 : if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
353 339588 : rid = "for vehicle '" + myVehicleParameter->id + "'";
354 : }
355 153269 : bool ok = true;
356 153269 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
357 306330 : MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
358 : }
359 306430 : myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
360 153271 : if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
361 42 : throw ProcessError(TLF("Invalid reference to route '%' in route %.", myActiveRouteRefID, rid));
362 : }
363 153201 : myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
364 153201 : myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
365 153201 : myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
366 153201 : myActiveRouteReplacedAtTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REPLACED_AT_TIME, myActiveRouteID.c_str(), ok, -1);
367 153201 : myActiveRouteReplacedIndex = attrs.getOpt<int>(SUMO_ATTR_REPLACED_ON_INDEX, myActiveRouteID.c_str(), ok, 0);
368 153201 : myActiveRoutePeriod = attrs.getOptSUMOTimeReporting(SUMO_ATTR_CYCLETIME, myActiveRouteID.c_str(), ok,
369 : // handle obsolete attribute name
370 : attrs.getOptSUMOTimeReporting(SUMO_ATTR_PERIOD, myActiveRouteID.c_str(), ok, 0));
371 153201 : myActiveRoutePermanent = attrs.getOpt<bool>(SUMO_ATTR_STATE, myActiveRouteID.c_str(), ok, false);
372 153201 : if (attrs.hasAttribute(SUMO_ATTR_PERIOD)) {
373 0 : WRITE_WARNING(TL("Attribute 'period' is deprecated for route. Use 'cycleTime' instead."));
374 : }
375 153201 : myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
376 153201 : if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
377 68 : WRITE_WARNING(TLF("Invalid cost for route '%'.", myActiveRouteID));
378 : }
379 : }
380 :
381 :
382 : void
383 10327 : MSRouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
384 : // Currently unused
385 10327 : }
386 :
387 :
388 : void
389 8478 : MSRouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
390 : // Currently unused
391 8478 : }
392 :
393 :
394 : void
395 37830 : MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
396 : // Currently unsued
397 37830 : }
398 :
399 :
400 : void
401 197438 : MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
402 197438 : std::string type = "vehicle";
403 197438 : if (mayBeDisconnected) {
404 44237 : if (myVehicleParameter->repetitionNumber >= 0) {
405 : type = "flow";
406 : } else {
407 : type = "trip";
408 : }
409 : }
410 :
411 : try {
412 197438 : const bool mustReroute = myActiveRoute.size() == 0 && myActiveRouteStops.size() != 0;
413 : if (mustReroute) {
414 : // implicit route from stops
415 105 : for (const SUMOVehicleParameter::Stop& stop : myActiveRouteStops) {
416 84 : myActiveRoute.push_back(MSEdge::dictionary(stop.edge));
417 : }
418 : }
419 197438 : if (myActiveRoute.size() == 0) {
420 590 : delete myActiveRouteColor;
421 590 : myActiveRouteColor = nullptr;
422 590 : if (myActiveRouteRefID != "" && myCurrentRouteDistribution != nullptr) {
423 42 : ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG);
424 42 : if (route != nullptr) {
425 84 : myCurrentRouteDistribution->add(route, myActiveRouteProbability);
426 : }
427 42 : myActiveRouteID = "";
428 : myActiveRouteRefID = "";
429 : return;
430 : }
431 548 : if (myVehicleParameter != nullptr) {
432 1084 : throw ProcessError("The route for " + type + " '" + myVehicleParameter->id + "' has no edges.");
433 : } else {
434 18 : throw ProcessError(TLF("Route '%' has no edges.", myActiveRouteID));
435 : }
436 : }
437 196848 : if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
438 144 : throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
439 : }
440 196776 : if (myActiveRouteRepeat > 0) {
441 : // duplicate route
442 818 : ConstMSEdgeVector tmpEdges = myActiveRoute;
443 818 : auto tmpStops = myActiveRouteStops;
444 :
445 818 : if (MSGlobals::gCheckRoutes) {
446 : SUMOVehicleClass vClass = SVC_IGNORING;
447 818 : std::string errSuffix = ".";
448 818 : if (myVehicleParameter != nullptr) {
449 658 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
450 658 : MSVehicleType* vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
451 658 : if (vtype != nullptr) {
452 : vClass = vtype->getVehicleClass();
453 2640 : errSuffix = TLF(" for vehicle '%' with vClass %.", myVehicleParameter->id, getVehicleClassNames(vClass));
454 : }
455 : }
456 818 : if (myActiveRoute.size() > 0 && !myActiveRoute.back()->isConnectedTo(*myActiveRoute.front(), vClass)) {
457 16 : if (tmpStops.size() == 0 || tmpStops.back().jump < 0) {
458 32 : throw ProcessError(TLF("Disconnected route '%' when repeating. Last edge '%' is not connected to first edge '%'%",
459 16 : myActiveRouteID, myActiveRoute.back()->getID(), myActiveRoute.front()->getID(), errSuffix));
460 : }
461 : }
462 : }
463 8709 : for (int i = 0; i < myActiveRouteRepeat; i++) {
464 7907 : myActiveRoute.insert(myActiveRoute.begin(), tmpEdges.begin(), tmpEdges.end());
465 8947 : for (SUMOVehicleParameter::Stop stop : tmpStops) {
466 1048 : if (stop.until > 0) {
467 544 : if (myActiveRoutePeriod <= 0) {
468 8 : const std::string description = myVehicleParameter != nullptr
469 16 : ? "for " + type + " '" + myVehicleParameter->id + "'"
470 24 : : "'" + myActiveRouteID + "'";
471 24 : throw ProcessError(TLF("Cannot repeat stops with 'until' in route % because no cycleTime is defined.", description));
472 : }
473 536 : stop.until += myActiveRoutePeriod * (i + 1);
474 : }
475 1040 : if (stop.arrival > 0) {
476 25 : if (myActiveRoutePeriod <= 0) {
477 0 : const std::string description = myVehicleParameter != nullptr
478 0 : ? "for " + type + " '" + myVehicleParameter->id + "'"
479 0 : : "'" + myActiveRouteID + "'";
480 0 : throw ProcessError(TLF("Cannot repeat stops with 'arrival' in route % because no cycleTime is defined.", description));
481 : }
482 25 : stop.arrival += myActiveRoutePeriod * (i + 1);
483 : }
484 1040 : stop.index = STOP_INDEX_REPEAT;
485 1040 : myActiveRouteStops.push_back(stop);
486 1048 : }
487 : }
488 802 : if (myActiveRouteStops.size() > 0) {
489 : // never jump on the last stop of a repeating route
490 92 : myActiveRouteStops.back().jump = -1;
491 : }
492 834 : }
493 196760 : MSRoute* const route = new MSRoute(myActiveRouteID, myActiveRoute,
494 393520 : myAmLoadingState ? myActiveRoutePermanent : myVehicleParameter == nullptr,
495 196760 : myActiveRouteColor, myActiveRouteStops,
496 196760 : myActiveRouteReplacedAtTime, myActiveRouteReplacedIndex);
497 196760 : route->setPeriod(myActiveRoutePeriod);
498 196760 : route->setCosts(myCurrentCosts);
499 : route->setReroute(mustReroute);
500 : myActiveRoute.clear();
501 : ConstMSRoutePtr constRoute = std::shared_ptr<const MSRoute>(route);
502 393520 : if (!MSRoute::dictionary(myActiveRouteID, constRoute)) {
503 142 : if (!MSGlobals::gStateLoaded) {
504 26 : if (myVehicleParameter != nullptr) {
505 20 : if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == nullptr) {
506 12 : throw ProcessError("Another route for " + type + " '" + myVehicleParameter->id + "' exists.");
507 : } else {
508 42 : throw ProcessError(TLF("A vehicle with id '%' already exists.", myVehicleParameter->id));
509 : }
510 : } else {
511 18 : throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myActiveRouteID));
512 : }
513 : }
514 : } else {
515 196618 : if (myCurrentRouteDistribution != nullptr) {
516 6666 : myCurrentRouteDistribution->add(constRoute, myActiveRouteProbability);
517 : }
518 : }
519 : myActiveRouteID = "";
520 196734 : myActiveRouteColor = nullptr;
521 : myActiveRouteStops.clear();
522 196734 : myActiveRouteRepeat = 0;
523 662 : } catch (ProcessError&) {
524 662 : deleteActivePlanAndVehicleParameter();
525 662 : throw;
526 662 : }
527 : }
528 :
529 :
530 : void
531 1273 : MSRouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
532 : // check whether the id is really necessary
533 1273 : if (myVehicleParameter != nullptr) {
534 : // ok, a vehicle is wrapping the route,
535 : // we may use this vehicle's id as default
536 1094 : myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
537 : // we have to record this or we cannot remove the distribution later
538 1094 : myVehicleParameter->routeid = myCurrentRouteDistributionID;
539 : } else {
540 179 : bool ok = true;
541 179 : myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
542 179 : if (!ok) {
543 0 : return;
544 : }
545 : }
546 1273 : myCurrentRouteDistribution = new RandomDistributor<ConstMSRoutePtr>();
547 : std::vector<double> probs;
548 1273 : if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
549 15 : bool ok = true;
550 15 : StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
551 45 : while (st.hasNext()) {
552 60 : probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
553 : }
554 15 : }
555 1273 : if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
556 22 : bool ok = true;
557 22 : StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
558 22 : int probIndex = 0;
559 66 : while (st.hasNext()) {
560 44 : std::string routeID = st.next();
561 44 : ConstMSRoutePtr route = MSRoute::dictionary(routeID, &myParsingRNG);
562 44 : if (route == nullptr) {
563 0 : throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
564 : }
565 44 : const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : 1.0);
566 44 : myCurrentRouteDistribution->add(route, prob, false);
567 44 : probIndex++;
568 : }
569 22 : if (probs.size() > 0 && probIndex != (int)probs.size()) {
570 0 : WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
571 : " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
572 : }
573 22 : }
574 1273 : }
575 :
576 :
577 : void
578 1259 : MSRouteHandler::closeRouteDistribution() {
579 1259 : if (myCurrentRouteDistribution != nullptr) {
580 1259 : const bool haveSameID = MSRoute::dictionary(myCurrentRouteDistributionID, &myParsingRNG) != nullptr;
581 1259 : if (MSGlobals::gStateLoaded && haveSameID) {
582 4 : delete myCurrentRouteDistribution;
583 2 : myCurrentRouteDistribution = nullptr;
584 2 : return;
585 : }
586 1247 : if (haveSameID) {
587 0 : delete myCurrentRouteDistribution;
588 0 : throw ProcessError(TLF("Another route (or distribution) with the id '%' exists.", myCurrentRouteDistributionID));
589 : }
590 1257 : if (myCurrentRouteDistribution->getOverallProb() == 0) {
591 0 : delete myCurrentRouteDistribution;
592 0 : throw ProcessError(TLF("Route distribution '%' is empty.", myCurrentRouteDistributionID));
593 : }
594 1257 : MSRoute::dictionary(myCurrentRouteDistributionID, myCurrentRouteDistribution, myVehicleParameter == nullptr);
595 1257 : myCurrentRouteDistribution = nullptr;
596 : }
597 : }
598 :
599 :
600 : void
601 296448 : MSRouteHandler::closeVehicle() {
602 : // get nested route
603 296448 : const std::string embeddedRouteID = "!" + myVehicleParameter->id;
604 : ConstMSRoutePtr route = nullptr;
605 296448 : if (myReplayRerouting) {
606 2832 : RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
607 2832 : if (rDist != nullptr && rDist->getVals().size() > 0) {
608 : route = rDist->getVals().front();
609 : }
610 : }
611 296448 : if (route == nullptr) {
612 590860 : route = MSRoute::dictionary(embeddedRouteID, &myParsingRNG);
613 : }
614 296448 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
615 296448 : if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN) {
616 : // let's check whether this vehicle had to depart before the simulation starts
617 1182638 : if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
618 : return;
619 : }
620 : }
621 :
622 : // get the vehicle's type
623 : MSVehicleType* vtype = nullptr;
624 :
625 : try {
626 295622 : if (myVehicleParameter->vtypeid != "") {
627 295622 : vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
628 295622 : if (vtype == nullptr) {
629 0 : throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
630 : }
631 295622 : if (vtype->getVehicleClass() == SVC_PEDESTRIAN) {
632 90 : WRITE_WARNINGF(TL("Vehicle type '%' with vClass=pedestrian should only be used for persons and not for vehicle '%'."), vtype->getID(), myVehicleParameter->id);
633 : }
634 : } else {
635 : // there should be one (at least the default one)
636 0 : vtype = vehControl.getVType(DEFAULT_VTYPE_ID, &myParsingRNG);
637 : }
638 295622 : if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
639 : // if the route id was given, prefer that one
640 152713 : if (route != nullptr && !myAmLoadingState) {
641 1554 : WRITE_WARNINGF(TL("Ignoring child element 'route' for vehicle '%' because attribute 'route' is set."), myVehicleParameter->id);
642 : }
643 305426 : route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
644 : }
645 295622 : if (route == nullptr) {
646 : // nothing found? -> error
647 340 : if (myVehicleParameter->routeid != "") {
648 28 : throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
649 : } else {
650 978 : throw ProcessError(TLF("Vehicle '%' has no route.", myVehicleParameter->id));
651 : }
652 : }
653 295282 : myActiveRouteID = "";
654 :
655 340 : } catch (ProcessError&) {
656 340 : deleteActivePlanAndVehicleParameter();
657 340 : throw;
658 340 : }
659 295282 : if (route->mustReroute()) {
660 14 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
661 14 : if (myVehicleParameter->stops.size() > 0) {
662 14 : route = addVehicleStopsToImplicitRoute(route, false);
663 : }
664 : }
665 295282 : if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
666 89 : if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
667 175 : myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
668 65 : myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
669 14 : throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid departEdge index "
670 28 : + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
671 : }
672 : }
673 295275 : if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
674 44 : if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
675 95 : myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
676 37 : myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
677 14 : throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
678 28 : + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
679 : }
680 : }
681 :
682 : // try to build the vehicle
683 : SUMOVehicle* vehicle = nullptr;
684 295268 : if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
685 295190 : MSVehicleControl::VehicleDefinitionSource source = (myAmLoadingState) ? MSVehicleControl::VehicleDefinitionSource::STATE : MSVehicleControl::VehicleDefinitionSource::ROUTEFILE;
686 : try {
687 295190 : vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
688 64 : } catch (const ProcessError& e) {
689 64 : myVehicleParameter = nullptr;
690 64 : if (!MSGlobals::gCheckRoutes) {
691 30 : WRITE_WARNING(e.what());
692 : vehControl.fixVehicleCounts();
693 : return;
694 : } else {
695 98 : throw e;
696 : }
697 64 : }
698 295126 : const SUMOTime origDepart = myVehicleParameter->depart;
699 : // maybe we do not want this vehicle to be inserted due to scaling
700 295126 : int quota = myAmLoadingState ? 1 : vehControl.getQuota(vehControl.getScale() * vtype->getParameter().scale);
701 290715 : if (quota > 0) {
702 294188 : registerLastDepart();
703 294188 : myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
704 294188 : vehControl.addVehicle(myVehicleParameter->id, vehicle);
705 294188 : if (myReplayRerouting) {
706 2832 : RandomDistributor<ConstMSRoutePtr>* rDist = MSRoute::distDictionary(embeddedRouteID);
707 2832 : if (rDist != nullptr) {
708 2809 : for (int i = 0; i < (int)rDist->getVals().size() - 1; i++) {
709 1791 : SUMOTime replacedAt = rDist->getVals()[i]->getReplacedTime();
710 1791 : auto* cmd = new Command_RouteReplacement(vehicle->getID(), rDist->getVals()[i + 1]);
711 1791 : if (i == 0 && replacedAt >= 0 && replacedAt == myVehicleParameter->depart) {
712 : // routing in the insertion step happens *after* insertion
713 12 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(cmd, replacedAt);
714 : } else {
715 1779 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(cmd, replacedAt);
716 : }
717 : }
718 : }
719 : }
720 : int offset = 0;
721 294360 : for (int i = 1; i < quota; i++) {
722 172 : if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
723 156 : MSNet::getInstance()->getInsertionControl().add(vehicle);
724 : }
725 172 : SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*myVehicleParameter);
726 344 : newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
727 178 : while (vehControl.getVehicle(newPars->id) != nullptr) {
728 6 : offset += 1;
729 12 : newPars->id = myVehicleParameter->id + myScaleSuffix + toString(i + offset);
730 : }
731 172 : newPars->depart = origDepart + MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
732 172 : if (vehControl.hasVTypeDistribution(myVehicleParameter->vtypeid)) {
733 : // resample type
734 27 : vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
735 : }
736 587 : vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes, source, !myAmLoadingState);
737 172 : vehControl.addVehicle(newPars->id, vehicle);
738 : }
739 294188 : myVehicleParameter = nullptr;
740 : } else {
741 938 : vehControl.deleteVehicle(vehicle, true);
742 938 : myVehicleParameter = nullptr;
743 : vehicle = nullptr;
744 : }
745 : } else {
746 : // strange: another vehicle with the same id already exists
747 78 : if (!MSGlobals::gStateLoaded) {
748 : // and was not loaded while loading a simulation state
749 : // -> error
750 12 : std::string veh_id = myVehicleParameter->id;
751 12 : deleteActivePlanAndVehicleParameter();
752 24 : std::string scaleWarning = "";
753 18 : if (vehControl.getScale() * vtype->getParameter().scale > 1 && veh_id.find(myScaleSuffix) != std::string::npos) {
754 : scaleWarning = "\n (Possibly duplicate id due to using option --scale. Set option --scale-suffix to prevent this)";
755 : }
756 24 : throw ProcessError("Another vehicle with the id '" + veh_id + "' exists." + scaleWarning);
757 : } else {
758 : // ok, it seems to be loaded previously while loading a simulation state
759 : vehicle = nullptr;
760 : }
761 : }
762 : // check whether the vehicle shall be added directly to the network or
763 : // shall stay in the internal buffer
764 294188 : if (vehicle != nullptr) {
765 294188 : if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
766 293613 : MSNet::getInstance()->getInsertionControl().add(vehicle);
767 : }
768 : }
769 : }
770 :
771 :
772 : ConstMSRoutePtr
773 14 : MSRouteHandler::addVehicleStopsToImplicitRoute(ConstMSRoutePtr route, bool isPermanent) {
774 : // the route was defined without edges and its current edges were
775 : // derived from route-stops.
776 : // We may need to add additional edges for the vehicle-stops
777 : assert(myVehicleParameter->wasSet(VEHPARS_ROUTE_SET));
778 : assert(route->getStops().size() > 0);
779 14 : ConstMSEdgeVector edges = route->getEdges();
780 42 : for (SUMOVehicleParameter::Stop stop : myVehicleParameter->stops) {
781 28 : MSEdge* stopEdge = MSEdge::dictionary(stop.edge);
782 28 : if (stop.index == 0) {
783 14 : if (edges.front() != stopEdge ||
784 0 : route->getStops().front().endPos < stop.endPos) {
785 14 : edges.insert(edges.begin(), stopEdge);
786 : }
787 14 : } else if (stop.index == STOP_INDEX_END) {
788 14 : if (edges.back() != stopEdge ||
789 0 : route->getStops().back().endPos > stop.endPos) {
790 14 : edges.push_back(stopEdge);
791 : }
792 : } else {
793 0 : WRITE_WARNINGF(TL("Could not merge vehicle stops for vehicle '%' into implicitly defined route '%'"), myVehicleParameter->id, route->getID());
794 : }
795 28 : }
796 14 : ConstMSRoutePtr newRoute = std::make_shared<MSRoute>("!" + myVehicleParameter->id, edges,
797 28 : isPermanent, new RGBColor(route->getColor()), route->getStops());
798 28 : if (!MSRoute::dictionary(newRoute->getID(), newRoute)) {
799 0 : throw ProcessError("Could not adapt implicit route for " + std::string(isPermanent ? "flow" : "vehicle") + " '" + myVehicleParameter->id + "'");
800 : }
801 14 : return newRoute;
802 14 : }
803 :
804 :
805 : void
806 40719 : MSRouteHandler::closeTransportable() {
807 : try {
808 40719 : if (myActiveTransportablePlan->size() == 0) {
809 12 : std::string error = myActiveTypeName + " '" + myVehicleParameter->id + "' has no plan.";
810 6 : error[0] = (char)::toupper((char)error[0]);
811 12 : throw ProcessError(error);
812 : }
813 : // let's check whether this transportable had to depart before the simulation starts
814 16815 : if (!(myAddVehiclesDirectly || checkLastDepart())
815 179657 : || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
816 10 : deleteActivePlanAndVehicleParameter();
817 : return;
818 : }
819 : // type existence has been checked on opening
820 40703 : MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
821 40703 : if (myActiveType == ObjectTypeEnum::PERSON
822 39948 : && type->getVehicleClass() != SVC_PEDESTRIAN
823 40723 : && !type->getParameter().wasSet(VTYPEPARS_VEHICLECLASS_SET)) {
824 48 : WRITE_WARNINGF(TL("Person '%' receives type '%' which implicitly uses unsuitable vClass '%'."), myVehicleParameter->id, type->getID(), toString(type->getVehicleClass()));
825 : }
826 40703 : int created = addFlowTransportable(myVehicleParameter->depart, type, myVehicleParameter->id, -1);
827 40675 : registerLastDepart();
828 40675 : if (created > 0) {
829 40651 : resetActivePlanAndVehicleParameter();
830 : } else {
831 24 : deleteActivePlanAndVehicleParameter();
832 : }
833 34 : } catch (ProcessError&) {
834 34 : deleteActivePlanAndVehicleParameter();
835 34 : throw;
836 34 : }
837 : }
838 :
839 :
840 : void
841 39964 : MSRouteHandler::closePerson() {
842 39964 : closeTransportable();
843 39938 : }
844 :
845 :
846 : void
847 755 : MSRouteHandler::closeContainer() {
848 755 : closeTransportable();
849 747 : }
850 :
851 :
852 : void
853 1647 : MSRouteHandler::closePersonFlow() {
854 1647 : closeTransportableFlow();
855 1638 : }
856 :
857 :
858 : void
859 287 : MSRouteHandler::closeContainerFlow() {
860 287 : closeTransportableFlow();
861 287 : }
862 :
863 :
864 : void
865 1934 : MSRouteHandler::closeTransportableFlow() {
866 : try {
867 1934 : const std::string fid = myVehicleParameter->id;
868 1934 : if (myActiveTransportablePlan->size() == 0) {
869 0 : throw ProcessError(myActiveTypeName + "Flow '" + fid + "' has no plan.");
870 : }
871 : // let's check whether this transportable (person/container) had to depart before the simulation starts
872 1934 : if (!(myAddVehiclesDirectly || checkLastDepart())
873 9679 : || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
874 0 : deleteActivePlanAndVehicleParameter();
875 : return;
876 : }
877 : // instantiate all persons/containers of this flow
878 : int i = 0;
879 1934 : registerLastDepart();
880 1934 : std::string baseID = myVehicleParameter->id;
881 1934 : if (myVehicleParameter->repetitionProbability > 0) {
882 206 : if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
883 0 : throw ProcessError("probabilistic " + myActiveTypeName + "Flow '" + fid + "' must specify end time");
884 : } else {
885 440709 : for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
886 440503 : if (RandHelper::rand(&myParsingRNG) < myVehicleParameter->repetitionProbability) {
887 : // type existence has been checked on opening
888 32993 : MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
889 32993 : addFlowTransportable(t, type, baseID, i++);
890 : }
891 : }
892 : }
893 : } else {
894 1728 : SUMOTime depart = myVehicleParameter->depart;
895 1728 : if (myVehicleParameter->repetitionOffset < 0) {
896 : // poisson: randomize first depart
897 64 : myVehicleParameter->incrementFlow(1, &myParsingRNG);
898 : }
899 429587 : for (; i < myVehicleParameter->repetitionNumber && (myVehicleParameter->repetitionNumber != std::numeric_limits<int>::max()
900 78581 : || depart + myVehicleParameter->repetitionTotalOffset <= myVehicleParameter->repetitionEnd); i++) {
901 : // type existence has been checked on opening
902 427868 : MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
903 427868 : addFlowTransportable(depart + myVehicleParameter->repetitionTotalOffset, type, baseID, i);
904 427859 : myVehicleParameter->incrementFlow(1, &myParsingRNG);
905 : }
906 : }
907 1925 : resetActivePlanAndVehicleParameter();
908 9 : } catch (ProcessError&) {
909 9 : deleteActivePlanAndVehicleParameter();
910 9 : throw;
911 9 : }
912 1925 : myStartTriggeredInFlow = false;
913 : }
914 :
915 :
916 : int
917 501564 : MSRouteHandler::addFlowTransportable(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
918 : try {
919 : int numCreated = 0;
920 501564 : MSNet* const net = MSNet::getInstance();
921 501564 : MSTransportableControl& tc = myActiveType == ObjectTypeEnum::PERSON ? net->getPersonControl() : net->getContainerControl();
922 501543 : const MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
923 : //MSTransportableControl& pc = net->getPersonControl();
924 501543 : const int quota = vc.getQuota(vc.getScale() * type->getParameter().scale, tc.getLoadedNumber());
925 501543 : if (quota == 0) {
926 : tc.addDiscarded();
927 : }
928 1003254 : for (int j = 0; j < quota; j++) {
929 501727 : if (i > 0 || j > 0) {
930 : // copy parameter and plan because the transportable takes over responsibility
931 459135 : SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
932 459135 : *copyParam = *myVehicleParameter;
933 459135 : myVehicleParameter = copyParam;
934 459135 : MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
935 : MSStage* lastStage = nullptr;
936 1424763 : for (MSStage* const s : *myActiveTransportablePlan) {
937 965628 : copyPlan->push_back(s->clone());
938 965628 : if (lastStage != nullptr && s->getStageType() == MSStageType::WALKING && lastStage->getDestinationStop() != nullptr) {
939 0 : MSStageMoving* walk = static_cast<MSStageMoving*>(copyPlan->back());
940 0 : walk->setDepartPos(lastStage->getDestinationStop()->getAccessPos(walk->getEdge(), &myParsingRNG));
941 : }
942 : lastStage = s;
943 : }
944 459135 : myActiveTransportablePlan = copyPlan;
945 459135 : if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
946 198 : const double initialDepartPos = RandHelper::rand(myActiveTransportablePlan->front()->getDestination()->getLength(), &myParsingRNG);
947 198 : myActiveTransportablePlan->front()->setArrivalPos(initialDepartPos);
948 : }
949 : }
950 501727 : myVehicleParameter->id = (baseID
951 1925350 : + (i >= 0 ? "." + toString(i) : "")
952 1505421 : + (j > 0 ? "." + toString(j) : ""));
953 501727 : myVehicleParameter->depart = depart += net->getInsertionControl().computeRandomDepartOffset();
954 501727 : MSTransportable* transportable = myActiveType == ObjectTypeEnum::PERSON ?
955 305838 : tc.buildPerson(myVehicleParameter, type, myActiveTransportablePlan, &myParsingRNG) :
956 195889 : tc.buildContainer(myVehicleParameter, type, myActiveTransportablePlan);
957 501727 : numCreated++;
958 501727 : if (!tc.add(transportable)) {
959 16 : std::string error = "Another " + myActiveTypeName + " with the id '" + myVehicleParameter->id + "' exists.";
960 16 : delete transportable;
961 16 : resetActivePlanAndVehicleParameter();
962 16 : if (!MSGlobals::gStateLoaded) {
963 32 : throw ProcessError(error);
964 : }
965 311254 : } else if ((net->hasPersons() && net->getPersonControl().get(myVehicleParameter->id) != nullptr)
966 807549 : && (net->hasContainers() && net->getContainerControl().get(myVehicleParameter->id) != nullptr)) {
967 48 : WRITE_WARNINGF(TL("There exists a person and a container with the same id '%'. Starting with SUMO 1.9.0 this is an error."), myVehicleParameter->id);
968 : }
969 : }
970 501527 : return numCreated;
971 37 : } catch (ProcessError&) {
972 37 : deleteActivePlanAndVehicleParameter();
973 37 : throw;
974 37 : }
975 : }
976 :
977 :
978 : void
979 58543 : MSRouteHandler::closeVType() {
980 58543 : MSVehicleType* vehType = MSVehicleType::build(*myCurrentVType);
981 58539 : vehType->check();
982 58539 : if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
983 : const std::string id = vehType->getID();
984 177 : delete vehType;
985 177 : if (!MSGlobals::gStateLoaded) {
986 60 : throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", id));
987 : }
988 : } else {
989 58362 : if (myCurrentVTypeDistribution != nullptr) {
990 1012 : myCurrentVTypeDistribution->add(vehType, vehType->getDefaultProbability());
991 : }
992 : }
993 58519 : }
994 :
995 :
996 : void
997 18803 : MSRouteHandler::closeFlow() {
998 18803 : myInsertStopEdgesAt = -1;
999 18803 : if (myVehicleParameter->repetitionNumber == 0) {
1000 0 : delete myVehicleParameter;
1001 0 : myVehicleParameter = nullptr;
1002 13 : return;
1003 : }
1004 : // let's check whether vehicles had to depart before the simulation starts
1005 18803 : myVehicleParameter->repetitionsDone = 0;
1006 18803 : if (myVehicleParameter->repetitionProbability < 0) {
1007 15040 : const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
1008 15717 : while (myVehicleParameter->repetitionTotalOffset < offsetToBegin) {
1009 690 : myVehicleParameter->incrementFlow(1, &myParsingRNG);
1010 690 : if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
1011 13 : delete myVehicleParameter;
1012 13 : myVehicleParameter = nullptr;
1013 13 : return;
1014 : }
1015 : }
1016 : }
1017 18790 : if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG) == nullptr) {
1018 0 : throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
1019 : }
1020 29106 : if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
1021 6712 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1022 6712 : closeRoute(true);
1023 : }
1024 18774 : ConstMSRoutePtr route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
1025 18774 : if (route == nullptr) {
1026 0 : throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
1027 : }
1028 18774 : if (route->mustReroute()) {
1029 14 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1030 14 : if (myVehicleParameter->stops.size() > 0) {
1031 21 : route = addVehicleStopsToImplicitRoute(route, true);
1032 7 : myVehicleParameter->routeid = route->getID();
1033 : }
1034 : }
1035 18774 : if (myVehicleParameter->departEdgeProcedure != RouteIndexDefinition::DEFAULT) {
1036 20 : if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
1037 43 : myVehicleParameter->departEdgeProcedure == RouteIndexDefinition::GIVEN &&
1038 9 : myVehicleParameter->departEdge >= (int)route->getEdges().size()) {
1039 14 : throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid departEdge index "
1040 28 : + toString(myVehicleParameter->departEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
1041 : }
1042 : }
1043 18767 : if (myVehicleParameter->arrivalEdgeProcedure != RouteIndexDefinition::DEFAULT) {
1044 9 : if ((myVehicleParameter->parametersSet & VEHPARS_FORCE_REROUTE) == 0 &&
1045 18 : myVehicleParameter->arrivalEdgeProcedure == RouteIndexDefinition::GIVEN &&
1046 2 : myVehicleParameter->arrivalEdge >= (int)route->getEdges().size()) {
1047 0 : throw ProcessError("Flow '" + myVehicleParameter->id + "' has invalid arrivalEdge index "
1048 0 : + toString(myVehicleParameter->arrivalEdge) + " for route with " + toString(route->getEdges().size()) + " edges.");
1049 : }
1050 : }
1051 18767 : myActiveRouteID = "";
1052 :
1053 : // check whether the vehicle shall be added directly to the network or
1054 : // shall stay in the internal buffer
1055 18767 : if (myAddVehiclesDirectly || checkLastDepart()) {
1056 18767 : if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
1057 18733 : registerLastDepart();
1058 : } else {
1059 34 : if (MSGlobals::gStateLoaded) {
1060 34 : delete myVehicleParameter;
1061 : } else {
1062 0 : throw ProcessError(TLF("Another flow with the id '%' exists.", myVehicleParameter->id));
1063 : }
1064 : }
1065 : }
1066 18767 : myVehicleParameter = nullptr;
1067 : }
1068 :
1069 :
1070 : void
1071 37525 : MSRouteHandler::closeTrip() {
1072 37525 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1073 37525 : closeRoute(true);
1074 36933 : closeVehicle();
1075 36920 : }
1076 :
1077 : void
1078 4735 : MSRouteHandler::addRide(const SUMOSAXAttributes& attrs) {
1079 4735 : addRideOrTransport(attrs, SUMO_TAG_RIDE);
1080 4703 : }
1081 :
1082 : void
1083 846 : MSRouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
1084 846 : addRideOrTransport(attrs, SUMO_TAG_TRANSPORT);
1085 836 : }
1086 :
1087 : void
1088 5581 : MSRouteHandler::addRideOrTransport(const SUMOSAXAttributes& attrs, const SumoXMLTag modeTag) {
1089 5581 : if (myVehicleParameter == nullptr) {
1090 0 : throw ProcessError(TLF("Cannot define % stage without %.", toString(modeTag), toString(modeTag)));
1091 : }
1092 : try {
1093 6427 : const std::string mode = modeTag == SUMO_TAG_RIDE ? "ride" : "transport";
1094 5623 : std::string agent = "person";
1095 5623 : std::string stop = "bus stop";
1096 5581 : if (myActiveType == ObjectTypeEnum::CONTAINER) {
1097 : agent = "container";
1098 : stop = "container stop";
1099 : }
1100 :
1101 5581 : if (!((myActiveType == ObjectTypeEnum::PERSON && modeTag == SUMO_TAG_RIDE) ||
1102 846 : (myActiveType == ObjectTypeEnum::CONTAINER && modeTag == SUMO_TAG_TRANSPORT))) {
1103 24 : throw ProcessError("Found " + mode + " inside " + agent + " element");
1104 : }
1105 5569 : const std::string aid = myVehicleParameter->id;
1106 5569 : bool ok = true;
1107 : const MSEdge* from = nullptr;
1108 11168 : const std::string desc = attrs.getOpt<std::string>(SUMO_ATTR_LINES, aid.c_str(), ok, "ANY");
1109 5599 : StringTokenizer st(desc);
1110 11138 : MSStoppingPlace* s = retrieveStoppingPlace(attrs, " in " + agent + " '" + aid + "'");
1111 : MSEdge* to = nullptr;
1112 5569 : if (s != nullptr) {
1113 1114 : to = &s->getLane().getEdge();
1114 : }
1115 5569 : double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, aid.c_str(), ok,
1116 1114 : s == nullptr ? std::numeric_limits<double>::infinity() : s->getEndLanePosition());
1117 :
1118 : const SUMOVehicleParameter* startVeh = nullptr;
1119 : const MSEdge* startVehFrom = nullptr;
1120 5569 : if (myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED) {
1121 846 : if (st.size() != 1) {
1122 0 : throw ProcessError("Triggered departure for " + agent + " '" + aid + "' requires a unique lines value.");
1123 : }
1124 : // agent starts
1125 846 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
1126 846 : const std::string vehID = st.front();
1127 846 : SUMOVehicle* sVeh = vehControl.getVehicle(vehID);
1128 846 : if (sVeh == nullptr) {
1129 40 : if (MSNet::getInstance()->hasFlow(vehID)) {
1130 32 : startVeh = MSNet::getInstance()->getInsertionControl().getFlowPars(vehID);
1131 32 : if (startVeh != nullptr) {
1132 32 : ConstMSRoutePtr const route = MSRoute::dictionary(startVeh->routeid);
1133 32 : startVehFrom = route->getEdges().front();
1134 : // flows are inserted at the end of the time step so we
1135 : // do delay the pedestrian event by one time step
1136 32 : myVehicleParameter->depart = startVeh->depart + DELTA_T;
1137 32 : myStartTriggeredInFlow = true;
1138 : }
1139 : }
1140 : } else {
1141 806 : startVeh = &sVeh->getParameter();
1142 806 : startVehFrom = sVeh->getRoute().getEdges().front();
1143 806 : myVehicleParameter->depart = startVeh->depart;
1144 : }
1145 : if (startVeh == nullptr) {
1146 16 : throw ProcessError("Unknown vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
1147 : }
1148 838 : if (startVeh->departProcedure == DepartDefinition::TRIGGERED) {
1149 0 : throw ProcessError("Cannot use triggered vehicle '" + vehID + "' in triggered departure for " + agent + " '" + aid + "'.");
1150 : }
1151 : }
1152 :
1153 5561 : if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
1154 3803 : const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, aid.c_str(), ok);
1155 3803 : from = MSEdge::dictionary(fromID);
1156 3803 : if (from == nullptr) {
1157 0 : throw ProcessError("The from edge '" + fromID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
1158 : }
1159 3803 : if (!myActiveTransportablePlan->empty() && myActiveTransportablePlan->back()->getDestination() != from) {
1160 54 : const bool stopWithAccess = (myActiveTransportablePlan->back()->getDestinationStop() != nullptr
1161 54 : && &myActiveTransportablePlan->back()->getDestinationStop()->getLane().getEdge() == from);
1162 54 : const bool transferAtJunction = (from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getFromJunction()
1163 54 : || from->getFromJunction() == myActiveTransportablePlan->back()->getDestination()->getToJunction());
1164 54 : if (!(stopWithAccess || transferAtJunction)) {
1165 14 : throw ProcessError("Disconnected plan for " + agent + " '" + aid +
1166 28 : "' (edge '" + fromID + "' != edge '" + myActiveTransportablePlan->back()->getDestination()->getID() + "').");
1167 : }
1168 : }
1169 3789 : if (startVeh != nullptr && startVehFrom != from) {
1170 8 : throw ProcessError("Disconnected plan for triggered " + agent + " '" + aid +
1171 16 : "' (edge '" + fromID + "' != edge '" + startVehFrom->getID() + "').");
1172 : }
1173 1758 : } else if (startVeh != nullptr) {
1174 : from = startVehFrom;
1175 : }
1176 5539 : if (myActiveTransportablePlan->empty()) {
1177 3512 : if (from == nullptr) {
1178 0 : throw ProcessError("The start edge for " + agent + " '" + aid + "' is not known.");
1179 : } else {
1180 3512 : myActiveTransportablePlan->push_back(new MSStageWaiting(
1181 7024 : from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
1182 : }
1183 : }
1184 : // given attribute may override given stopping place due access requirements
1185 5539 : if (to == nullptr || attrs.hasAttribute(SUMO_ATTR_TO)) {
1186 4501 : const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, aid.c_str(), ok);
1187 4501 : to = MSEdge::dictionary(toID);
1188 4501 : if (to == nullptr) {
1189 0 : throw ProcessError("The to edge '" + toID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
1190 : }
1191 : }
1192 5539 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, aid.c_str(), ok, OptionsCont::getOptions().getString("persontrip.default.group"));
1193 5539 : const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
1194 5539 : const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
1195 11078 : arrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, to->getLength(), SUMO_ATTR_ARRIVALPOS, agent + " '" + aid + "' takes a " + mode + " to edge '" + to->getID() + "'");
1196 5539 : myActiveTransportablePlan->push_back(new MSStageDriving(from, to, s, arrivalPos, 0.0, st.getVector(), group, intendedVeh, intendedDepart));
1197 5539 : myParamStack.push_back(myActiveTransportablePlan->back());
1198 5611 : } catch (ProcessError&) {
1199 42 : deleteActivePlanAndVehicleParameter();
1200 42 : throw;
1201 42 : }
1202 5539 : }
1203 :
1204 : MSStoppingPlace*
1205 81844 : MSRouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, SUMOVehicleParameter::Stop* stopParam) {
1206 81844 : bool ok = true;
1207 : // dummy stop parameter to hold the attributes
1208 81844 : SUMOVehicleParameter::Stop stop;
1209 81844 : if (stopParam != nullptr) {
1210 35280 : stop = *stopParam;
1211 : } else {
1212 93128 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1213 46564 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
1214 46564 : stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
1215 46564 : stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
1216 46564 : stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
1217 93128 : stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
1218 : }
1219 : MSStoppingPlace* toStop = nullptr;
1220 81844 : if (stop.busstop != "") {
1221 10925 : toStop = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
1222 10925 : if (toStop == nullptr) {
1223 12 : ok = false;
1224 36 : WRITE_ERROR(TLF("The busStop '%' is not known%.", stop.busstop, errorSuffix));
1225 : }
1226 70919 : } else if (stop.containerstop != "") {
1227 1189 : toStop = MSNet::getInstance()->getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
1228 1189 : if (toStop == nullptr) {
1229 0 : ok = false;
1230 0 : WRITE_ERROR(TLF("The containerStop '%' is not known%.", stop.containerstop, errorSuffix));
1231 : }
1232 69730 : } else if (stop.parkingarea != "") {
1233 9139 : toStop = MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
1234 9139 : if (toStop == nullptr) {
1235 0 : ok = false;
1236 0 : WRITE_ERROR(TLF("The parkingArea '%' is not known%.", stop.parkingarea, errorSuffix));
1237 : }
1238 60591 : } else if (stop.chargingStation != "") {
1239 : // ok, we have a charging station
1240 1578 : toStop = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
1241 1578 : if (toStop == nullptr) {
1242 0 : ok = false;
1243 0 : WRITE_ERROR(TLF("The chargingStation '%' is not known%.", stop.chargingStation, errorSuffix));
1244 : }
1245 59013 : } else if (stop.overheadWireSegment != "") {
1246 : // ok, we have an overhead wire segment
1247 0 : toStop = MSNet::getInstance()->getStoppingPlace(stop.overheadWireSegment, SUMO_TAG_OVERHEAD_WIRE_SEGMENT);
1248 0 : if (toStop == nullptr) {
1249 0 : ok = false;
1250 0 : WRITE_ERROR(TLF("The overhead wire segment '%' is not known%.", stop.overheadWireSegment, errorSuffix));
1251 : }
1252 : }
1253 81844 : if (!ok && MSGlobals::gCheckRoutes) {
1254 18 : throw ProcessError(TLF("Invalid stop definition%.", errorSuffix));
1255 : }
1256 81838 : return toStop;
1257 81844 : }
1258 :
1259 : Parameterised*
1260 35307 : MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
1261 : Parameterised* result = nullptr;
1262 : try {
1263 : std::string errorSuffix;
1264 35307 : if (myActiveType == ObjectTypeEnum::PERSON) {
1265 3483 : errorSuffix = " in person '" + myVehicleParameter->id + "'";
1266 34146 : } else if (myActiveType == ObjectTypeEnum::CONTAINER) {
1267 600 : errorSuffix = " in container '" + myVehicleParameter->id + "'";
1268 33946 : } else if (myVehicleParameter != nullptr) {
1269 92433 : errorSuffix = " in vehicle '" + myVehicleParameter->id + "'";
1270 : } else {
1271 9515 : errorSuffix = " in route '" + myActiveRouteID + "'";
1272 : }
1273 35307 : SUMOVehicleParameter::Stop stop;
1274 70614 : bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
1275 35307 : if (!ok) {
1276 27 : if (MSGlobals::gCheckRoutes) {
1277 27 : throw ProcessError();
1278 : }
1279 : return result;
1280 : }
1281 35280 : const MSEdge* edge = nullptr;
1282 35280 : MSStoppingPlace* toStop = retrieveStoppingPlace(attrs, errorSuffix, &stop);
1283 : // if one of the previous stops is defined
1284 35274 : if (toStop != nullptr) {
1285 18224 : const MSLane& l = toStop->getLane();
1286 : stop.lane = l.getID();
1287 18224 : if ((stop.parametersSet & STOP_END_SET) == 0) {
1288 18208 : stop.endPos = toStop->getEndLanePosition();
1289 : } else {
1290 16 : stop.endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
1291 : }
1292 18224 : stop.startPos = toStop->getBeginLanePosition();
1293 18224 : edge = &l.getEdge();
1294 : } else {
1295 : // no, the lane and the position should be given directly
1296 : // get the lane
1297 17050 : stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
1298 17160 : stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
1299 17050 : if (ok && stop.edge != "") { // edge is given directly
1300 1273 : edge = MSEdge::dictionary(stop.edge);
1301 1273 : if (edge == nullptr || (edge->isInternal() && !MSGlobals::gUsingInternalLanes)) {
1302 18 : throw ProcessError(TLF("The edge '%' for a stop is not known%.", stop.edge, errorSuffix));
1303 : }
1304 15777 : } else if (ok && stop.lane != "") { // lane is given directly
1305 15711 : MSLane* stopLane = MSLane::dictionary(stop.lane);
1306 15711 : if (stopLane == nullptr) {
1307 : // check for opposite-direction stop
1308 153 : stopLane = MSBaseVehicle::interpretOppositeStop(stop);
1309 153 : if (stopLane != nullptr) {
1310 145 : edge = MSEdge::dictionary(stop.edge);
1311 : }
1312 : } else {
1313 15558 : edge = &stopLane->getEdge();
1314 : }
1315 15703 : if (stopLane == nullptr || (stopLane->isInternal() && !MSGlobals::gUsingInternalLanes)) {
1316 150 : throw ProcessError(TLF("The lane '%' for a stop is not known%.", stop.lane, errorSuffix));
1317 : }
1318 66 : } else if (ok && ((attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y))
1319 31 : || (attrs.hasAttribute(SUMO_ATTR_LON) && attrs.hasAttribute(SUMO_ATTR_LAT)))) {
1320 : Position pos;
1321 : bool geo = false;
1322 49 : if (attrs.hasAttribute(SUMO_ATTR_X) && attrs.hasAttribute(SUMO_ATTR_Y)) {
1323 35 : pos = Position(attrs.get<double>(SUMO_ATTR_X, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_Y, myVehicleParameter->id.c_str(), ok));
1324 : } else {
1325 14 : pos = Position(attrs.get<double>(SUMO_ATTR_LON, myVehicleParameter->id.c_str(), ok), attrs.get<double>(SUMO_ATTR_LAT, myVehicleParameter->id.c_str(), ok));
1326 : geo = true;
1327 : }
1328 49 : PositionVector positions;
1329 49 : positions.push_back(pos);
1330 : ConstMSEdgeVector geoEdges;
1331 : SUMOVehicleClass vClass = SVC_PASSENGER;
1332 49 : auto& vc = MSNet::getInstance()->getVehicleControl();
1333 49 : if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
1334 49 : MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
1335 49 : if (type != nullptr) {
1336 49 : vClass = type->getParameter().vehicleClass;
1337 : }
1338 : }
1339 49 : parseGeoEdges(positions, geo, vClass, geoEdges, myVehicleParameter->id, true, ok, true);
1340 49 : if (ok) {
1341 42 : edge = geoEdges.front();
1342 42 : if (geo) {
1343 14 : GeoConvHelper::getFinal().x2cartesian_const(pos);
1344 : }
1345 42 : stop.parametersSet |= STOP_END_SET;
1346 42 : stop.endPos = edge->getLanes()[0]->getShape().nearest_offset_to_point2D(pos, false);
1347 : } else {
1348 21 : throw ProcessError(TLF("Could not map stop position '%' to the network%.", pos, errorSuffix));
1349 : }
1350 56 : } else {
1351 17 : if (myActiveTransportablePlan && !myActiveTransportablePlan->empty()) { // use end of movement before
1352 11 : toStop = myActiveTransportablePlan->back()->getDestinationStop();
1353 11 : if (toStop != nullptr) { // use end of movement before definied as a stopping place
1354 0 : edge = &toStop->getLane().getEdge();
1355 0 : stop.lane = toStop->getLane().getID();
1356 0 : stop.endPos = toStop->getEndLanePosition();
1357 0 : stop.startPos = toStop->getBeginLanePosition();
1358 : } else { // use end of movement before definied as lane/edge
1359 11 : edge = myActiveTransportablePlan->back()->getDestination();
1360 11 : stop.lane = edge->getLanes()[0]->getID();
1361 11 : stop.endPos = myActiveTransportablePlan->back()->getArrivalPos();
1362 22 : stop.startPos = MAX2(0., stop.endPos - MIN_STOP_LENGTH);
1363 : }
1364 : } else {
1365 12 : const std::string msg = TLF("A stop must be placed on a busStop, a chargingStation, an overheadWireSegment, a containerStop, a parkingArea, an edge or a lane%.", errorSuffix);
1366 6 : if (MSGlobals::gCheckRoutes) {
1367 0 : throw ProcessError(msg);
1368 : } else {
1369 18 : WRITE_WARNING(msg);
1370 : return result;
1371 : }
1372 : }
1373 : }
1374 16981 : stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
1375 16981 : if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
1376 0 : WRITE_WARNINGF(TL("Deprecated attribute 'pos' in description of stop%."), errorSuffix);
1377 0 : stop.endPos = attrs.getOpt<double>(SUMO_ATTR_POSITION, nullptr, ok, stop.endPos);
1378 : }
1379 33342 : stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - MIN_STOP_LENGTH));
1380 16981 : if (!myAmLoadingState) {
1381 24617 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS))
1382 16744 : || !MSGlobals::gCheckRoutes;
1383 16744 : if (!ok || (checkStopPos(stop.startPos, stop.endPos, edge->getLength(), 0, friendlyPos) != StopPos::STOPPOS_VALID)) {
1384 0 : throw ProcessError(TLF("Invalid start or end position for stop on %'%.",
1385 0 : stop.lane != "" ? ("lane '" + stop.lane) : ("edge '" + stop.edge), errorSuffix));
1386 : }
1387 : }
1388 : }
1389 35205 : stop.edge = edge->getID();
1390 35205 : if (myActiveTransportablePlan) {
1391 1354 : if (myActiveTransportablePlan->empty()) {
1392 543 : double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1393 956 : ? myVehicleParameter->departPos
1394 491 : : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1395 904 : myActiveTransportablePlan->push_back(new MSStageWaiting(
1396 1808 : edge, toStop, -1, myVehicleParameter->depart, departPos, "start", true));
1397 450 : } else if (myActiveTransportablePlan->back()->getDestination() != edge) {
1398 21 : if (myActiveTransportablePlan->back()->getDestination()->isTazConnector()) {
1399 7 : myActiveTransportablePlan->back()->setDestination(edge, toStop);
1400 : } else {
1401 56 : throw ProcessError(TLF("Disconnected plan for % '%' (%!=%).", myActiveTypeName, myVehicleParameter->id,
1402 28 : edge->getID(), myActiveTransportablePlan->back()->getDestination()->getID()));
1403 : }
1404 : }
1405 : // transporting veh stops somewhere
1406 429 : else if (myActiveTransportablePlan->back()->getStageType() == MSStageType::WAITING
1407 429 : && (attrs.hasAttribute(SUMO_ATTR_ENDPOS) || attrs.hasAttribute(SUMO_ATTR_STARTPOS))) {
1408 12 : const double start = SUMOVehicleParameter::interpretEdgePos(stop.startPos, edge->getLength(), SUMO_ATTR_STARTPOS, "stopping at " + edge->getID());
1409 12 : const double end = SUMOVehicleParameter::interpretEdgePos(stop.endPos, edge->getLength(), SUMO_ATTR_ENDPOS, "stopping at " + edge->getID());
1410 12 : const double prevAr = myActiveTransportablePlan->back()->getArrivalPos();
1411 12 : if (start > prevAr + NUMERICAL_EPS || end < prevAr - NUMERICAL_EPS) {
1412 36 : WRITE_WARNINGF(TL("Disconnected plan for % '%' (stop range %-% does not cover previous arrival position %)."),
1413 : myActiveTypeName, myVehicleParameter->id, toString(start), toString(end), toString(prevAr));
1414 : }
1415 : }
1416 1340 : std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "");
1417 1340 : double pos = (stop.startPos + stop.endPos) / 2.;
1418 1340 : if (!myActiveTransportablePlan->empty()) {
1419 1340 : pos = myActiveTransportablePlan->back()->getArrivalPos();
1420 : }
1421 1340 : myActiveTransportablePlan->push_back(new MSStageWaiting(edge, toStop, stop.duration, stop.until, pos, actType, false));
1422 1340 : result = myActiveTransportablePlan->back();
1423 :
1424 33851 : } else if (myVehicleParameter != nullptr) {
1425 30716 : myVehicleParameter->stops.push_back(stop);
1426 30716 : result = &myVehicleParameter->stops.back();
1427 : } else {
1428 3135 : myActiveRouteStops.push_back(stop);
1429 : result = &myActiveRouteStops.back();
1430 : }
1431 35191 : if (myInsertStopEdgesAt >= 0) {
1432 : //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
1433 7554 : if (edge->isInternal()) {
1434 9 : if (myInsertStopEdgesAt > 0 && *(myActiveRoute.begin() + (myInsertStopEdgesAt - 1)) != edge->getNormalBefore()) {
1435 0 : myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalBefore());
1436 0 : myInsertStopEdgesAt++;
1437 : }
1438 9 : myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge->getNormalSuccessor());
1439 9 : myInsertStopEdgesAt++;
1440 : } else {
1441 7545 : myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
1442 7545 : myInsertStopEdgesAt++;
1443 : }
1444 27637 : } else if (myHaveVia) {
1445 : // vias were loaded, check for consistency
1446 3396 : if (std::find(myActiveRoute.begin(), myActiveRoute.end(), edge) == myActiveRoute.end()) {
1447 21 : WRITE_WARNINGF(TL("Stop edge '%' missing in attribute 'via' for % '%'."),
1448 : edge->getID(), myActiveTypeName, myVehicleParameter->id);
1449 : }
1450 : }
1451 35417 : } catch (ProcessError&) {
1452 110 : deleteActivePlanAndVehicleParameter();
1453 110 : throw;
1454 110 : }
1455 35191 : return result;
1456 : }
1457 :
1458 :
1459 : void
1460 40905 : MSRouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
1461 : const MSEdge* fromEdge, const MSEdge*& toEdge,
1462 : double& departPos, double& arrivalPos, MSStoppingPlace*& bs,
1463 : const MSStage* const lastStage, bool& ok) {
1464 : try {
1465 40905 : const std::string description = "person '" + personID + "' walking from edge '" + fromEdge->getID() + "'";
1466 :
1467 40905 : if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1468 0 : 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."));
1469 : }
1470 40905 : departPos = 0.;
1471 40905 : if (lastStage != nullptr) {
1472 32863 : if (lastStage->getDestinationStop() != nullptr) {
1473 285 : departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge, &myParsingRNG);
1474 32578 : } else if (lastStage->getDestination() == fromEdge) {
1475 32525 : departPos = lastStage->getArrivalPos();
1476 53 : } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
1477 22 : departPos = fromEdge->getLength();
1478 : }
1479 : }
1480 :
1481 40905 : bs = retrieveStoppingPlace(attrs, " " + description);
1482 40905 : if (bs != nullptr) {
1483 6441 : arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
1484 3391 : if (arrivalPos < 0) {
1485 0 : throw ProcessError("Bus stop '" + bs->getID() + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
1486 : }
1487 3391 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1488 8 : const double length = toEdge != nullptr ? toEdge->getLength() : bs->getLane().getLength();
1489 8 : const double arrPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, length,
1490 8 : attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1491 8 : if (arrPos >= bs->getBeginLanePosition() && arrPos < bs->getEndLanePosition()) {
1492 4 : arrivalPos = arrPos;
1493 : } else {
1494 12 : WRITE_WARNINGF(TL("Ignoring arrivalPos for % because it is outside the given stop '%'."), description, toString(SUMO_ATTR_BUS_STOP));
1495 4 : arrivalPos = bs->getAccessPos(&bs->getLane().getEdge());
1496 : }
1497 : }
1498 : } else {
1499 37514 : if (toEdge == nullptr) {
1500 0 : throw ProcessError(TLF("No destination edge for %.", description));
1501 : }
1502 37514 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1503 31700 : arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, myHardFail, description, toEdge->getLength(),
1504 63400 : attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1505 : } else {
1506 5814 : arrivalPos = toEdge->getLength() / 2.;
1507 : }
1508 : }
1509 0 : } catch (ProcessError&) {
1510 0 : deleteActivePlanAndVehicleParameter();
1511 0 : throw;
1512 0 : }
1513 40905 : }
1514 :
1515 :
1516 : void
1517 8055 : MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1518 8055 : if (myVehicleParameter == nullptr) {
1519 12 : throw ProcessError(TL("Cannot define person stage without person."));
1520 : }
1521 : try {
1522 : myActiveRoute.clear();
1523 8049 : bool ok = true;
1524 : const char* const id = myVehicleParameter->id.c_str();
1525 : const MSEdge* from = nullptr;
1526 8049 : const MSEdge* to = nullptr;
1527 8049 : parseFromViaTo(SUMO_TAG_PERSON, attrs);
1528 8042 : myInsertStopEdgesAt = -1;
1529 10543 : if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
1530 10445 : || attrs.hasAttribute(SUMO_ATTR_FROMXY) || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
1531 5688 : from = myActiveRoute.front();
1532 2354 : } else if (myActiveTransportablePlan->empty()) {
1533 0 : throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
1534 : } else {
1535 2354 : from = myActiveTransportablePlan->back()->getDestination();
1536 : }
1537 11924 : if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1538 11162 : || attrs.hasAttribute(SUMO_ATTR_TOXY) || attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
1539 4992 : to = myActiveRoute.back();
1540 : } // else, to may also be derived from stopping place
1541 :
1542 8042 : const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1543 8042 : if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1544 0 : throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1545 : }
1546 :
1547 8042 : double departPos = 0;
1548 8042 : double arrivalPos = 0;
1549 8042 : MSStoppingPlace* stoppingPlace = nullptr;
1550 8042 : parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
1551 :
1552 8042 : SVCPermissions modeSet = 0;
1553 8042 : if (attrs.hasAttribute(SUMO_ATTR_MODES)) {
1554 1924 : const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1555 : std::string errorMsg;
1556 : // try to parse person modes
1557 1924 : if (!SUMOVehicleParameter::parsePersonModes(modes, "person", id, modeSet, errorMsg)) {
1558 0 : throw InvalidArgument(errorMsg);
1559 : }
1560 : } else {
1561 7080 : modeSet = myVehicleParameter->modes;
1562 : }
1563 8042 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, OptionsCont::getOptions().getString("persontrip.default.group"));
1564 8042 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
1565 24126 : const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, myVehicleParameter->vTypes);
1566 16183 : for (StringTokenizer st(types); st.hasNext();) {
1567 99 : const std::string vtypeid = st.next();
1568 99 : const MSVehicleType* const vType = vehControl.getVType(vtypeid);
1569 99 : if (vType == nullptr) {
1570 0 : throw InvalidArgument("The vehicle type '" + vtypeid + "' in a trip for person '" + myVehicleParameter->id + "' is not known.");
1571 : }
1572 198 : modeSet |= (vType->getVehicleClass() == SVC_BICYCLE) ? SVC_BICYCLE : SVC_PASSENGER;
1573 8042 : }
1574 8042 : const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
1575 8042 : if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1576 0 : throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
1577 : }
1578 8042 : const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1579 16084 : const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), -1, "personTrip");
1580 8042 : if (ok) {
1581 8042 : if (myActiveTransportablePlan->empty()) {
1582 5539 : double initialDepartPos = myVehicleParameter->departPos;
1583 5539 : if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
1584 : initialDepartPos = RandHelper::rand(from->getLength(), &myParsingRNG);
1585 : }
1586 11078 : myActiveTransportablePlan->push_back(new MSStageWaiting(from, nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1587 : }
1588 8042 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1589 8042 : MSStoppingPlace* fromStop = myActiveTransportablePlan->empty() ? nullptr : myActiveTransportablePlan->back()->getDestinationStop();
1590 11092 : myActiveTransportablePlan->push_back(new MSStageTrip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to,
1591 : stoppingPlace, duration, modeSet, types, speed, walkFactor, group,
1592 11092 : departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
1593 8042 : myParamStack.push_back(myActiveTransportablePlan->back());
1594 8042 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1595 1467 : myActiveTransportablePlan->back()->markSet(VEHPARS_ARRIVALPOS_SET);
1596 : }
1597 : }
1598 : myActiveRoute.clear();
1599 7 : } catch (ProcessError&) {
1600 7 : deleteActivePlanAndVehicleParameter();
1601 7 : throw;
1602 7 : }
1603 8042 : }
1604 :
1605 :
1606 : void
1607 38379 : MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1608 38379 : if (myVehicleParameter == nullptr) {
1609 0 : throw ProcessError(TL("Cannot define person stage without person."));
1610 : }
1611 38379 : myActiveRouteID = "";
1612 38379 : if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1613 : try {
1614 : myActiveRoute.clear();
1615 32873 : bool ok = true;
1616 32873 : const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
1617 32873 : if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1618 24 : throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1619 : }
1620 : double speed = -1; // default to vType speed
1621 32865 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
1622 313 : speed = attrs.get<double>(SUMO_ATTR_SPEED, nullptr, ok);
1623 313 : if (speed <= 0) {
1624 0 : throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
1625 : }
1626 : }
1627 32865 : double departPos = 0;
1628 32865 : double arrivalPos = 0;
1629 32865 : MSStoppingPlace* bs = nullptr;
1630 32865 : if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1631 616 : myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1632 616 : ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteID, &myParsingRNG);
1633 616 : if (route == nullptr) {
1634 0 : throw ProcessError("The route '" + myActiveRouteID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1635 : }
1636 616 : myActiveRoute = route->getEdges();
1637 : } else {
1638 64496 : MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
1639 : }
1640 32863 : if (myActiveTransportablePlan->empty()) {
1641 32404 : double initialDepartPos = myVehicleParameter->departPos;
1642 32404 : if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
1643 22 : initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
1644 : }
1645 64808 : myActiveTransportablePlan->push_back(new MSStageWaiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1646 : }
1647 32863 : parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActiveTransportablePlan->back(), ok);
1648 32863 : if (myActiveRoute.empty()) {
1649 0 : throw ProcessError(TLF("No edges to walk for person '%'.", myVehicleParameter->id));
1650 : }
1651 33132 : if (myActiveTransportablePlan->back()->getDestination() != myActiveRoute.front() &&
1652 32863 : myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getFromJunction() &&
1653 240 : myActiveTransportablePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getToJunction()) {
1654 215 : if (myActiveTransportablePlan->back()->getDestinationStop() == nullptr || myActiveTransportablePlan->back()->getDestinationStop()->getAccessPos(myActiveRoute.front()) < 0.) {
1655 24 : throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + " not connected to " + myActiveTransportablePlan->back()->getDestination()->getID() + ").");
1656 : }
1657 : }
1658 32851 : const int departLane = attrs.getOpt<int>(SUMO_ATTR_DEPARTLANE, nullptr, ok, -1);
1659 65702 : const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), departLane, "walk");
1660 32851 : myActiveTransportablePlan->push_back(new MSStageWalking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat, departLane, myActiveRouteID));
1661 32851 : myParamStack.push_back(myActiveTransportablePlan->back());
1662 32851 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1663 30241 : myActiveTransportablePlan->back()->markSet(VEHPARS_ARRIVALPOS_SET);
1664 : }
1665 : myActiveRoute.clear();
1666 22 : } catch (ProcessError&) {
1667 22 : deleteActivePlanAndVehicleParameter();
1668 22 : throw;
1669 22 : }
1670 : } else { // parse walks from->to as person trips
1671 5506 : addPersonTrip(attrs);
1672 : }
1673 38357 : }
1674 :
1675 : double
1676 40893 : MSRouteHandler::interpretDepartPosLat(const std::string& value, int departLane, const std::string& element) {
1677 40893 : double pos = MSPModel::UNSPECIFIED_POS_LAT;
1678 40893 : if (value == "") {
1679 : return pos;
1680 : }
1681 : std::string error;
1682 : DepartPosLatDefinition dpd;
1683 250 : if (SUMOVehicleParameter::parseDepartPosLat(value, element, myVehicleParameter->id, pos, dpd, error)) {
1684 250 : if (dpd != DepartPosLatDefinition::GIVEN) {
1685 4 : const MSLane* lane = MSStageMoving::checkDepartLane(myActiveRoute.front(), SVC_IGNORING, departLane, myVehicleParameter->id);
1686 4 : if (lane == nullptr) {
1687 0 : throw ProcessError(TLF("Could not find departure lane for walk of person '%' when interpreting departPosLat", myVehicleParameter->id));
1688 : }
1689 4 : const double usableWidth = lane->getWidth() - 0.5;
1690 4 : switch (dpd) {
1691 0 : case DepartPosLatDefinition::RIGHT:
1692 0 : pos = -usableWidth / 2;
1693 0 : break;
1694 0 : case DepartPosLatDefinition::LEFT:
1695 0 : pos = usableWidth / 2;
1696 0 : break;
1697 0 : case DepartPosLatDefinition::CENTER:
1698 0 : pos = 0;
1699 0 : break;
1700 4 : case DepartPosLatDefinition::RANDOM:
1701 : case DepartPosLatDefinition::FREE:
1702 : case DepartPosLatDefinition::RANDOM_FREE:
1703 : /// @note must be randomized for every person individually when loading a personFlow
1704 4 : pos = MSPModel::RANDOM_POS_LAT;
1705 4 : break;
1706 : default:
1707 : break;
1708 : }
1709 : }
1710 : } else {
1711 0 : throw ProcessError(error);
1712 : }
1713 250 : return pos;
1714 : }
1715 :
1716 :
1717 : void
1718 42745 : MSRouteHandler::addTransportable(const SUMOSAXAttributes& /*attrs*/, const bool isPerson) {
1719 42745 : myActiveType = isPerson ? ObjectTypeEnum::PERSON : ObjectTypeEnum::CONTAINER;
1720 42745 : if (!MSNet::getInstance()->getVehicleControl().hasVType(myVehicleParameter->vtypeid)) {
1721 0 : const std::string error = TLF("The type '%' for % '%' is not known.", myVehicleParameter->vtypeid, myActiveTypeName, myVehicleParameter->id);
1722 0 : deleteActivePlanAndVehicleParameter();
1723 0 : throw ProcessError(error);
1724 : }
1725 42745 : myActiveTransportablePlan = new MSTransportable::MSTransportablePlan();
1726 42745 : }
1727 :
1728 :
1729 : void
1730 487 : MSRouteHandler::addTranship(const SUMOSAXAttributes& attrs) {
1731 : try {
1732 : myActiveRoute.clear();
1733 487 : const std::string cid = myVehicleParameter->id;
1734 487 : bool ok = true;
1735 487 : const MSEdge* from = nullptr;
1736 487 : const MSEdge* to = nullptr;
1737 : MSStoppingPlace* cs = nullptr;
1738 :
1739 : double speed;
1740 487 : const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
1741 487 : if (attrs.hasAttribute(SUMO_ATTR_SPEED)) { // speed is explicitly set
1742 71 : speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, -1);
1743 71 : if (!ok) {
1744 0 : throw ProcessError(TLF("Could not read tranship speed for container '%'.", cid));
1745 : }
1746 416 : } else if (vtype != nullptr && vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) { // speed is set by vtype
1747 : speed = vtype->getMaxSpeed();
1748 : } else { // default speed value
1749 416 : speed = DEFAULT_CONTAINER_TRANSHIP_SPEED;
1750 : }
1751 487 : if (speed <= 0) {
1752 0 : throw ProcessError(TLF("Non-positive tranship speed for container '%'.", cid));
1753 : }
1754 : // values from preceding stage:
1755 : const MSEdge* preEdge = nullptr;
1756 : double prePos = 0;
1757 487 : if (!myActiveTransportablePlan->empty()) {
1758 152 : preEdge = myActiveTransportablePlan->back()->getDestination();
1759 152 : prePos = myActiveTransportablePlan->back()->getArrivalPos();
1760 : }
1761 : // set depart position as given attribute value, arrival position of preceding stage or default (=0)
1762 487 : double departPos = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS, cid.c_str(), ok, prePos);
1763 :
1764 487 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
1765 252 : MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, cid.c_str(), ok), myActiveRoute, myActiveRouteID);
1766 : } else {
1767 : // set 'from':
1768 361 : if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
1769 279 : const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, cid.c_str(), ok);
1770 279 : from = MSEdge::dictionary(fromID);
1771 279 : if (from == nullptr) {
1772 0 : throw ProcessError("The from edge '" + fromID + "' within a tranship of container '" + cid + "' is not known.");
1773 : }
1774 279 : if (preEdge != nullptr && preEdge != from) {
1775 0 : throw ProcessError("Disconnected plan for container '" + cid + "' (" + from->getID() + "!=" + preEdge->getID() + ").");
1776 : }
1777 82 : } else if (preEdge == nullptr) {
1778 0 : throw ProcessError(TLF("The start edge for container '%' is not known.", cid));
1779 : } else {
1780 82 : from = preEdge;
1781 : }
1782 : // set 'to':
1783 361 : if (attrs.hasAttribute(SUMO_ATTR_TO)) {
1784 271 : const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, cid.c_str(), ok);
1785 271 : to = MSEdge::dictionary(toID);
1786 271 : if (to == nullptr) {
1787 0 : throw ProcessError("The to edge '" + toID + "' within a tranship of container '" + cid + "' is not known.");
1788 : }
1789 : } else {
1790 90 : const std::string description = "container '" + cid + "' transhipping from edge '" + from->getID() + "'";
1791 90 : cs = retrieveStoppingPlace(attrs, " " + description);
1792 90 : if (cs != nullptr) {
1793 90 : to = &cs->getLane().getEdge();
1794 : } else {
1795 0 : throw ProcessError(TLF("Inconsistent tranship for container '%', needs either: 'edges', 'to', 'containerStop' (or any other stopping place)", cid));
1796 : }
1797 : }
1798 361 : myActiveRoute.push_back(from);
1799 361 : myActiveRoute.push_back(to);
1800 : }
1801 487 : if (myActiveRoute.empty()) {
1802 0 : throw ProcessError(TLF("No edges to tranship container '%'.", cid));
1803 : }
1804 487 : if (preEdge == nullptr) { // additional 'stop' to start the container plan
1805 335 : myActiveTransportablePlan->push_back(new MSStageWaiting(
1806 670 : myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, departPos, "start", true));
1807 : }
1808 974 : double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, cid.c_str(), ok,
1809 487 : cs == nullptr ? myActiveRoute.back()->getLength() : cs->getEndLanePosition());
1810 487 : myActiveTransportablePlan->push_back(new MSStageTranship(myActiveRoute, cs, speed, departPos, arrivalPos));
1811 487 : myParamStack.push_back(myActiveTransportablePlan->back());
1812 : myActiveRoute.clear();
1813 0 : } catch (ProcessError&) {
1814 0 : deleteActivePlanAndVehicleParameter();
1815 0 : throw;
1816 0 : }
1817 487 : }
1818 :
1819 :
1820 : void
1821 119 : MSRouteHandler::initLaneTree(NamedRTree* tree) {
1822 4763 : for (const auto& edge : MSEdge::getAllEdges()) {
1823 4644 : if (edge->isNormal() || MSGlobals::gUsingInternalLanes) {
1824 8518 : for (MSLane* lane : edge->getLanes()) {
1825 4178 : Boundary b = lane->getShape().getBoxBoundary();
1826 4178 : const float cmin[2] = {(float) b.xmin(), (float) b.ymin()};
1827 4178 : const float cmax[2] = {(float) b.xmax(), (float) b.ymax()};
1828 4178 : tree->Insert(cmin, cmax, lane);
1829 4178 : }
1830 : }
1831 : }
1832 119 : }
1833 :
1834 :
1835 : SumoRNG*
1836 0 : MSRouteHandler::getRNG() {
1837 0 : return &myParsingRNG;
1838 : }
1839 :
1840 :
1841 : MSEdge*
1842 980 : MSRouteHandler::retrieveEdge(const std::string& id) {
1843 980 : return MSEdge::dictionary(id);
1844 : }
1845 :
1846 :
1847 : /****************************************************************************/
|