Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSStageWalking.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @date Mon, 9 Jul 2001
20 : ///
21 : // A stage performing walking on a sequence of edges.
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <string>
26 : #include <vector>
27 : #include <utils/iodevices/OutputDevice.h>
28 : #include <utils/options/OptionsCont.h>
29 : #include <utils/common/ToString.h>
30 : #include <utils/common/StringUtils.h>
31 : #include <utils/geom/GeomHelper.h>
32 : #include <utils/router/IntermodalNetwork.h>
33 : #include <microsim/MSNet.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/MSLane.h>
36 : #include <microsim/transportables/MSTransportableControl.h>
37 : #include <microsim/MSInsertionControl.h>
38 : #include <microsim/MSEventControl.h>
39 : #include <microsim/MSVehicle.h>
40 : #include <microsim/MSVehicleControl.h>
41 : #include <microsim/MSStoppingPlace.h>
42 : #include <microsim/MSRouteHandler.h>
43 : #include <microsim/devices/MSDevice_Tripinfo.h>
44 : #include <microsim/devices/MSDevice_Taxi.h>
45 : #include <microsim/trigger/MSTriggeredRerouter.h>
46 : #include "MSPModel_Striping.h"
47 : #include "MSStageTrip.h"
48 : #include "MSPerson.h"
49 : #include "MSStageWalking.h"
50 :
51 :
52 : // ===========================================================================
53 : // static member definition
54 : // ===========================================================================
55 : bool MSStageWalking::myWarnedInvalidTripinfo = false;
56 :
57 :
58 : // ===========================================================================
59 : // method definitions
60 : // ===========================================================================
61 229682 : MSStageWalking::MSStageWalking(const std::string& personID,
62 : const ConstMSEdgeVector& route,
63 : MSStoppingPlace* toStop,
64 : SUMOTime walkingTime, double speed,
65 : double departPos, double arrivalPos, double departPosLat, int departLane,
66 229682 : const std::string& routeID) :
67 : MSStageMoving(MSStageType::WALKING, route, routeID, toStop, speed, departPos, arrivalPos, departPosLat, departLane),
68 229682 : myWalkingTime(walkingTime),
69 229682 : myExitTimes(nullptr),
70 229682 : myInternalDistance(0) {
71 229682 : myDepartPos = SUMOVehicleParameter::interpretEdgePos(departPos, route.front()->getLength(), SUMO_ATTR_DEPARTPOS,
72 459364 : "person '" + personID + "' walking from edge '" + route.front()->getID() + "'");
73 229682 : myArrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, route.back()->getLength(), SUMO_ATTR_ARRIVALPOS,
74 229682 : "person '" + personID + "' walking to edge '" + route.back()->getID() + "'");
75 229682 : if (walkingTime > 0) {
76 111 : mySpeed = computeAverageSpeed();
77 : }
78 229682 : }
79 :
80 :
81 459278 : MSStageWalking::~MSStageWalking() {
82 229639 : delete myExitTimes;
83 459278 : }
84 :
85 :
86 : MSStage*
87 26644 : MSStageWalking::clone() const {
88 26644 : std::vector<const MSEdge*> route = myRoute;
89 26644 : double departPos = myDepartPos;
90 26644 : double arrivalPos = myArrivalPos;
91 26644 : int departLane = myDepartLane;
92 26644 : if (myRouteID != "" && MSRoute::distDictionary(myRouteID) != nullptr) {
93 576 : route = MSRoute::dictionary(myRouteID, MSRouteHandler::getParsingRNG())->getEdges();
94 288 : if (departPos > route[0]->getLength()) {
95 0 : WRITE_WARNINGF(TL("Adjusting departPos for cloned walk with routeDistribution '%'"), myRouteID);
96 0 : departPos = route[0]->getLength();
97 : }
98 288 : if (arrivalPos > route.back()->getLength()) {
99 0 : WRITE_WARNINGF(TL("Adjusting arrivalPos for cloned walk with routeDistribution '%'"), myRouteID);
100 0 : arrivalPos = route.back()->getLength();
101 : }
102 288 : if (departLane >= route[0]->getNumLanes()) {
103 0 : WRITE_WARNINGF(TL("Adjusting departLane for cloned walk with routeDistribution '%'"), myRouteID);
104 0 : departLane = route[0]->getNumLanes() - 1;
105 : }
106 : }
107 26644 : MSStage* clon = new MSStageWalking("dummyID", route, myDestinationStop, myWalkingTime, mySpeed, departPos, arrivalPos, myDepartPosLat, departLane, myRouteID);
108 26644 : clon->setParameters(*this);
109 26644 : return clon;
110 26644 : }
111 :
112 :
113 : void
114 228747 : MSStageWalking::proceed(MSNet* net, MSTransportable* person, SUMOTime now, MSStage* previous) {
115 228747 : myDeparted = now;
116 228747 : myRouteStep = myRoute.begin();
117 228747 : myLastEdgeEntryTime = now;
118 228747 : if (myWalkingTime == 0) {
119 0 : if (!person->proceed(net, now)) {
120 0 : MSNet::getInstance()->getPersonControl().erase(person);
121 : }
122 0 : return;
123 : }
124 228747 : const OptionsCont& oc = OptionsCont::getOptions();
125 228747 : if (previous->getEdgePos(now) >= 0 && previous->getEdge() == *myRouteStep) {
126 : // we need to adapt to the arrival position of the vehicle unless we have an explicit access
127 228049 : myDepartPos = previous->getEdgePos(now);
128 456098 : if (oc.getString("pedestrian.model") == "jupedsim") {
129 0 : myDepartPosLat = previous->getEdgePosLat(now);
130 : }
131 228049 : if (myWalkingTime > 0) {
132 93 : mySpeed = computeAverageSpeed();
133 : }
134 : }
135 228747 : MSTransportableControl& pControl = net->getPersonControl();
136 228747 : myPState = pControl.getMovementModel()->add(person, this, now);
137 228743 : if (myPState == nullptr) {
138 8 : pControl.erase(person);
139 8 : return;
140 : }
141 228735 : if (previous->getStageType() != MSStageType::WALKING || previous->getEdge() != getEdge()) {
142 : // we only need new move reminders if we are walking a different edge (else it is probably a rerouting)
143 227224 : activateEntryReminders(person, true);
144 : }
145 457470 : if (oc.getBool("vehroute-output.exit-times")) {
146 144 : myExitTimes = new std::vector<SUMOTime>();
147 : }
148 228735 : (*myRouteStep)->addTransportable(person);
149 : }
150 :
151 :
152 : void
153 1696 : MSStageWalking::abort(MSTransportable*) {
154 1696 : MSNet::getInstance()->getPersonControl().getMovementModel()->remove(myPState);
155 1696 : }
156 :
157 :
158 : void
159 18 : MSStageWalking::setSpeed(double speed) {
160 18 : mySpeed = speed;
161 18 : }
162 :
163 :
164 : double
165 204 : MSStageWalking::computeAverageSpeed() const {
166 204 : return walkDistance() / STEPS2TIME(myWalkingTime + 1); // avoid systematic rounding errors
167 : }
168 :
169 :
170 : double
171 276455 : MSStageWalking::walkDistance(bool partial) const {
172 : double length = 0;
173 276455 : auto endIt = partial && myArrived < 0 ? myRouteStep + 1 : myRoute.end();
174 751229 : for (ConstMSEdgeVector::const_iterator i = myRoute.begin(); i != endIt; ++i) {
175 474774 : length += (*i)->getLength();
176 : }
177 276455 : if (myRoute.size() > 1 && MSNet::getInstance()->getPersonControl().getMovementModel()->usingInternalLanes()) {
178 121451 : if (myInternalDistance > 0) {
179 109360 : length += myInternalDistance;
180 : } else {
181 : // use lower bound for distance to pass the intersection
182 48194 : for (ConstMSEdgeVector::const_iterator i = myRoute.begin(); i != endIt - 1; ++i) {
183 36103 : const MSEdge* fromEdge = *i;
184 36103 : const MSEdge* toEdge = *(i + 1);
185 36103 : const MSLane* from = getSidewalk<MSEdge, MSLane>(fromEdge);
186 36103 : const MSLane* to = getSidewalk<MSEdge, MSLane>(toEdge);
187 : Position fromPos;
188 : Position toPos;
189 36103 : if (from != nullptr && to != nullptr) {
190 36103 : if (fromEdge->getToJunction() == toEdge->getFromJunction()) {
191 36055 : fromPos = from->getShape().back();
192 36055 : toPos = to->getShape().front();
193 48 : } else if (fromEdge->getToJunction() == toEdge->getToJunction()) {
194 26 : fromPos = from->getShape().back();
195 26 : toPos = to->getShape().back();
196 22 : } else if (fromEdge->getFromJunction() == toEdge->getFromJunction()) {
197 10 : fromPos = from->getShape().front();
198 10 : toPos = to->getShape().front();
199 12 : } else if (fromEdge->getFromJunction() == toEdge->getToJunction()) {
200 12 : fromPos = from->getShape().front();
201 12 : toPos = to->getShape().back();
202 : }
203 : //std::cout << " from=" << from->getID() << " to=" << to->getID() << " junctionLength=" << fromPos.distanceTo2D(toPos) << "\n";
204 36103 : length += fromPos.distanceTo2D(toPos);
205 : }
206 : }
207 : }
208 : }
209 : // determine walking direction for depart and arrival
210 276455 : int dummy = 0;
211 276455 : const int departFwdArrivalDir = MSPModel::canTraverse(MSPModel::FORWARD, myRoute, dummy);
212 276455 : const int departBwdArrivalDir = MSPModel::canTraverse(MSPModel::BACKWARD, myRoute, dummy);
213 276455 : const bool mayStartForward = departFwdArrivalDir != MSPModel::UNDEFINED_DIRECTION;
214 276455 : const bool mayStartBackward = departBwdArrivalDir != MSPModel::UNDEFINED_DIRECTION;
215 276455 : const double arrivalPos = partial && myArrived < 0 ? getEdgePos(SIMSTEP) : myArrivalPos;
216 276455 : const double lengthFwd = (length - myDepartPos - (
217 : departFwdArrivalDir == MSPModel::BACKWARD
218 276455 : ? arrivalPos
219 276455 : : myRoute.back()->getLength() - arrivalPos));
220 276455 : const double lengthBwd = (length - (myRoute.front()->getLength() - myDepartPos) - (
221 : departBwdArrivalDir == MSPModel::BACKWARD
222 276455 : ? arrivalPos
223 276455 : : myRoute.back()->getLength() - arrivalPos));
224 : //std::cout << " length=" << length << " lengthFwd=" << lengthFwd << " lengthBwd=" << lengthBwd << " mayStartForward=" << mayStartForward << " mayStartBackward=" << mayStartBackward << "\n";
225 :
226 276455 : if (myRoute.size() == 1) {
227 134393 : if (myDepartPos > myArrivalPos) {
228 : length = lengthBwd;
229 : } else {
230 : length = lengthFwd;
231 : }
232 : } else {
233 142062 : if (mayStartForward && mayStartBackward) {
234 11035 : length = lengthFwd < lengthBwd ? lengthFwd : lengthBwd;
235 131027 : } else if (mayStartForward) {
236 : length = lengthFwd;
237 49204 : } else if (mayStartBackward) {
238 : length = lengthBwd;
239 : } else {
240 : length = lengthFwd;
241 : }
242 : }
243 : //std::cout << SIMTIME << " route=" << toString(myRoute)
244 : // << " depPos=" << myDepartPos << " arPos=" << myArrivalPos
245 : // << " dFwdADir=" << departFwdArrivalDir
246 : // << " dBwdADir=" << departBwdArrivalDir
247 : // << " lengthFwd=" << lengthFwd
248 : // << " lengthBwd=" << lengthBwd
249 : // << "\n";
250 :
251 276455 : return MAX2(POSITION_EPS, length);
252 : }
253 :
254 :
255 : SUMOTime
256 111040 : MSStageWalking::getTimeLoss(const MSTransportable* transportable) const {
257 111040 : SUMOTime timeLoss = myArrived == -1 ? 0 : getDuration() - TIME2STEPS(walkDistance(true) / getMaxSpeed(transportable));
258 111040 : if (timeLoss < 0 && timeLoss > TIME2STEPS(-0.1)) {
259 : // avoid negative timeLoss due to rounding errors
260 : timeLoss = 0;
261 : }
262 111040 : return timeLoss;
263 : }
264 :
265 :
266 : void
267 55520 : MSStageWalking::tripInfoOutput(OutputDevice& os, const MSTransportable* const person) const {
268 55520 : if (!myWarnedInvalidTripinfo && MSNet::getInstance()->getPersonControl().getMovementModel()->usingShortcuts()) {
269 0 : WRITE_WARNING(TL("The pedestrian model uses infrastructure which is not in the network, timeLoss and routeLength may be invalid."));
270 0 : myWarnedInvalidTripinfo = true;
271 : }
272 55520 : const double distance = walkDistance(true);
273 55520 : const double maxSpeed = getMaxSpeed(person);
274 55520 : const SUMOTime duration = myArrived - myDeparted;
275 55520 : const SUMOTime timeLoss = getTimeLoss(person);
276 55520 : MSDevice_Tripinfo::addPedestrianData(distance, duration, timeLoss);
277 55520 : os.openTag("walk");
278 55520 : os.writeAttr("depart", myDeparted >= 0 ? time2string(myDeparted) : "-1");
279 55520 : os.writeAttr("departPos", myDepartPos);
280 55520 : os.writeAttr("arrival", myArrived >= 0 ? time2string(myArrived) : "-1");
281 55520 : os.writeAttr("arrivalPos", myArrived >= 0 ? toString(myArrivalPos) : "-1");
282 110775 : os.writeAttr("duration", myDeparted < 0 ? "-1" :
283 55255 : time2string(myArrived >= 0 ? duration : MSNet::getInstance()->getCurrentTimeStep() - myDeparted));
284 55520 : os.writeAttr("routeLength", myArrived >= 0 ? toString(distance) : "-1");
285 55520 : os.writeAttr("timeLoss", time2string(timeLoss));
286 55520 : os.writeAttr("maxSpeed", maxSpeed);
287 55520 : os.writeAttr("waitingTime", time2string(getTotalWaitingTime()));
288 55520 : os.closeTag();
289 55520 : }
290 :
291 :
292 : void
293 2425 : MSStageWalking::routeOutput(const bool /* isPerson */, OutputDevice& os, const bool withRouteLength, const MSStage* const /* previous */, const bool withTiming, const bool saveState) const {
294 2425 : os.openTag("walk").writeAttr(SUMO_ATTR_EDGES, myRoute);
295 2425 : std::string comment = "";
296 2425 : if (myDestinationStop != nullptr) {
297 453 : os.writeAttr(toString(myDestinationStop->getElement()), myDestinationStop->getID());
298 453 : if (myDestinationStop->getMyName() != "") {
299 231 : comment = " <!-- " + StringUtils::escapeXML(myDestinationStop->getMyName(), true) + " -->";
300 : }
301 1972 : } else if (wasSet(VEHPARS_ARRIVALPOS_SET)) {
302 166 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, myArrivalPos);
303 : }
304 2425 : if (myWalkingTime > 0) {
305 0 : os.writeAttr(SUMO_ATTR_DURATION, time2string(myWalkingTime));
306 2425 : } else if (mySpeed > 0) {
307 24 : os.writeAttr(SUMO_ATTR_SPEED, mySpeed);
308 : }
309 2425 : if (withRouteLength) {
310 36 : if (myDeparted >= 0) {
311 36 : os.writeAttr("routeLength", walkDistance(true));
312 : } else {
313 0 : os.writeAttr("routeLength", "-1");
314 : }
315 : }
316 2425 : if (myExitTimes != nullptr) {
317 : std::vector<std::string> exits;
318 562 : for (SUMOTime t : *myExitTimes) {
319 780 : exits.push_back(time2string(t));
320 : }
321 344 : std::vector<std::string> missing(MAX2(0, (int)myRoute.size() - (int)myExitTimes->size()), "-1");
322 172 : exits.insert(exits.end(), missing.begin(), missing.end());
323 172 : os.writeAttr("exitTimes", exits);
324 172 : }
325 2425 : if (withTiming) {
326 194 : os.writeAttr(SUMO_ATTR_STARTED, myDeparted >= 0 ? time2string(myDeparted) : "-1");
327 194 : os.writeAttr(SUMO_ATTR_ENDED, myArrived >= 0 ? time2string(myArrived) : "-1");
328 : }
329 4850 : if (OptionsCont::getOptions().getBool("vehroute-output.cost")) {
330 105 : os.writeAttr(SUMO_ATTR_COST, getCosts());
331 : }
332 2425 : if (saveState && getTotalWaitingTime() > 0) {
333 2 : os.writeAttr("waitingTime", time2string(getTotalWaitingTime()));
334 : }
335 2425 : os.closeTag(comment);
336 2425 : }
337 :
338 :
339 : bool
340 2858144 : MSStageWalking::moveToNextEdge(MSTransportable* person, SUMOTime currentTime, int prevDir, MSEdge* nextInternal, const bool isReplay) {
341 2858144 : ((MSEdge*)getEdge())->removeTransportable(person);
342 2858144 : const MSLane* lane = getSidewalk<MSEdge, MSLane>(getEdge());
343 : const bool arrived = myRouteStep == myRoute.end() - 1;
344 2858144 : if (lane != nullptr) {
345 2836265 : const double tl = person->getVehicleType().getLength();
346 : const double lastPos = (arrived
347 2836265 : ? (prevDir == MSPModel::FORWARD
348 4144288 : ? getArrivalPos() + tl
349 1983758 : : getArrivalPos() - tl)
350 675735 : : person->getPositionOnLane());
351 2836265 : activateLeaveReminders(person, lane, lastPos, currentTime, arrived);
352 : }
353 2858144 : if (myExitTimes != nullptr && nextInternal == nullptr) {
354 374 : myExitTimes->push_back(currentTime);
355 : }
356 : myMoveReminders.clear();
357 2858144 : myLastEdgeEntryTime = currentTime;
358 : //std::cout << SIMTIME << " moveToNextEdge person=" << person->getID() << "\n";
359 2858144 : if (myCurrentInternalEdge != nullptr) {
360 365596 : myInternalDistance += (myPState->getPathLength() == 0 ? myCurrentInternalEdge->getLength() : myPState->getPathLength());
361 : }
362 2858144 : if (arrived) {
363 2160556 : MSPerson* p = dynamic_cast<MSPerson*>(person);
364 2160556 : if (!isReplay && p->hasInfluencer() && p->getInfluencer().isRemoteControlled()) {
365 1941782 : myCurrentInternalEdge = nextInternal;
366 1941782 : ((MSEdge*) getEdge())->addTransportable(person);
367 1941782 : return false;
368 : }
369 218774 : if (myDestinationStop != nullptr) {
370 33582 : myDestinationStop->addTransportable(person);
371 : }
372 218774 : if (isReplay) {
373 : // cannot do this in the replay device because the person might get deleted below
374 40 : MSNet::getInstance()->getPersonControl().getMovementModel()->remove(myPState);
375 : }
376 218774 : if (!person->proceed(MSNet::getInstance(), currentTime)) {
377 212253 : MSNet::getInstance()->getPersonControl().erase(person);
378 : }
379 : //std::cout << " end walk. myRouteStep=" << (*myRouteStep)->getID() << "\n";
380 218774 : return true;
381 : } else {
382 697588 : if (nextInternal == nullptr) {
383 : ++myRouteStep;
384 : }
385 697588 : myCurrentInternalEdge = nextInternal;
386 697588 : ((MSEdge*) getEdge())->addTransportable(person);
387 697588 : return false;
388 : }
389 : }
390 :
391 :
392 : void
393 2866172 : MSStageWalking::activateEntryReminders(MSTransportable* person, const bool isDepart) {
394 2866172 : const MSLane* const nextLane = getSidewalk<MSEdge, MSLane>(getEdge());
395 2866172 : if (nextLane != nullptr) {
396 3686446 : for (MSMoveReminder* const rem : nextLane->getMoveReminders()) {
397 1381615 : if (rem->notifyEnter(*person, isDepart ? MSMoveReminder::NOTIFICATION_DEPARTED : MSMoveReminder::NOTIFICATION_JUNCTION, nextLane)) {
398 18564 : myMoveReminders.push_back(rem);
399 : }
400 : }
401 : }
402 5732344 : if (hasParameter("rerouter")) {
403 : double minDist = std::numeric_limits<double>::max();
404 : MSTriggeredRerouter* nearest = nullptr;
405 190 : for (MSMoveReminder* const rem : myMoveReminders) {
406 120 : MSTriggeredRerouter* rerouter = dynamic_cast<MSTriggeredRerouter*>(rem);
407 120 : if (rerouter != nullptr) {
408 70 : const double dist2 = rerouter->getPosition().distanceSquaredTo2D(person->getPosition());
409 70 : if (dist2 < minDist) {
410 : nearest = rerouter;
411 : minDist = dist2;
412 : }
413 : }
414 : }
415 70 : if (nearest != nullptr) {
416 70 : nearest->triggerRouting(*person, MSMoveReminder::NOTIFICATION_JUNCTION);
417 : }
418 : // TODO maybe removal of the reminders? Or can we rely on movetonextedge to clean everything up?
419 : }
420 2866172 : }
421 :
422 :
423 : void
424 0 : MSStageWalking::activateMoveReminders(MSTransportable* person, double oldPos, double newPos, double newSpeed) {
425 0 : for (std::vector<MSMoveReminder*>::iterator rem = myMoveReminders.begin(); rem != myMoveReminders.end();) {
426 0 : if ((*rem)->notifyMove(*person, oldPos, newPos, newSpeed)) {
427 : ++rem;
428 : } else {
429 0 : rem = myMoveReminders.erase(rem);
430 : }
431 : }
432 0 : }
433 :
434 :
435 : void
436 2836265 : MSStageWalking::activateLeaveReminders(MSTransportable* person, const MSLane* lane, double lastPos, SUMOTime t, bool arrived) {
437 2836265 : MSMoveReminder::Notification notification = arrived ? MSMoveReminder::NOTIFICATION_ARRIVED : MSMoveReminder::NOTIFICATION_JUNCTION;
438 2853705 : for (MSMoveReminder* const rem : myMoveReminders) {
439 17440 : rem->updateDetector(*person, 0.0, lane->getLength(), myLastEdgeEntryTime, t, t, true);
440 17440 : rem->notifyLeave(*person, lastPos, notification);
441 : }
442 2836265 : }
443 :
444 :
445 : int
446 449288 : MSStageWalking::getRoutePosition() const {
447 449288 : return (int)(myRouteStep - myRoute.begin());
448 : }
449 :
450 :
451 : double
452 22605730 : MSStageWalking::getMaxSpeed(const MSTransportable* const person) const {
453 22605730 : return mySpeed >= 0 ? mySpeed : person->getMaxSpeed();
454 : }
455 :
456 : std::string
457 0 : MSStageWalking::getStageSummary(const bool /* isPerson */) const {
458 0 : const std::string dest = (getDestinationStop() == nullptr ?
459 0 : " edge '" + getDestination()->getID() + "'" :
460 0 : " stop '" + getDestinationStop()->getID() + "'" + (
461 0 : getDestinationStop()->getMyName() != "" ? " (" + getDestinationStop()->getMyName() + ")" : ""));
462 0 : return "walking to " + dest;
463 : }
464 :
465 :
466 : void
467 32 : MSStageWalking::saveState(std::ostringstream& out, MSTransportable* /*transportable*/) {
468 64 : out << " " << myDeparted << " " << (myRouteStep - myRoute.begin()) << " " << myLastEdgeEntryTime;
469 32 : if (myExitTimes != nullptr) {
470 12 : out << " " << myExitTimes->size();
471 18 : for (SUMOTime t : *myExitTimes) {
472 : out << " " << t;
473 : }
474 : } else {
475 20 : out << " " << -1;
476 : }
477 32 : myPState->saveState(out);
478 32 : }
479 :
480 :
481 : void
482 36 : MSStageWalking::loadState(MSTransportable* transportable, std::istringstream& state) {
483 : int stepIdx;
484 36 : state >> myDeparted >> stepIdx >> myLastEdgeEntryTime;
485 : int exitTimesSize;
486 36 : state >> exitTimesSize;
487 36 : if (exitTimesSize >= 0) {
488 12 : myExitTimes = new std::vector<SUMOTime>();
489 : SUMOTime t;
490 18 : for (int i = 0; i < exitTimesSize; i++) {
491 : state >> t;
492 6 : myExitTimes->push_back(t);
493 : }
494 24 : if (!OptionsCont::getOptions().getBool("vehroute-output.exit-times")) {
495 0 : delete myExitTimes;
496 0 : myExitTimes = nullptr;
497 : }
498 : }
499 36 : myRouteStep = myRoute.begin() + stepIdx;
500 36 : myPState = MSNet::getInstance()->getPersonControl().getMovementModel()->loadState(transportable, this, state);
501 36 : if (myPState->getLane() && !myPState->getLane()->isNormal()) {
502 1 : myCurrentInternalEdge = &myPState->getLane()->getEdge();
503 1 : myCurrentInternalEdge->addTransportable(transportable);
504 : } else {
505 35 : (*myRouteStep)->addTransportable(transportable);
506 : }
507 36 : }
508 :
509 :
510 : /****************************************************************************/
|