Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file 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 293142 : 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 293142 : const std::string& routeID) :
67 : MSStageMoving(MSStageType::WALKING, route, routeID, toStop, speed, departPos, arrivalPos, departPosLat, departLane),
68 293142 : myWalkingTime(walkingTime),
69 293142 : myExitTimes(nullptr),
70 293142 : myInternalDistance(0) {
71 293142 : myDepartPos = SUMOVehicleParameter::interpretEdgePos(departPos, route.front()->getLength(), SUMO_ATTR_DEPARTPOS,
72 586284 : "person '" + personID + "' walking from edge '" + route.front()->getID() + "'");
73 293142 : myArrivalPos = SUMOVehicleParameter::interpretEdgePos(arrivalPos, route.back()->getLength(), SUMO_ATTR_ARRIVALPOS,
74 586284 : "person '" + personID + "' walking to edge '" + route.back()->getID() + "'");
75 293142 : if (walkingTime > 0) {
76 96 : mySpeed = computeAverageSpeed();
77 : }
78 293142 : }
79 :
80 :
81 586206 : MSStageWalking::~MSStageWalking() {
82 293229 : delete myExitTimes;
83 586206 : }
84 :
85 :
86 : MSStage*
87 22311 : MSStageWalking::clone() const {
88 22311 : std::vector<const MSEdge*> route = myRoute;
89 22311 : double departPos = myDepartPos;
90 22311 : double arrivalPos = myArrivalPos;
91 22311 : int departLane = myDepartLane;
92 22311 : 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 22311 : MSStage* clon = new MSStageWalking("dummyID", route, myDestinationStop, myWalkingTime, mySpeed, departPos, arrivalPos, myDepartPosLat, departLane, myRouteID);
108 22311 : clon->setParameters(*this);
109 22311 : return clon;
110 : }
111 :
112 :
113 : void
114 292632 : MSStageWalking::proceed(MSNet* net, MSTransportable* person, SUMOTime now, MSStage* previous) {
115 292632 : myDeparted = now;
116 292632 : myRouteStep = myRoute.begin();
117 292632 : myLastEdgeEntryTime = now;
118 292632 : if (myWalkingTime == 0) {
119 0 : if (!person->proceed(net, now)) {
120 0 : MSNet::getInstance()->getPersonControl().erase(person);
121 : }
122 0 : return;
123 : }
124 292632 : if (previous->getEdgePos(now) >= 0 && previous->getEdge() == *myRouteStep) {
125 : // we need to adapt to the arrival position of the vehicle unless we have an explicit access
126 290475 : myDepartPos = previous->getEdgePos(now);
127 290475 : const OptionsCont& oc = OptionsCont::getOptions();
128 580950 : const std::string model = oc.getString("pedestrian.model");
129 290475 : if (model != "jupedsim") {
130 290475 : if (previous->getStageType() == MSStageType::ACCESS) {
131 2356 : const Position& lastPos = previous->getPosition(now);
132 : // making a wild guess on where we actually want to depart laterally
133 2356 : const double possibleDepartPosLat = lastPos.distanceTo(previous->getEdgePosition(previous->getEdge(), myDepartPos, 0.));
134 2356 : const Position& newPos = previous->getEdgePosition(previous->getEdge(), myDepartPos, -possibleDepartPosLat); // Minus sign is here for legacy reasons.
135 2356 : if (lastPos.almostSame(newPos)) {
136 522 : myDepartPosLat = possibleDepartPosLat;
137 3668 : } else if (lastPos.almostSame(previous->getEdgePosition(previous->getEdge(), myDepartPos, possibleDepartPosLat))) {
138 : // maybe the other side of the street
139 514 : myDepartPosLat = -possibleDepartPosLat;
140 : }
141 : }
142 : } else {
143 0 : myDepartPosLat = previous->getEdgePosLat(now);
144 : }
145 290475 : if (myWalkingTime > 0) {
146 70 : mySpeed = computeAverageSpeed();
147 : }
148 : }
149 292632 : MSTransportableControl& pControl = net->getPersonControl();
150 292632 : myPState = pControl.getMovementModel()->add(person, this, now);
151 292628 : if (myPState == nullptr) {
152 8 : pControl.erase(person);
153 8 : return;
154 : }
155 292620 : if (previous->getStageType() != MSStageType::WALKING || previous->getEdge() != getEdge()) {
156 : // we only need new move reminders if we are walking a different edge (else it is probably a rerouting)
157 291089 : activateEntryReminders(person, true);
158 : }
159 585240 : if (OptionsCont::getOptions().getBool("vehroute-output.exit-times")) {
160 126 : myExitTimes = new std::vector<SUMOTime>();
161 : }
162 292620 : (*myRouteStep)->addTransportable(person);
163 : }
164 :
165 :
166 : void
167 1579 : MSStageWalking::abort(MSTransportable*) {
168 1579 : MSNet::getInstance()->getPersonControl().getMovementModel()->remove(myPState);
169 1579 : }
170 :
171 :
172 : void
173 23 : MSStageWalking::setSpeed(double speed) {
174 23 : mySpeed = speed;
175 23 : }
176 :
177 :
178 : double
179 166 : MSStageWalking::computeAverageSpeed() const {
180 166 : return walkDistance() / STEPS2TIME(myWalkingTime + 1); // avoid systematic rounding errors
181 : }
182 :
183 :
184 : bool
185 1110602 : MSPerson::isJammed() const {
186 1110602 : MSStageWalking* stage = dynamic_cast<MSStageWalking*>(getCurrentStage());
187 1110602 : if (stage != nullptr) {
188 1110602 : return stage->getPState()->isJammed();
189 : }
190 : return false;
191 : }
192 :
193 :
194 : double
195 239848 : MSStageWalking::walkDistance(bool partial) const {
196 : double length = 0;
197 239848 : auto endIt = partial && myArrived < 0 ? myRouteStep + 1 : myRoute.end();
198 660815 : for (ConstMSEdgeVector::const_iterator i = myRoute.begin(); i != endIt; ++i) {
199 420967 : length += (*i)->getLength();
200 : }
201 239848 : if (myRoute.size() > 1 && MSNet::getInstance()->getPersonControl().getMovementModel()->usingInternalLanes()) {
202 91794 : if (myInternalDistance > 0) {
203 79715 : length += myInternalDistance;
204 : } else {
205 : // use lower bound for distance to pass the intersection
206 48154 : for (ConstMSEdgeVector::const_iterator i = myRoute.begin(); i != endIt - 1; ++i) {
207 36075 : const MSEdge* fromEdge = *i;
208 36075 : const MSEdge* toEdge = *(i + 1);
209 36075 : const MSLane* from = getSidewalk<MSEdge, MSLane>(fromEdge);
210 36075 : const MSLane* to = getSidewalk<MSEdge, MSLane>(toEdge);
211 : Position fromPos;
212 : Position toPos;
213 36075 : if (from != nullptr && to != nullptr) {
214 36075 : if (fromEdge->getToJunction() == toEdge->getFromJunction()) {
215 36025 : fromPos = from->getShape().back();
216 36025 : toPos = to->getShape().front();
217 50 : } else if (fromEdge->getToJunction() == toEdge->getToJunction()) {
218 25 : fromPos = from->getShape().back();
219 25 : toPos = to->getShape().back();
220 25 : } else if (fromEdge->getFromJunction() == toEdge->getFromJunction()) {
221 10 : fromPos = from->getShape().front();
222 10 : toPos = to->getShape().front();
223 15 : } else if (fromEdge->getFromJunction() == toEdge->getToJunction()) {
224 15 : fromPos = from->getShape().front();
225 15 : toPos = to->getShape().back();
226 : }
227 : //std::cout << " from=" << from->getID() << " to=" << to->getID() << " junctionLength=" << fromPos.distanceTo2D(toPos) << "\n";
228 36075 : length += fromPos.distanceTo2D(toPos);
229 : }
230 : }
231 : }
232 : }
233 : // determine walking direction for depart and arrival
234 239848 : int dummy = 0;
235 239848 : const int departFwdArrivalDir = MSPModel::canTraverse(MSPModel::FORWARD, myRoute, dummy);
236 239848 : const int departBwdArrivalDir = MSPModel::canTraverse(MSPModel::BACKWARD, myRoute, dummy);
237 239848 : const bool mayStartForward = departFwdArrivalDir != MSPModel::UNDEFINED_DIRECTION;
238 239848 : const bool mayStartBackward = departBwdArrivalDir != MSPModel::UNDEFINED_DIRECTION;
239 239848 : const double arrivalPos = partial && myArrived < 0 ? getEdgePos(SIMSTEP) : myArrivalPos;
240 239848 : const double lengthFwd = (length - myDepartPos - (
241 : departFwdArrivalDir == MSPModel::BACKWARD
242 239848 : ? arrivalPos
243 239848 : : myRoute.back()->getLength() - arrivalPos));
244 239848 : const double lengthBwd = (length - (myRoute.front()->getLength() - myDepartPos) - (
245 : departBwdArrivalDir == MSPModel::BACKWARD
246 239848 : ? arrivalPos
247 239848 : : myRoute.back()->getLength() - arrivalPos));
248 : //std::cout << " length=" << length << " lengthFwd=" << lengthFwd << " lengthBwd=" << lengthBwd << " mayStartForward=" << mayStartForward << " mayStartBackward=" << mayStartBackward << "\n";
249 :
250 239848 : if (myRoute.size() == 1) {
251 129252 : if (myDepartPos > myArrivalPos) {
252 : length = lengthBwd;
253 : } else {
254 : length = lengthFwd;
255 : }
256 : } else {
257 110596 : if (mayStartForward && mayStartBackward) {
258 14935 : length = lengthFwd < lengthBwd ? lengthFwd : lengthBwd;
259 95661 : } else if (mayStartForward) {
260 : length = lengthFwd;
261 14816 : } else if (mayStartBackward) {
262 : length = lengthBwd;
263 : } else {
264 : length = lengthFwd;
265 : }
266 : }
267 : //std::cout << SIMTIME << " route=" << toString(myRoute)
268 : // << " depPos=" << myDepartPos << " arPos=" << myArrivalPos
269 : // << " dFwdADir=" << departFwdArrivalDir
270 : // << " dBwdADir=" << departBwdArrivalDir
271 : // << " lengthFwd=" << lengthFwd
272 : // << " lengthBwd=" << lengthBwd
273 : // << "\n";
274 :
275 239848 : return MAX2(POSITION_EPS, length);
276 : }
277 :
278 :
279 : SUMOTime
280 95854 : MSStageWalking::getTimeLoss(const MSTransportable* transportable) const {
281 95854 : SUMOTime timeLoss = myArrived == -1 ? 0 : getDuration() - TIME2STEPS(walkDistance(true) / getMaxSpeed(transportable));
282 95854 : if (timeLoss < 0 && timeLoss > TIME2STEPS(-0.1)) {
283 : // avoid negative timeLoss due to rounding errors
284 : timeLoss = 0;
285 : }
286 95854 : return timeLoss;
287 : }
288 :
289 :
290 : void
291 47927 : MSStageWalking::tripInfoOutput(OutputDevice& os, const MSTransportable* const person) const {
292 47927 : if (!myWarnedInvalidTripinfo && MSNet::getInstance()->getPersonControl().getMovementModel()->usingShortcuts()) {
293 0 : WRITE_WARNING(TL("The pedestrian model uses infrastructure which is not in the network, timeLoss and routeLength may be invalid."));
294 0 : myWarnedInvalidTripinfo = true;
295 : }
296 47927 : const double distance = walkDistance(true);
297 47927 : const double maxSpeed = getMaxSpeed(person);
298 47927 : const SUMOTime duration = myArrived - myDeparted;
299 47927 : const SUMOTime timeLoss = getTimeLoss(person);
300 47927 : MSDevice_Tripinfo::addPedestrianData(distance, duration, timeLoss);
301 47927 : os.openTag("walk");
302 95854 : os.writeAttr("depart", myDeparted >= 0 ? time2string(myDeparted) : "-1");
303 47927 : os.writeAttr("departPos", myDepartPos);
304 95854 : os.writeAttr("arrival", myArrived >= 0 ? time2string(myArrived) : "-1");
305 95854 : os.writeAttr("arrivalPos", myArrived >= 0 ? toString(myArrivalPos) : "-1");
306 143767 : os.writeAttr("duration", myDeparted < 0 ? "-1" :
307 47913 : time2string(myArrived >= 0 ? duration : MSNet::getInstance()->getCurrentTimeStep() - myDeparted));
308 95854 : os.writeAttr("routeLength", myArrived >= 0 ? toString(distance) : "-1");
309 95854 : os.writeAttr("timeLoss", time2string(timeLoss));
310 47927 : os.writeAttr("maxSpeed", maxSpeed);
311 47927 : os.closeTag();
312 47927 : }
313 :
314 :
315 : void
316 2113 : MSStageWalking::routeOutput(const bool /* isPerson */, OutputDevice& os, const bool withRouteLength, const MSStage* const /* previous */) const {
317 2113 : os.openTag("walk").writeAttr(SUMO_ATTR_EDGES, myRoute);
318 2113 : std::string comment = "";
319 2113 : if (myDestinationStop != nullptr) {
320 311 : os.writeAttr(toString(myDestinationStop->getElement()), myDestinationStop->getID());
321 622 : if (myDestinationStop->getMyName() != "") {
322 124 : comment = " <!-- " + StringUtils::escapeXML(myDestinationStop->getMyName(), true) + " -->";
323 : }
324 1802 : } else if (wasSet(VEHPARS_ARRIVALPOS_SET)) {
325 182 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, myArrivalPos);
326 : }
327 2113 : if (myWalkingTime > 0) {
328 0 : os.writeAttr(SUMO_ATTR_DURATION, time2string(myWalkingTime));
329 2113 : } else if (mySpeed > 0) {
330 24 : os.writeAttr(SUMO_ATTR_SPEED, mySpeed);
331 : }
332 2113 : if (withRouteLength) {
333 36 : if (myDeparted >= 0) {
334 72 : os.writeAttr("routeLength", walkDistance(true));
335 : } else {
336 0 : os.writeAttr("routeLength", "-1");
337 : }
338 : }
339 2113 : if (myExitTimes != nullptr) {
340 : std::vector<std::string> exits;
341 444 : for (SUMOTime t : *myExitTimes) {
342 636 : exits.push_back(time2string(t));
343 : }
344 252 : std::vector<std::string> missing(MAX2(0, (int)myRoute.size() - (int)myExitTimes->size()), "-1");
345 126 : exits.insert(exits.end(), missing.begin(), missing.end());
346 126 : os.writeAttr("exitTimes", exits);
347 126 : os.writeAttr(SUMO_ATTR_STARTED, myDeparted >= 0 ? time2string(myDeparted) : "-1");
348 126 : os.writeAttr(SUMO_ATTR_ENDED, myArrived >= 0 ? time2string(myArrived) : "-1");
349 126 : }
350 4226 : if (OptionsCont::getOptions().getBool("vehroute-output.cost")) {
351 210 : os.writeAttr(SUMO_ATTR_COST, getCosts());
352 : }
353 2113 : os.closeTag(comment);
354 2113 : }
355 :
356 :
357 : bool
358 983336 : MSStageWalking::moveToNextEdge(MSTransportable* person, SUMOTime currentTime, int prevDir, MSEdge* nextInternal) {
359 983336 : ((MSEdge*)getEdge())->removeTransportable(person);
360 983336 : const MSLane* lane = getSidewalk<MSEdge, MSLane>(getEdge());
361 : const bool arrived = myRouteStep == myRoute.end() - 1;
362 983336 : if (lane != nullptr) {
363 983214 : const double tl = person->getVehicleType().getLength();
364 : const double lastPos = (arrived
365 983214 : ? (prevDir == MSPModel::FORWARD
366 316852 : ? getArrivalPos() + tl
367 27790 : : getArrivalPos() - tl)
368 694152 : : person->getPositionOnLane());
369 983214 : activateLeaveReminders(person, lane, lastPos, currentTime, arrived);
370 : }
371 983336 : if (myExitTimes != nullptr && nextInternal == nullptr) {
372 318 : myExitTimes->push_back(currentTime);
373 : }
374 : myMoveReminders.clear();
375 983336 : myLastEdgeEntryTime = currentTime;
376 : //std::cout << SIMTIME << " moveToNextEdge person=" << person->getID() << "\n";
377 983336 : if (myCurrentInternalEdge != nullptr) {
378 261816 : myInternalDistance += (myPState->getPathLength() == 0 ? myCurrentInternalEdge->getLength() : myPState->getPathLength());
379 : }
380 983336 : if (arrived) {
381 289088 : MSPerson* p = dynamic_cast<MSPerson*>(person);
382 289088 : if (p->hasInfluencer() && p->getInfluencer().isRemoteControlled()) {
383 0 : myCurrentInternalEdge = nextInternal;
384 0 : ((MSEdge*) getEdge())->addTransportable(person);
385 0 : return false;
386 : }
387 289088 : if (myDestinationStop != nullptr) {
388 36555 : myDestinationStop->addTransportable(person);
389 : }
390 289088 : if (!person->proceed(MSNet::getInstance(), currentTime)) {
391 282734 : MSNet::getInstance()->getPersonControl().erase(person);
392 : }
393 : //std::cout << " end walk. myRouteStep=" << (*myRouteStep)->getID() << "\n";
394 289088 : return true;
395 : } else {
396 694248 : if (nextInternal == nullptr) {
397 : ++myRouteStep;
398 : }
399 694248 : myCurrentInternalEdge = nextInternal;
400 694248 : ((MSEdge*) getEdge())->addTransportable(person);
401 694248 : return false;
402 : }
403 : }
404 :
405 :
406 : void
407 983214 : MSStageWalking::activateLeaveReminders(MSTransportable* person, const MSLane* lane, double lastPos, SUMOTime t, bool arrived) {
408 983214 : MSMoveReminder::Notification notification = arrived ? MSMoveReminder::NOTIFICATION_ARRIVED : MSMoveReminder::NOTIFICATION_JUNCTION;
409 1058611 : for (MSMoveReminder* const rem : myMoveReminders) {
410 75397 : rem->updateDetector(*person, 0.0, lane->getLength(), myLastEdgeEntryTime, t, t, true);
411 75397 : rem->notifyLeave(*person, lastPos, notification);
412 : }
413 983214 : }
414 :
415 :
416 : void
417 985660 : MSStageWalking::activateEntryReminders(MSTransportable* person, const bool isDepart) {
418 985660 : const MSLane* const nextLane = getSidewalk<MSEdge, MSLane>(getEdge());
419 985660 : if (nextLane != nullptr) {
420 1065702 : for (MSMoveReminder* const rem : nextLane->getMoveReminders()) {
421 153560 : if (rem->notifyEnter(*person, isDepart ? MSMoveReminder::NOTIFICATION_DEPARTED : MSMoveReminder::NOTIFICATION_JUNCTION, nextLane)) {
422 76522 : myMoveReminders.push_back(rem);
423 : }
424 : }
425 : }
426 1971320 : if (hasParameter("rerouter")) {
427 : double minDist = std::numeric_limits<double>::max();
428 : MSTriggeredRerouter* nearest = nullptr;
429 190 : for (MSMoveReminder* const rem : myMoveReminders) {
430 120 : MSTriggeredRerouter* rerouter = dynamic_cast<MSTriggeredRerouter*>(rem);
431 120 : if (rerouter != nullptr) {
432 70 : const double dist2 = rerouter->getPosition().distanceSquaredTo2D(person->getPosition());
433 70 : if (dist2 < minDist) {
434 : nearest = rerouter;
435 : minDist = dist2;
436 : }
437 : }
438 : }
439 70 : if (nearest != nullptr) {
440 70 : nearest->triggerRouting(*person, MSMoveReminder::NOTIFICATION_JUNCTION);
441 : }
442 : // TODO maybe removal of the reminders? Or can we rely on movetonextedge to clean everything up?
443 : }
444 985660 : }
445 :
446 :
447 : int
448 449430 : MSStageWalking::getRoutePosition() const {
449 449430 : return (int)(myRouteStep - myRoute.begin());
450 : }
451 :
452 :
453 : double
454 52195909 : MSStageWalking::getMaxSpeed(const MSTransportable* const person) const {
455 52195909 : return mySpeed >= 0 ? mySpeed : person->getMaxSpeed();
456 : }
457 :
458 : std::string
459 0 : MSStageWalking::getStageSummary(const bool /* isPerson */) const {
460 : const std::string dest = (getDestinationStop() == nullptr ?
461 0 : " edge '" + getDestination()->getID() + "'" :
462 0 : " stop '" + getDestinationStop()->getID() + "'" + (
463 0 : getDestinationStop()->getMyName() != "" ? " (" + getDestinationStop()->getMyName() + ")" : ""));
464 0 : return "walking to " + dest;
465 : }
466 :
467 :
468 : void
469 27 : MSStageWalking::saveState(std::ostringstream& out) {
470 54 : out << " " << myDeparted << " " << (myRouteStep - myRoute.begin()) << " " << myLastEdgeEntryTime;
471 27 : myPState->saveState(out);
472 27 : }
473 :
474 :
475 : void
476 33 : MSStageWalking::loadState(MSTransportable* transportable, std::istringstream& state) {
477 : int stepIdx;
478 33 : state >> myDeparted >> stepIdx >> myLastEdgeEntryTime;
479 33 : myRouteStep = myRoute.begin() + stepIdx;
480 33 : myPState = MSNet::getInstance()->getPersonControl().getMovementModel()->loadState(transportable, this, state);
481 33 : if (myPState->getLane() && !myPState->getLane()->isNormal()) {
482 2 : myCurrentInternalEdge = &myPState->getLane()->getEdge();
483 2 : myCurrentInternalEdge->addTransportable(transportable);
484 : } else {
485 31 : (*myRouteStep)->addTransportable(transportable);
486 : }
487 33 : }
488 :
489 :
490 : /****************************************************************************/
|