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 79515 : MSRouteHandler::MSRouteHandler(const std::string& file, bool addVehiclesDirectly) :
59 : SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes", true),
60 159030 : MapMatcher(OptionsCont::getOptions().getBool("mapmatch.junctions"),
61 159030 : OptionsCont::getOptions().getBool("mapmatch.taz"),
62 79515 : OptionsCont::getOptions().getFloat("mapmatch.distance"),
63 : MsgHandler::getErrorInstance()),
64 79515 : myActiveRouteRepeat(0),
65 79515 : myActiveRoutePeriod(0),
66 79515 : myActiveRoutePermanent(false),
67 79515 : myActiveType(ObjectTypeEnum::UNDEFINED),
68 79515 : myHaveVia(false),
69 79515 : myActiveTransportablePlan(nullptr),
70 79515 : myAddVehiclesDirectly(addVehiclesDirectly),
71 79515 : myCurrentVTypeDistribution(nullptr),
72 79515 : myCurrentRouteDistribution(nullptr),
73 79515 : myAmLoadingState(false),
74 79515 : myScaleSuffix(OptionsCont::getOptions().getString("scale-suffix")),
75 79515 : myReplayRerouting(OptionsCont::getOptions().getBool("replay-rerouting")),
76 353419 : myStartTriggeredInFlow(false) {
77 79515 : myActiveRoute.reserve(100);
78 79515 : }
79 :
80 :
81 193072 : 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 46495 : MSRouteHandler::resetActivePlanAndVehicleParameter() {
99 46495 : myVehicleParameter = nullptr;
100 46495 : myActiveTransportablePlan = nullptr;
101 46495 : myActiveType = ObjectTypeEnum::UNDEFINED;
102 46495 : }
103 :
104 :
105 : void
106 78852 : MSRouteHandler::parseFromViaTo(SumoXMLTag tag, const SUMOSAXAttributes& attrs) {
107 78852 : const std::string element = toString(tag);
108 : myActiveRoute.clear();
109 78852 : bool useTaz = OptionsCont::getOptions().getBool("with-taz");
110 78852 : 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 78852 : bool ok = true;
115 78900 : const std::string rid = "for " + element + " '" + myVehicleParameter->id + "'";
116 : SUMOVehicleClass vClass = SVC_PASSENGER;
117 78852 : auto& vc = MSNet::getInstance()->getVehicleControl();
118 78852 : if (!vc.getVTypeDistribution(myVehicleParameter->vtypeid)) {
119 78010 : MSVehicleType* const type = vc.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
120 78010 : if (type != nullptr) {
121 78010 : vClass = type->getParameter().vehicleClass;
122 : }
123 : }
124 : // from-attributes
125 97242 : if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_FROM) && !attrs.hasAttribute(SUMO_ATTR_FROMXY) && !attrs.hasAttribute(SUMO_ATTR_FROMLONLAT))) &&
126 35482 : (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 76554 : } 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 76379 : } 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 152750 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
153 76351 : myActiveRoute, rid);
154 : }
155 78824 : if (!ok) {
156 14 : throw ProcessError();
157 : }
158 :
159 : // via-attributes
160 78810 : if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
161 69158 : myInsertStopEdgesAt = (int)myActiveRoute.size();
162 : }
163 : ConstMSEdgeVector viaEdges;
164 78810 : 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 78754 : } 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 78747 : } 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 157460 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok),
179 : viaEdges, rid);
180 : }
181 78804 : if (!viaEdges.empty()) {
182 906 : myHaveVia = true;
183 : }
184 84959 : 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 98965 : if ((useTaz || (!attrs.hasAttribute(SUMO_ATTR_TO) && !attrs.hasAttribute(SUMO_ATTR_TOXY) && !attrs.hasAttribute(SUMO_ATTR_TOLONLAT))) &&
191 38624 : (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 75703 : } 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 75528 : } 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 150986 : MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
218 75493 : myActiveRoute, rid);
219 : }
220 78804 : myActiveRouteID = "!" + myVehicleParameter->id;
221 78804 : if (myVehicleParameter->routeid == "") {
222 : myVehicleParameter->routeid = myActiveRouteID;
223 : }
224 157614 : }
225 :
226 :
227 : void
228 11036014 : MSRouteHandler::myStartElement(int element, const SUMOSAXAttributes& attrs) {
229 : try {
230 53268 : if (myActiveTransportablePlan != nullptr && myActiveTransportablePlan->empty() && myVehicleParameter->departProcedure == DepartDefinition::TRIGGERED
231 11036952 : && 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 11036014 : if (myVehicleParameter == nullptr) {
236 10819952 : myActiveTypeName = toString((SumoXMLTag)element);
237 10819952 : myHaveVia = false;
238 : }
239 11036014 : SUMORouteHandler::myStartElement(element, attrs);
240 11035681 : switch (element) {
241 1806 : case SUMO_TAG_PERSONFLOW:
242 1806 : addTransportable(attrs, true);
243 : break;
244 313 : case SUMO_TAG_CONTAINERFLOW:
245 313 : addTransportable(attrs, false);
246 : break;
247 19575 : case SUMO_TAG_FLOW:
248 19575 : if (myVehicleParameter) {
249 19575 : parseFromViaTo((SumoXMLTag)element, attrs);
250 : }
251 : break;
252 50455 : case SUMO_TAG_TRIP:
253 50455 : parseFromViaTo((SumoXMLTag)element, attrs);
254 : break;
255 : default:
256 : break;
257 : }
258 374 : } catch (ProcessError&) {
259 374 : deleteActivePlanAndVehicleParameter();
260 374 : throw;
261 374 : }
262 11035640 : }
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 152475 : MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
334 : myActiveRoute.clear();
335 152475 : myInsertStopEdgesAt = -1;
336 : // check whether the id is really necessary
337 : std::string rid;
338 152475 : if (myCurrentRouteDistribution != nullptr) {
339 6692 : myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
340 10038 : rid = "distribution '" + myCurrentRouteDistributionID + "'";
341 149129 : } else if (myVehicleParameter != nullptr) {
342 : // ok, a vehicle is wrapping the route,
343 : // we may use this vehicle's id as default
344 111302 : myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
345 111302 : if (attrs.hasAttribute(SUMO_ATTR_ID)) {
346 24 : WRITE_WARNINGF(TL("Ids of internal routes are ignored (vehicle '%')."), myVehicleParameter->id);
347 : }
348 : } else {
349 37827 : bool ok = true;
350 37827 : myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
351 37827 : if (!ok) {
352 0 : return;
353 : }
354 113481 : rid = "'" + myActiveRouteID + "'";
355 : }
356 152475 : if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
357 342630 : rid = "for vehicle '" + myVehicleParameter->id + "'";
358 : }
359 152475 : bool ok = true;
360 152475 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
361 304746 : MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
362 : }
363 304874 : myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
364 152499 : if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
365 42 : throw ProcessError(TLF("Invalid reference to route '%' in route %.", myActiveRouteRefID, rid));
366 : }
367 152423 : myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
368 152423 : myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
369 152423 : myActiveRouteRepeat = attrs.getOpt<int>(SUMO_ATTR_REPEAT, myActiveRouteID.c_str(), ok, 0);
370 152423 : myActiveRouteReplacedAtTime = attrs.getOptSUMOTimeReporting(SUMO_ATTR_REPLACED_AT_TIME, myActiveRouteID.c_str(), ok, -1);
371 152423 : myActiveRouteReplacedIndex = attrs.getOpt<int>(SUMO_ATTR_REPLACED_ON_INDEX, myActiveRouteID.c_str(), ok, 0);
372 152423 : 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 152423 : myActiveRoutePermanent = attrs.getOpt<bool>(SUMO_ATTR_STATE, myActiveRouteID.c_str(), ok, false);
376 152423 : if (attrs.hasAttribute(SUMO_ATTR_PERIOD)) {
377 0 : WRITE_WARNING(TL("Attribute 'period' is deprecated for route. Use 'cycleTime' instead."));
378 : }
379 152423 : myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
380 152423 : if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
381 52 : WRITE_WARNING(TLF("Invalid cost for route '%'.", myActiveRouteID));
382 : }
383 : }
384 :
385 :
386 : void
387 10751 : MSRouteHandler::openFlow(const SUMOSAXAttributes& /*attrs*/) {
388 : // Currently unused
389 10751 : }
390 :
391 :
392 : void
393 8824 : MSRouteHandler::openRouteFlow(const SUMOSAXAttributes& /*attrs*/) {
394 : // Currently unused
395 8824 : }
396 :
397 :
398 : void
399 50455 : MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
400 : // Currently unsued
401 50455 : }
402 :
403 :
404 : void
405 209422 : MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
406 209422 : std::string type = "vehicle";
407 209422 : if (mayBeDisconnected) {
408 56999 : if (myVehicleParameter->repetitionNumber >= 0) {
409 : type = "flow";
410 : } else {
411 : type = "trip";
412 : }
413 : }
414 :
415 : try {
416 209422 : 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 209422 : 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 209362 : if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
442 0 : throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
443 : }
444 209362 : 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 209346 : MSRoute* const route = new MSRoute(myActiveRouteID, myActiveRoute,
498 418692 : myAmLoadingState ? myActiveRoutePermanent : myVehicleParameter == nullptr,
499 209346 : myActiveRouteColor, myActiveRouteStops,
500 209346 : myActiveRouteReplacedAtTime, myActiveRouteReplacedIndex);
501 209346 : route->setPeriod(myActiveRoutePeriod);
502 209346 : route->setCosts(myCurrentCosts);
503 : route->setReroute(mustReroute);
504 : myActiveRoute.clear();
505 : ConstMSRoutePtr constRoute = std::shared_ptr<const MSRoute>(route);
506 418692 : if (!MSRoute::dictionary(myActiveRouteID, constRoute)) {
507 420 : 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 208926 : if (myCurrentRouteDistribution != nullptr) {
520 6586 : myCurrentRouteDistribution->add(constRoute, myActiveRouteProbability);
521 : }
522 : }
523 : myActiveRouteID = "";
524 209320 : myActiveRouteColor = nullptr;
525 : myActiveRouteStops.clear();
526 209320 : 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 314495 : MSRouteHandler::closeVehicle() {
606 : // get nested route
607 314495 : const std::string embeddedRouteID = "!" + myVehicleParameter->id;
608 : ConstMSRoutePtr route = nullptr;
609 314495 : 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 314495 : if (route == nullptr) {
616 626948 : route = MSRoute::dictionary(embeddedRouteID, &myParsingRNG);
617 : }
618 314495 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
619 314495 : if (myVehicleParameter->departProcedure == DepartDefinition::GIVEN) {
620 : // let's check whether this vehicle had to depart before the simulation starts
621 313913 : if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart <= MSNet::getInstance()->getStateLoaderTime() && !myAmLoadingState)) {
622 1123 : mySkippedVehicles.insert(myVehicleParameter->id);
623 : return;
624 : }
625 : }
626 :
627 : // get the vehicle's type
628 : MSVehicleType* vtype = nullptr;
629 :
630 : try {
631 313372 : if (myVehicleParameter->vtypeid != "") {
632 313372 : vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
633 313372 : if (vtype == nullptr) {
634 0 : throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
635 : }
636 313372 : 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 313372 : if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
644 : // if the route id was given, prefer that one
645 155491 : if (route != nullptr && !myAmLoadingState) {
646 18 : WRITE_WARNINGF(TL("Ignoring child element 'route' for vehicle '%' because attribute 'route' is set."), myVehicleParameter->id);
647 : }
648 310982 : route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
649 : }
650 313372 : 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 313351 : myActiveRouteID = "";
659 :
660 21 : } catch (ProcessError&) {
661 21 : deleteActivePlanAndVehicleParameter();
662 21 : throw;
663 21 : }
664 313351 : 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 313351 : 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 313344 : 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 313337 : if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
690 313098 : MSVehicleControl::VehicleDefinitionSource source = (myAmLoadingState) ? MSVehicleControl::VehicleDefinitionSource::STATE : MSVehicleControl::VehicleDefinitionSource::ROUTEFILE;
691 : try {
692 313098 : 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 312990 : const SUMOTime origDepart = myVehicleParameter->depart;
706 : // maybe we do not want this vehicle to be inserted due to scaling
707 312990 : int quota = myAmLoadingState ? 1 : vehControl.getQuota(vehControl.getScale() * vtype->getParameter().scale);
708 308072 : if (quota > 0) {
709 312034 : registerLastDepart();
710 312034 : myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
711 312034 : vehControl.addVehicle(myVehicleParameter->id, vehicle);
712 312034 : 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 312206 : 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 312034 : 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 239 : 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 312034 : if (vehicle != nullptr) {
772 312034 : if (vehicle->getParameter().departProcedure == DepartDefinition::GIVEN || vehicle->getParameter().departProcedure == DepartDefinition::BEGIN) {
773 311459 : 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 43722 : MSRouteHandler::closeTransportable() {
814 : try {
815 43722 : 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 19577 : if (!(myAddVehiclesDirectly || checkLastDepart())
822 63293 : || (myVehicleParameter->depart <= MSNet::getInstance()->getStateLoaderTime() && !myAmLoadingState)) {
823 31 : deleteActivePlanAndVehicleParameter();
824 : return;
825 : }
826 : // type existence has been checked on opening
827 43685 : MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
828 43685 : if (myActiveType == ObjectTypeEnum::PERSON
829 41626 : && type->getVehicleClass() != SVC_PEDESTRIAN
830 43741 : && !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 43685 : int created = addFlowTransportable(myVehicleParameter->depart, type, myVehicleParameter->id, -1);
834 43657 : registerLastDepart();
835 43657 : if (created > 0) {
836 43633 : 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 41663 : MSRouteHandler::closePerson() {
849 41663 : closeTransportable();
850 41637 : }
851 :
852 :
853 : void
854 2059 : MSRouteHandler::closeContainer() {
855 2059 : closeTransportable();
856 2051 : }
857 :
858 :
859 : void
860 1796 : MSRouteHandler::closePersonFlow() {
861 1796 : closeTransportableFlow();
862 1784 : }
863 :
864 :
865 : void
866 311 : MSRouteHandler::closeContainerFlow() {
867 311 : closeTransportableFlow();
868 311 : }
869 :
870 :
871 : void
872 2107 : MSRouteHandler::closeTransportableFlow() {
873 : try {
874 2107 : const std::string fid = myVehicleParameter->id;
875 2107 : 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 2107 : if (!(myAddVehiclesDirectly || checkLastDepart())
880 4214 : || (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 2107 : registerLastDepart();
887 2107 : std::string baseID = myVehicleParameter->id;
888 2107 : 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 1900 : SUMOTime depart = myVehicleParameter->depart;
902 1900 : if (myVehicleParameter->repetitionOffset < 0) {
903 : // poisson: randomize first depart
904 99 : myVehicleParameter->incrementFlow(1, &myParsingRNG);
905 : }
906 451354 : 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 449466 : MSVehicleType* const type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
910 449466 : addFlowTransportable(depart + myVehicleParameter->repetitionTotalOffset, type, baseID, i);
911 449454 : myVehicleParameter->incrementFlow(1, &myParsingRNG);
912 : }
913 : }
914 2095 : resetActivePlanAndVehicleParameter();
915 12 : } catch (ProcessError&) {
916 12 : deleteActivePlanAndVehicleParameter();
917 12 : throw;
918 12 : }
919 2095 : myStartTriggeredInFlow = false;
920 : }
921 :
922 :
923 : int
924 527946 : MSRouteHandler::addFlowTransportable(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
925 : try {
926 : int numCreated = 0;
927 527946 : MSNet* const net = MSNet::getInstance();
928 527946 : MSTransportableControl& tc = myActiveType == ObjectTypeEnum::PERSON ? net->getPersonControl() : net->getContainerControl();
929 527922 : const MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
930 : //MSTransportableControl& pc = net->getPersonControl();
931 527922 : const int quota = vc.getQuota(vc.getScale() * type->getParameter().scale, tc.getLoadedNumber());
932 527922 : if (quota == 0) {
933 : tc.addDiscarded();
934 : }
935 1056348 : for (int j = 0; j < quota; j++) {
936 528442 : if (i > 0 || j > 0) {
937 : // copy parameter and plan because the transportable takes over responsibility
938 482698 : SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
939 482698 : *copyParam = *myVehicleParameter;
940 482698 : myVehicleParameter = copyParam;
941 482698 : MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
942 : MSStage* lastStage = nullptr;
943 1554555 : for (MSStage* const s : *myActiveTransportablePlan) {
944 1071857 : copyPlan->push_back(s->clone());
945 1071857 : 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 482698 : myActiveTransportablePlan = copyPlan;
952 482698 : 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 528442 : myVehicleParameter->id = (baseID
958 2025958 : + (i >= 0 ? "." + toString(i) : "")
959 1585326 : + (j > 0 ? "." + toString(j) : ""));
960 528442 : if (MSGlobals::gStateLoaded && tc.get(myVehicleParameter->id) != nullptr) {
961 0 : return numCreated;
962 : }
963 528442 : myVehicleParameter->depart = depart += net->getInsertionControl().computeRandomDepartOffset();
964 528442 : MSTransportable* transportable = myActiveType == ObjectTypeEnum::PERSON ?
965 331043 : tc.buildPerson(myVehicleParameter, type, myActiveTransportablePlan, &myParsingRNG) :
966 197399 : tc.buildContainer(myVehicleParameter, type, myActiveTransportablePlan);
967 528442 : numCreated++;
968 528442 : 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 337872 : } else if ((net->hasPersons() && net->getPersonControl().get(myVehicleParameter->id) != nullptr)
974 859469 : && (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 527906 : return numCreated;
979 40 : } catch (ProcessError&) {
980 40 : deleteActivePlanAndVehicleParameter();
981 40 : throw;
982 40 : }
983 : }
984 :
985 :
986 : void
987 62191 : MSRouteHandler::closeVType() {
988 : MSVehicleType* vehType = nullptr;
989 62191 : if (myCurrentVTypeRef.empty()) {
990 62109 : vehType = MSVehicleType::build(*myCurrentVType, getFileName());
991 62105 : vehType->check();
992 62105 : if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
993 : const std::string id = vehType->getID();
994 194 : delete vehType;
995 194 : if (!MSGlobals::gStateLoaded) {
996 60 : throw ProcessError(TLF("Another vehicle type (or distribution) with the id '%' exists.", id));
997 : }
998 : }
999 : }
1000 62167 : 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 19575 : MSRouteHandler::closeFlow() {
1023 19575 : myInsertStopEdgesAt = -1;
1024 19575 : 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 19575 : myVehicleParameter->repetitionsDone = 0;
1031 19575 : if (myVehicleParameter->repetitionProbability < 0) {
1032 15465 : const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
1033 16798 : 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 19560 : 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 30302 : if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
1046 6585 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1047 6585 : closeRoute(true);
1048 : }
1049 19560 : ConstMSRoutePtr route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
1050 19560 : if (route == nullptr) {
1051 0 : throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
1052 : }
1053 19560 : 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 19560 : 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 19553 : 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 19553 : myActiveRouteID = "";
1077 :
1078 : // check whether the vehicle shall be added directly to the network or
1079 : // shall stay in the internal buffer
1080 19553 : if (myAddVehiclesDirectly || checkLastDepart()) {
1081 19553 : if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
1082 19514 : 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 19553 : myVehicleParameter = nullptr;
1092 : }
1093 :
1094 :
1095 : void
1096 50414 : MSRouteHandler::closeTrip() {
1097 50414 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1098 50414 : closeRoute(true);
1099 50414 : closeVehicle();
1100 50397 : }
1101 :
1102 : void
1103 5409 : MSRouteHandler::addRide(const SUMOSAXAttributes& attrs) {
1104 5409 : addRideOrTransport(attrs, SUMO_TAG_RIDE);
1105 5369 : }
1106 :
1107 : void
1108 1843 : MSRouteHandler::addTransport(const SUMOSAXAttributes& attrs) {
1109 1843 : addRideOrTransport(attrs, SUMO_TAG_TRANSPORT);
1110 1831 : }
1111 :
1112 : void
1113 7252 : MSRouteHandler::addRideOrTransport(const SUMOSAXAttributes& attrs, const SumoXMLTag modeTag) {
1114 7252 : 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 7304 : std::string agent = "person";
1120 7304 : std::string stop = "bus stop";
1121 7252 : if (myActiveType == ObjectTypeEnum::CONTAINER) {
1122 : agent = "container";
1123 : stop = "container stop";
1124 : }
1125 :
1126 7252 : if (!((myActiveType == ObjectTypeEnum::PERSON && modeTag == SUMO_TAG_RIDE) ||
1127 1843 : (myActiveType == ObjectTypeEnum::CONTAINER && modeTag == SUMO_TAG_TRANSPORT))) {
1128 36 : throw ProcessError("Found " + mode + " inside " + agent + " element");
1129 : }
1130 7240 : const std::string aid = myVehicleParameter->id;
1131 7240 : bool ok = true;
1132 : const MSEdge* from = nullptr;
1133 14520 : const std::string desc = attrs.getOpt<std::string>(SUMO_ATTR_LINES, aid.c_str(), ok, LINE_ANY);
1134 7280 : StringTokenizer st(desc);
1135 14480 : MSStoppingPlace* s = retrieveStoppingPlace(attrs, " in " + agent + " '" + aid + "'");
1136 : MSEdge* to = nullptr;
1137 7240 : if (s != nullptr) {
1138 1876 : to = &s->getLane().getEdge();
1139 : }
1140 7240 : double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, aid.c_str(), ok,
1141 1876 : s == nullptr ? std::numeric_limits<double>::infinity() : s->getEndLanePosition());
1142 :
1143 : const SUMOVehicleParameter* startVeh = nullptr;
1144 : const MSEdge* startVehFrom = nullptr;
1145 7240 : 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 7224 : if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
1182 4946 : const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, aid.c_str(), ok);
1183 4946 : from = MSEdge::dictionary(fromID);
1184 4946 : if (from == nullptr) {
1185 0 : throw ProcessError("The from edge '" + fromID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
1186 : }
1187 4946 : 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 4932 : if (startVeh != nullptr && startVehFrom != from) {
1198 8 : throw ProcessError("Disconnected plan for triggered " + agent + " '" + aid +
1199 24 : "' (edge '" + fromID + "' != edge '" + startVehFrom->getID() + "').");
1200 : }
1201 2278 : } else if (startVeh != nullptr) {
1202 : from = startVehFrom;
1203 : }
1204 7202 : if (myActiveTransportablePlan->empty()) {
1205 4685 : if (from == nullptr) {
1206 0 : throw ProcessError("The start edge for " + agent + " '" + aid + "' is not known.");
1207 : } else {
1208 4685 : myActiveTransportablePlan->push_back(new MSStageWaiting(
1209 9370 : from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
1210 : }
1211 : }
1212 : // given attribute may override given stopping place due access requirements
1213 7202 : if (to == nullptr || attrs.hasAttribute(SUMO_ATTR_TO)) {
1214 5407 : const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, aid.c_str(), ok);
1215 5407 : to = MSEdge::dictionary(toID);
1216 5407 : if (to == nullptr) {
1217 6 : throw ProcessError("The to edge '" + toID + "' within a " + mode + " of " + agent + " '" + aid + "' is not known.");
1218 : }
1219 : }
1220 7200 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, aid.c_str(), ok, OptionsCont::getOptions().getString("persontrip.default.group"));
1221 7200 : const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
1222 7200 : const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
1223 14400 : arrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, to->getLength(), SUMO_ATTR_ARRIVALPOS, agent + " '" + aid + "' takes a " + mode + " to edge '" + to->getID() + "'");
1224 7200 : MSStageDriving* stage = new MSStageDriving(from, to, s, arrivalPos, 0.0, st.getVector(), group, intendedVeh, intendedDepart);
1225 : // for loading from saved state
1226 7200 : stage->setDeparted(attrs.getOptSUMOTimeReporting(SUMO_ATTR_STARTED, nullptr, ok, -1));
1227 7200 : stage->setEnded(attrs.getOptSUMOTimeReporting(SUMO_ATTR_ENDED, nullptr, ok, -1));
1228 7200 : stage->setVehicleID(attrs.getOpt<std::string>(SUMO_ATTR_VEHICLE, nullptr, ok, ""));
1229 7200 : stage->setVehicleDistance(attrs.getOpt<double>(SUMO_ATTR_ROUTELENGTH, nullptr, ok, -1));
1230 7200 : myActiveTransportablePlan->push_back(stage);
1231 7200 : myParamStack.push_back(stage);
1232 7292 : } catch (ProcessError&) {
1233 52 : deleteActivePlanAndVehicleParameter();
1234 52 : throw;
1235 52 : }
1236 7200 : }
1237 :
1238 : MSStoppingPlace*
1239 88152 : MSRouteHandler::retrieveStoppingPlace(const SUMOSAXAttributes& attrs, const std::string& errorSuffix, SUMOVehicleParameter::Stop* stopParam) {
1240 88152 : bool ok = true;
1241 : // dummy stop parameter to hold the attributes
1242 88152 : SUMOVehicleParameter::Stop stop;
1243 88152 : if (stopParam != nullptr) {
1244 37930 : stop = *stopParam;
1245 : } else {
1246 100444 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1247 50222 : stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_TRAIN_STOP, nullptr, ok, stop.busstop); // alias
1248 50222 : stop.chargingStation = attrs.getOpt<std::string>(SUMO_ATTR_CHARGING_STATION, nullptr, ok, "");
1249 50222 : stop.overheadWireSegment = attrs.getOpt<std::string>(SUMO_ATTR_OVERHEAD_WIRE_SEGMENT, nullptr, ok, "");
1250 50222 : stop.containerstop = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
1251 100444 : stop.parkingarea = attrs.getOpt<std::string>(SUMO_ATTR_PARKING_AREA, nullptr, ok, "");
1252 : }
1253 : MSStoppingPlace* toStop = nullptr;
1254 88152 : if (stop.busstop != "") {
1255 12563 : toStop = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
1256 12563 : if (toStop == nullptr) {
1257 12 : ok = false;
1258 36 : WRITE_ERROR(TLF("The busStop '%' is not known%.", stop.busstop, errorSuffix));
1259 : }
1260 75589 : } 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 74038 : } 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 57648 : } else if (stop.chargingStation != "") {
1273 : // ok, we have a charging station
1274 2020 : toStop = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
1275 2020 : if (toStop == nullptr) {
1276 0 : ok = false;
1277 0 : WRITE_ERROR(TLF("The chargingStation '%' is not known%.", stop.chargingStation, errorSuffix));
1278 : }
1279 55628 : } 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 88152 : if (!ok && MSGlobals::gCheckRoutes) {
1288 18 : throw ProcessError(TLF("Invalid stop definition%.", errorSuffix));
1289 : }
1290 88146 : return toStop;
1291 88152 : }
1292 :
1293 : Parameterised*
1294 37957 : MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
1295 : Parameterised* result = nullptr;
1296 : try {
1297 : std::string errorSuffix;
1298 37957 : if (myActiveType == ObjectTypeEnum::PERSON) {
1299 4047 : errorSuffix = " in person '" + myVehicleParameter->id + "'";
1300 36608 : } else if (myActiveType == ObjectTypeEnum::CONTAINER) {
1301 1026 : errorSuffix = " in container '" + myVehicleParameter->id + "'";
1302 36266 : } else if (myVehicleParameter != nullptr) {
1303 98877 : errorSuffix = " in vehicle '" + myVehicleParameter->id + "'";
1304 : } else {
1305 9989 : errorSuffix = " in route '" + myActiveRouteID + "'";
1306 : }
1307 37957 : SUMOVehicleParameter::Stop stop;
1308 75914 : bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
1309 37957 : if (!ok) {
1310 27 : if (MSGlobals::gCheckRoutes) {
1311 27 : throw ProcessError();
1312 : }
1313 : return result;
1314 : }
1315 37930 : const MSEdge* edge = nullptr;
1316 : // patch chargingStation stop on a parkingArea
1317 37930 : if (stop.chargingStation != "") {
1318 1204 : const MSChargingStation* cs = dynamic_cast<MSChargingStation*>(MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION));
1319 1204 : const MSParkingArea* pa = cs->getParkingArea();
1320 1204 : if (pa != nullptr) {
1321 : stop.parkingarea = pa->getID();
1322 18 : stop.parking = ParkingType::OFFROAD;
1323 : }
1324 : }
1325 37930 : MSStoppingPlace* toStop = retrieveStoppingPlace(attrs, errorSuffix, &stop);
1326 : // if one of the previous stops is defined
1327 37924 : if (toStop != nullptr) {
1328 26184 : const MSLane& l = toStop->getLane();
1329 : stop.lane = l.getID();
1330 26184 : if ((stop.parametersSet & STOP_END_SET) == 0) {
1331 26168 : stop.endPos = toStop->getEndLanePosition();
1332 : } else {
1333 16 : stop.endPos = attrs.get<double>(SUMO_ATTR_ENDPOS, nullptr, ok);
1334 : }
1335 26184 : stop.startPos = toStop->getBeginLanePosition();
1336 26184 : edge = &l.getEdge();
1337 : } else {
1338 : // no, the lane and the position should be given directly
1339 : // get the lane
1340 11740 : stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
1341 11740 : stop.edge = attrs.getOpt<std::string>(SUMO_ATTR_EDGE, nullptr, ok, "");
1342 11740 : if (ok && stop.edge != "") { // edge is given directly
1343 1777 : edge = MSEdge::dictionary(stop.edge);
1344 1777 : 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 9963 : } else if (ok && stop.lane != "") { // lane is given directly
1348 9889 : MSLane* stopLane = MSLane::dictionary(stop.lane);
1349 9889 : 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 9736 : edge = &stopLane->getEdge();
1357 : }
1358 9881 : 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 11713 : stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, edge->getLength());
1419 11713 : 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 11713 : if (stop.endPos < 0) {
1424 777 : stop.endPos += edge->getLength();
1425 : }
1426 23352 : stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - MIN_STOP_LENGTH));
1427 11713 : if (!myAmLoadingState) {
1428 20414 : const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, !attrs.hasAttribute(SUMO_ATTR_STARTPOS) && !attrs.hasAttribute(SUMO_ATTR_ENDPOS))
1429 11392 : || !MSGlobals::gCheckRoutes;
1430 11392 : 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 37897 : stop.edge = edge->getID();
1437 37897 : if (myActiveTransportablePlan) {
1438 1684 : if (myActiveTransportablePlan->empty()) {
1439 750 : double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1440 1239 : ? myVehicleParameter->departPos
1441 698 : : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1442 1187 : myActiveTransportablePlan->push_back(new MSStageWaiting(
1443 2374 : 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 1670 : std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "");
1464 1670 : double pos = (stop.startPos + stop.endPos) / 2.;
1465 1670 : if (!myActiveTransportablePlan->empty() && myActiveTransportablePlan->back()->getJumpDuration() < 0) {
1466 1648 : pos = myActiveTransportablePlan->back()->unspecifiedArrivalPos() ?
1467 1624 : MSStage::ARRIVALPOS_UNSPECIFIED : myActiveTransportablePlan->back()->getArrivalPos();
1468 : }
1469 1670 : myActiveTransportablePlan->push_back(new MSStageWaiting(edge, toStop, stop.duration, stop.until, pos, actType, false, stop.jump));
1470 1670 : result = myActiveTransportablePlan->back();
1471 :
1472 36213 : } else if (myVehicleParameter != nullptr) {
1473 32906 : myVehicleParameter->stops.push_back(stop);
1474 32906 : result = &myVehicleParameter->stops.back();
1475 : } else {
1476 3307 : myActiveRouteStops.push_back(stop);
1477 : result = &myActiveRouteStops.back();
1478 : }
1479 37883 : if (myInsertStopEdgesAt >= 0) {
1480 : //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
1481 6740 : 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 6723 : myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
1490 6723 : myInsertStopEdgesAt++;
1491 : }
1492 31143 : } 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 38025 : } catch (ProcessError&) {
1500 68 : deleteActivePlanAndVehicleParameter();
1501 68 : throw;
1502 68 : }
1503 37883 : return result;
1504 : }
1505 :
1506 :
1507 : void
1508 42140 : 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 42140 : const std::string description = "person '" + personID + "' walking from edge '" + fromEdge->getID() + "'";
1514 :
1515 42140 : 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 42140 : departPos = 0.;
1519 42140 : if (lastStage != nullptr) {
1520 33325 : if (lastStage->getDestinationStop() != nullptr) {
1521 313 : departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge, &myParsingRNG);
1522 33012 : } else if (lastStage->getDestination() == fromEdge) {
1523 32959 : departPos = lastStage->getArrivalPos();
1524 53 : } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
1525 22 : departPos = fromEdge->getLength();
1526 : }
1527 : }
1528 :
1529 42140 : bs = retrieveStoppingPlace(attrs, " " + description);
1530 42140 : if (bs != nullptr) {
1531 6851 : arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
1532 3610 : if (arrivalPos < 0) {
1533 0 : throw ProcessError("Bus stop '" + bs->getID() + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
1534 : }
1535 3610 : 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 38530 : if (toEdge == nullptr) {
1548 0 : throw ProcessError(TLF("No destination edge for %.", description));
1549 : }
1550 38530 : 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 6802 : arrivalPos = toEdge->getLength() / 2.;
1555 : }
1556 : }
1557 0 : } catch (ProcessError&) {
1558 0 : deleteActivePlanAndVehicleParameter();
1559 0 : throw;
1560 0 : }
1561 42140 : }
1562 :
1563 :
1564 : void
1565 8828 : MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1566 8828 : if (myVehicleParameter == nullptr) {
1567 12 : throw ProcessError(TL("Cannot define person stage without person."));
1568 : }
1569 : try {
1570 : myActiveRoute.clear();
1571 8822 : bool ok = true;
1572 : const char* const id = myVehicleParameter->id.c_str();
1573 : const MSEdge* from = nullptr;
1574 8822 : const MSEdge* to = nullptr;
1575 8822 : parseFromViaTo(SUMO_TAG_PERSON, attrs);
1576 8815 : myInsertStopEdgesAt = -1;
1577 11431 : if (attrs.hasAttribute(SUMO_ATTR_FROM) || attrs.hasAttribute(SUMO_ATTR_FROM_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_FROM_TAZ)
1578 11333 : || attrs.hasAttribute(SUMO_ATTR_FROMXY) || attrs.hasAttribute(SUMO_ATTR_FROMLONLAT)) {
1579 6346 : from = myActiveRoute.front();
1580 2469 : } else if (myActiveTransportablePlan->empty()) {
1581 0 : throw ProcessError(TLF("Start edge not defined for person '%'.", myVehicleParameter->id));
1582 : } else {
1583 2469 : from = myActiveTransportablePlan->back()->getDestination();
1584 : }
1585 13056 : if (attrs.hasAttribute(SUMO_ATTR_TO) || attrs.hasAttribute(SUMO_ATTR_TO_JUNCTION) || attrs.hasAttribute(SUMO_ATTR_TO_TAZ)
1586 12126 : || attrs.hasAttribute(SUMO_ATTR_TOXY) || attrs.hasAttribute(SUMO_ATTR_TOLONLAT)) {
1587 5574 : to = myActiveRoute.back();
1588 : } // else, to may also be derived from stopping place
1589 :
1590 8815 : const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1591 8815 : if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1592 0 : throw ProcessError(TLF("Non-positive walking duration for '%'.", myVehicleParameter->id));
1593 : }
1594 :
1595 8815 : double departPos = 0;
1596 8815 : double arrivalPos = 0;
1597 8815 : MSStoppingPlace* stoppingPlace = nullptr;
1598 8815 : parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
1599 :
1600 8815 : SVCPermissions modeSet = 0;
1601 8815 : if (attrs.hasAttribute(SUMO_ATTR_MODES)) {
1602 2286 : const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1603 : std::string errorMsg;
1604 : // try to parse person modes
1605 2286 : if (!SUMOVehicleParameter::parsePersonModes(modes, "person", id, modeSet, errorMsg)) {
1606 0 : throw InvalidArgument(errorMsg);
1607 : }
1608 : } else {
1609 7672 : modeSet = myVehicleParameter->modes;
1610 : }
1611 8815 : const std::string group = attrs.getOpt<std::string>(SUMO_ATTR_GROUP, id, ok, OptionsCont::getOptions().getString("persontrip.default.group"));
1612 8815 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
1613 26447 : const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, myVehicleParameter->vTypes);
1614 17746 : 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 8815 : }
1622 8813 : const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
1623 8813 : if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1624 0 : throw ProcessError(TLF("Non-positive walking speed for '%'.", myVehicleParameter->id));
1625 : }
1626 8813 : const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1627 17626 : const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), -1, "personTrip");
1628 8813 : if (ok) {
1629 8813 : if (myActiveTransportablePlan->empty()) {
1630 6197 : double initialDepartPos = myVehicleParameter->departPos;
1631 6197 : if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
1632 : initialDepartPos = RandHelper::rand(from->getLength(), &myParsingRNG);
1633 : }
1634 12394 : myActiveTransportablePlan->push_back(new MSStageWaiting(from, nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1635 : }
1636 8813 : myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1637 8813 : MSStoppingPlace* fromStop = myActiveTransportablePlan->empty() ? nullptr : myActiveTransportablePlan->back()->getDestinationStop();
1638 12053 : myActiveTransportablePlan->push_back(new MSStageTrip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to,
1639 : stoppingPlace, duration, modeSet, types, speed, walkFactor, group,
1640 12053 : departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
1641 8815 : myParamStack.push_back(myActiveTransportablePlan->back());
1642 8813 : 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 8813 : }
1652 :
1653 :
1654 : void
1655 39226 : MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1656 39226 : if (myVehicleParameter == nullptr) {
1657 0 : throw ProcessError(TL("Cannot define person stage without person."));
1658 : }
1659 39226 : myActiveRouteID = "";
1660 39226 : if (attrs.hasAttribute(SUMO_ATTR_EDGES) || attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1661 : try {
1662 : myActiveRoute.clear();
1663 33333 : bool ok = true;
1664 33333 : const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
1665 33333 : 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 33325 : 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 33325 : double departPos = 0;
1676 33325 : double arrivalPos = 0;
1677 33325 : MSStoppingPlace* bs = nullptr;
1678 33325 : if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1679 636 : myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1680 636 : ConstMSRoutePtr route = MSRoute::dictionary(myActiveRouteID, &myParsingRNG);
1681 636 : if (route == nullptr) {
1682 0 : throw ProcessError("The route '" + myActiveRouteID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1683 : }
1684 636 : 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 33325 : if (myActiveTransportablePlan->empty()) {
1689 32830 : double initialDepartPos = myVehicleParameter->departPos;
1690 32830 : if (myVehicleParameter->departPosProcedure == DepartPosDefinition::RANDOM) {
1691 23 : initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
1692 : }
1693 65660 : myActiveTransportablePlan->push_back(new MSStageWaiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1694 : }
1695 33325 : parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActiveTransportablePlan->back(), ok);
1696 33325 : if (myActiveRoute.empty()) {
1697 0 : throw ProcessError(TLF("No edges to walk for person '%'.", myVehicleParameter->id));
1698 : }
1699 33613 : if (myActiveTransportablePlan->back()->getDestination() != myActiveRoute.front() &&
1700 33325 : 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 33313 : const int departLane = attrs.getOpt<int>(SUMO_ATTR_DEPARTLANE, nullptr, ok, -1);
1707 66626 : const double departPosLat = interpretDepartPosLat(attrs.getOpt<std::string>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, ""), departLane, "walk");
1708 33313 : MSStageWalking* stage = new MSStageWalking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat, departLane, myActiveRouteID);
1709 33313 : stage->setDeparted(attrs.getOptSUMOTimeReporting(SUMO_ATTR_STARTED, nullptr, ok, -1));
1710 33313 : stage->setEnded(attrs.getOptSUMOTimeReporting(SUMO_ATTR_ENDED, nullptr, ok, -1));
1711 33313 : stage->setTotalWaitingTime(attrs.getOptSUMOTimeReporting(SUMO_ATTR_WAITINGTIME, nullptr, ok, 0));
1712 33327 : 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 33313 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1720 : stage->markSet(VEHPARS_ARRIVALPOS_SET);
1721 : }
1722 33313 : myActiveTransportablePlan->push_back(stage);
1723 33313 : 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 39206 : }
1733 :
1734 : double
1735 42126 : MSRouteHandler::interpretDepartPosLat(const std::string& value, int departLane, const std::string& element) {
1736 42126 : double pos = MSPModel::UNSPECIFIED_POS_LAT;
1737 42126 : 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 45931 : MSRouteHandler::addTransportable(const SUMOSAXAttributes& /*attrs*/, const bool isPerson) {
1778 45931 : myActiveType = isPerson ? ObjectTypeEnum::PERSON : ObjectTypeEnum::CONTAINER;
1779 45931 : 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 45931 : myActiveTransportablePlan = new MSTransportable::MSTransportablePlan();
1785 45931 : }
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 62135 : MSRouteHandler::getVTypeParameter(const std::string& refid) {
1902 62135 : const auto t = MSNet::getInstance()->getVehicleControl().getVType(refid);
1903 62135 : 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 : /****************************************************************************/
|