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