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