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