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 MSStageTrip.cpp
15 : /// @author Melanie Weber
16 : /// @author Andreas Kendziorra
17 : /// @author Michael Behrisch
18 : /// @author Jakob Erdmann
19 : /// @date Wed, 1 Jun 2022
20 : ///
21 : // An intermodal routing request (to be transformed into a sequence of walks and rides)
22 : /****************************************************************************/
23 : #include <config.h>
24 :
25 : #include <utils/common/StringTokenizer.h>
26 : #include <utils/geom/GeomHelper.h>
27 : #include <utils/options/OptionsCont.h>
28 : #include <utils/vehicle/SUMOVehicleParameter.h>
29 : #include <utils/router/PedestrianRouter.h>
30 : #include <utils/router/IntermodalRouter.h>
31 : #include <microsim/MSEdge.h>
32 : #include <microsim/MSLane.h>
33 : #include <microsim/MSNet.h>
34 : #include <microsim/MSStoppingPlace.h>
35 : #include <microsim/MSVehicleControl.h>
36 : #include <microsim/transportables/MSStageDriving.h>
37 : #include <microsim/transportables/MSStageWaiting.h>
38 : #include <microsim/transportables/MSStageWalking.h>
39 : #include <microsim/transportables/MSTransportable.h>
40 : #include <microsim/transportables/MSPerson.h>
41 : #include <microsim/transportables/MSStageTrip.h>
42 :
43 :
44 : // ===========================================================================
45 : // method definitions
46 : // ===========================================================================
47 :
48 : /* -------------------------------------------------------------------------
49 : * MSStageTrip - methods
50 : * ----------------------------------------------------------------------- */
51 230104 : MSStageTrip::MSStageTrip(const MSEdge* origin, MSStoppingPlace* fromStop,
52 : const MSEdge* destination, MSStoppingPlace* toStop,
53 : const SUMOTime duration, const SVCPermissions modeSet,
54 : const std::string& vTypes, const double speed, const double walkFactor,
55 : const std::string& group,
56 230104 : const double departPosLat, const bool hasArrivalPos, const double arrivalPos):
57 : MSStage(MSStageType::TRIP, destination, toStop, arrivalPos, 0.0, group),
58 230104 : myOrigin(origin),
59 230104 : myOriginStop(fromStop),
60 230104 : myDuration(duration),
61 230104 : myModeSet(modeSet),
62 230104 : myVTypes(vTypes),
63 230104 : mySpeed(speed),
64 230104 : myWalkFactor(walkFactor),
65 230104 : myDepartPosLat(departPosLat),
66 230104 : myHaveArrivalPos(hasArrivalPos) {
67 230104 : }
68 :
69 :
70 460198 : MSStageTrip::~MSStageTrip() {}
71 :
72 : MSStage*
73 221626 : MSStageTrip::clone() const {
74 221626 : MSStage* const clon = new MSStageTrip(myOrigin, const_cast<MSStoppingPlace*>(myOriginStop),
75 221626 : myDestination, myDestinationStop, myDuration,
76 221626 : myModeSet, myVTypes, mySpeed, myWalkFactor, myGroup,
77 221626 : myDepartPosLat, myHaveArrivalPos, myArrivalPos);
78 221626 : clon->setParameters(*this);
79 221626 : return clon;
80 : }
81 :
82 :
83 : Position
84 0 : MSStageTrip::getPosition(SUMOTime /* now */) const {
85 : // may be called concurrently while the trip is still being routed
86 0 : return getEdgePosition(myOrigin, myDepartPos, ROADSIDE_OFFSET * (MSGlobals::gLefthand ? -1 : 1));
87 : }
88 :
89 :
90 : double
91 0 : MSStageTrip::getAngle(SUMOTime /* now */) const {
92 : // may be called concurrently while the trip is still being routed
93 0 : return getEdgeAngle(myOrigin, myDepartPos) + M_PI / 2 * (MSGlobals::gLefthand ? -1 : 1);
94 : }
95 :
96 :
97 : const MSEdge*
98 416891 : MSStageTrip::getEdge() const {
99 416891 : return myOrigin;
100 : }
101 :
102 :
103 : double
104 408390 : MSStageTrip::getEdgePos(SUMOTime /* now */) const {
105 408390 : return myDepartPos;
106 : }
107 :
108 :
109 : std::vector<SUMOVehicle*>
110 205586 : MSStageTrip::getVehicles(MSVehicleControl& vehControl, MSTransportable* transportable, const MSEdge* origin) {
111 : std::vector<SUMOVehicleParameter*> pars;
112 411279 : for (StringTokenizer st(myVTypes); st.hasNext();) {
113 107 : pars.push_back(new SUMOVehicleParameter());
114 214 : pars.back()->vtypeid = st.next();
115 107 : pars.back()->parametersSet |= VEHPARS_VTYPE_SET;
116 107 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
117 214 : pars.back()->id = transportable->getID() + "_" + toString(pars.size() - 1);
118 205586 : }
119 205586 : if (pars.empty()) {
120 205483 : if ((myModeSet & SVC_PASSENGER) != 0) {
121 1599 : pars.push_back(new SUMOVehicleParameter());
122 3198 : pars.back()->id = transportable->getID() + "_0";
123 1599 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
124 : }
125 205483 : if ((myModeSet & SVC_TAXI) != 0) {
126 168 : pars.push_back(new SUMOVehicleParameter());
127 168 : pars.back()->vtypeid = DEFAULT_TAXITYPE_ID;
128 336 : pars.back()->id = transportable->getID() + "_taxi";
129 168 : pars.back()->line = "taxi";
130 : }
131 205483 : if ((myModeSet & SVC_BICYCLE) != 0) {
132 37 : pars.push_back(new SUMOVehicleParameter());
133 37 : pars.back()->vtypeid = DEFAULT_BIKETYPE_ID;
134 74 : pars.back()->id = transportable->getID() + "_b0";
135 37 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
136 : }
137 : }
138 411172 : ConstMSRoutePtr const routeDummy = std::make_shared<MSRoute>(transportable->getID() + "_0", ConstMSEdgeVector({ origin }), false, nullptr, StopParVector());
139 : std::vector<SUMOVehicle*> result;
140 207497 : for (SUMOVehicleParameter* vehPar : pars) {
141 1911 : const bool isTaxi = vehPar->vtypeid == DEFAULT_TAXITYPE_ID && vehPar->line == "taxi";
142 1911 : if (myDepartPos != 0) {
143 81 : vehPar->departPosProcedure = DepartPosDefinition::GIVEN;
144 81 : vehPar->departPos = myDepartPos;
145 81 : vehPar->parametersSet |= VEHPARS_DEPARTPOS_SET;
146 : }
147 1911 : vehPar->parametersSet |= VEHPARS_ARRIVALPOS_SET;
148 1911 : vehPar->arrivalPosProcedure = ArrivalPosDefinition::GIVEN;
149 1911 : vehPar->parametersSet |= VEHPARS_ARRIVALSPEED_SET;
150 1911 : vehPar->arrivalSpeedProcedure = ArrivalSpeedDefinition::GIVEN;
151 1911 : vehPar->arrivalSpeed = 0;
152 1911 : MSVehicleType* type = vehControl.getVType(vehPar->vtypeid);
153 1911 : if (type->getVehicleClass() != SVC_IGNORING && (origin->getPermissions() & type->getVehicleClass()) == 0 && !isTaxi) {
154 120 : WRITE_WARNINGF(TL("Ignoring vehicle type '%' when routing person '%' because it is not allowed on the start edge."), type->getID(), transportable->getID());
155 40 : delete vehPar;
156 : } else {
157 3742 : result.push_back(vehControl.buildVehicle(vehPar, routeDummy, type, !MSGlobals::gCheckRoutes));
158 : }
159 : }
160 205586 : if (result.empty()) {
161 : // walking only but may use access infrastructure
162 203749 : result.push_back(nullptr);
163 : }
164 205586 : return result;
165 205586 : }
166 :
167 :
168 : const std::string
169 205586 : MSStageTrip::reroute(const SUMOTime time, MSTransportableRouter& router, MSTransportable* const transportable,
170 : MSStage* previous, const MSEdge* origin, const MSEdge* destination, std::vector<MSStage*>& stages) {
171 205586 : if (origin->isTazConnector() && origin->getSuccessors().size() == 0) {
172 : // previous stage ended at a taz sink-edge
173 299 : origin = transportable->getNextStage(-1)->getDestination();
174 : }
175 205586 : MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
176 : double minCost = std::numeric_limits<double>::max();
177 : std::vector<MSTransportableRouter::TripItem> minResult;
178 : SUMOVehicle* minVehicle = nullptr;
179 411206 : for (SUMOVehicle* vehicle : getVehicles(vehControl, transportable, origin)) {
180 : std::vector<MSTransportableRouter::TripItem> result;
181 205620 : double departPos = previous->getArrivalPos();
182 : MSStoppingPlace* const prevStop = previous->getDestinationStop();
183 205620 : if (MSGlobals::gUseMesoSim && prevStop != nullptr) {
184 191 : departPos = (prevStop->getBeginLanePosition() + prevStop->getEndLanePosition()) / 2.;
185 : }
186 430811 : if (router.compute(origin, destination, departPos, prevStop == nullptr ? "" : prevStop->getID(),
187 380806 : myArrivalPos, myDestinationStop == nullptr ? "" : myDestinationStop->getID(),
188 205620 : transportable->getMaxSpeed() * myWalkFactor, vehicle, transportable->getVTypeParameter(), myModeSet, time, result)) {
189 : double totalCost = 0;
190 436647 : for (const MSTransportableRouter::TripItem& item : result) {
191 231098 : totalCost += item.cost;
192 : }
193 205549 : if (totalCost < minCost) {
194 : minCost = totalCost;
195 : minResult.swap(result);
196 : std::swap(minVehicle, vehicle);
197 : }
198 : }
199 205620 : if (vehicle != nullptr) {
200 41 : vehControl.deleteVehicle(vehicle, true);
201 : vehControl.discountRoutingVehicle();
202 : }
203 411206 : }
204 205586 : if (minCost != std::numeric_limits<double>::max()) {
205 205515 : const bool isTaxi = minVehicle != nullptr && minVehicle->getParameter().vtypeid == DEFAULT_TAXITYPE_ID && minVehicle->getParameter().line == "taxi";
206 : bool carUsed = false;
207 436567 : for (std::vector<MSTransportableRouter::TripItem>::iterator it = minResult.begin(); it != minResult.end(); ++it) {
208 231052 : if (!it->edges.empty()) {
209 211488 : MSStoppingPlace* bs = MSNet::getInstance()->getStoppingPlace(it->destStop);
210 211488 : double localArrivalPos = bs != nullptr ? bs->getAccessPos(it->edges.back()) : it->arrivalPos;
211 211488 : const MSEdge* const first = it->edges.front();
212 211488 : const MSEdge* const rideOrigin = origin->isTazConnector() && stages.empty() ? first : nullptr;
213 211488 : if (it + 1 == minResult.end() && myHaveArrivalPos) {
214 5561 : localArrivalPos = myArrivalPos;
215 : }
216 211488 : if (it->line == "") {
217 : // determine walk departPos
218 206816 : double depPos = previous->getArrivalPos();
219 206816 : if (previous->getDestinationStop() != nullptr) {
220 22406 : depPos = previous->getDestinationStop()->getAccessPos(first, first->getLanes()[0]->getRNG());
221 184410 : } else if (origin->isTazConnector()) {
222 : // walk the whole length of the first edge
223 105 : if (std::find(first->getPredecessors().begin(), first->getPredecessors().end(), origin) != first->getPredecessors().end()) {
224 : depPos = 0;
225 : } else {
226 : depPos = first->getLength();
227 : }
228 184305 : } else if (previous->getDestination() != first) {
229 53 : if ((previous->getDestination()->getToJunction() == first->getToJunction())
230 53 : || (previous->getDestination()->getFromJunction() == first->getToJunction())) {
231 : depPos = first->getLength();
232 : } else {
233 : depPos = 0.;
234 : }
235 : }
236 206816 : if (destination->isTazConnector()) {
237 : // walk the whole length of the last edge
238 745 : const MSEdge* last = it->edges.back();
239 745 : if (std::find(last->getSuccessors().begin(), last->getSuccessors().end(), destination) != last->getSuccessors().end()) {
240 : localArrivalPos = last->getLength();
241 : } else {
242 : localArrivalPos = 0;
243 : }
244 : }
245 206816 : previous = new MSStageWalking(transportable->getID(), it->edges, bs, myDuration, mySpeed, depPos, localArrivalPos, myDepartPosLat);
246 206816 : previous->setParameters(*this);
247 206816 : previous->setCosts(it->cost);
248 : previous->setTrip(this);
249 206816 : stages.push_back(previous);
250 4672 : } else if (isTaxi && it->line == minVehicle->getID()) {
251 170 : const ConstMSEdgeVector& prevEdges = previous->getEdges();
252 170 : if (prevEdges.size() >= 2 && previous->getDestinationStop() == nullptr) {
253 : // determine walking direction and let the previous
254 : // stage end after entering its final edge
255 6 : const MSEdge* last = prevEdges.back();
256 6 : const MSEdge* prev = prevEdges[prevEdges.size() - 2];
257 6 : if (last->getFromJunction() == prev->getToJunction() || prev->getFromJunction() == last->getFromJunction()) {
258 : previous->setArrivalPos(MIN2(last->getLength(), 10.0));
259 : } else {
260 6 : previous->setArrivalPos(MAX2(0.0, last->getLength() - 10));
261 : }
262 : }
263 510 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ "taxi" }), myGroup);
264 170 : previous->setParameters(*this);
265 170 : previous->setCosts(it->cost);
266 : previous->setTrip(this);
267 170 : stages.push_back(previous);
268 4672 : } else if (minVehicle != nullptr && it->line == minVehicle->getID()) {
269 4602 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ it->line }));
270 1534 : previous->setParameters(*this);
271 1534 : previous->setCosts(it->cost);
272 : previous->setTrip(this);
273 1534 : stages.push_back(previous);
274 1534 : minVehicle->replaceRouteEdges(it->edges, -1, 0, "person:" + transportable->getID(), true);
275 1534 : minVehicle->setArrivalPos(localArrivalPos);
276 1534 : const_cast<SUMOVehicleParameter&>(minVehicle->getParameter()).arrivalPos = localArrivalPos;
277 1534 : vehControl.addVehicle(minVehicle->getID(), minVehicle);
278 : carUsed = true;
279 : } else {
280 2968 : const std::string line = OptionsCont::getOptions().getBool("persontrip.ride-public-line") ? it->line : LINE_ANY;
281 8904 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ line }), myGroup, it->intended, TIME2STEPS(it->depart));
282 2968 : previous->setParameters(*this);
283 2968 : previous->setCosts(it->cost);
284 : previous->setTrip(this);
285 2968 : stages.push_back(previous);
286 : }
287 : }
288 : }
289 205515 : if (wasSet(VEHPARS_ARRIVALPOS_SET) && !stages.empty()) {
290 : // mark the last stage
291 1175 : stages.back()->markSet(VEHPARS_ARRIVALPOS_SET);
292 : }
293 : setCosts(minCost);
294 205515 : if (minVehicle != nullptr && (isTaxi || !carUsed)) {
295 296 : vehControl.deleteVehicle(minVehicle, true);
296 : vehControl.discountRoutingVehicle();
297 : }
298 : } else {
299 : // append stage so the GUI won't crash due to inconsistent state
300 71 : previous = new MSStageWalking(transportable->getID(), ConstMSEdgeVector({ origin, destination }), myDestinationStop, myDuration, mySpeed, previous->getArrivalPos(), myArrivalPos, myDepartPosLat);
301 71 : previous->setParameters(*this);
302 71 : stages.push_back(previous);
303 71 : if (MSGlobals::gCheckRoutes) { // if not pedestrians will teleport
304 141 : return "No connection found between " + getOriginDescription() + " and " + getDestinationDescription() + " for person '" + transportable->getID() + "'.";
305 : }
306 : }
307 205539 : if (stages.empty()) {
308 : // append stage so the GUI won't crash due to inconsistent state
309 14 : if (myOriginStop != nullptr && myOriginStop == myDestinationStop) {
310 28 : stages.push_back(new MSStageWaiting(destination, myDestinationStop, 0, -1, previous->getArrivalPos(), "sameStop", false));
311 : } else {
312 0 : stages.push_back(new MSStageWalking(transportable->getID(), ConstMSEdgeVector({ origin, destination }), myDestinationStop, myDuration, mySpeed, previous->getArrivalPos(), myArrivalPos, myDepartPosLat));
313 0 : if (MSGlobals::gCheckRoutes) { // if not pedestrians will teleport
314 0 : return "Empty route between " + getOriginDescription() + " and " + getDestinationDescription() + " for person '" + transportable->getID() + "'.";
315 : }
316 : }
317 : }
318 205539 : return "";
319 205926 : }
320 :
321 :
322 : const std::string
323 205506 : MSStageTrip::setArrived(MSNet* net, MSTransportable* transportable, SUMOTime now, const bool vehicleArrived) {
324 411012 : MSStage::setArrived(net, transportable, now, vehicleArrived);
325 : std::vector<MSStage*> stages;
326 : std::string result;
327 205506 : if (transportable->getCurrentStageIndex() == 0) {
328 0 : myDepartPos = transportable->getParameter().departPos;
329 0 : if (transportable->getParameter().departPosProcedure == DepartPosDefinition::RANDOM) {
330 : // TODO we should probably use the rng of the lane here
331 0 : myDepartPos = RandHelper::rand(myOrigin->getLength());
332 : }
333 0 : MSStageWaiting start(myOrigin, myOriginStop, -1, transportable->getParameter().depart, myDepartPos, "start", true);
334 0 : result = reroute(transportable->getParameter().depart, net->getIntermodalRouter(0), transportable, &start, myOrigin, myDestination, stages);
335 0 : } else {
336 : MSStage* previous = transportable->getNextStage(-1);
337 205506 : myDepartPos = previous->getArrivalPos();
338 411012 : result = reroute(now, net->getIntermodalRouter(0), transportable, previous, myOrigin, myDestination, stages);
339 : }
340 : int idx = 1;
341 416955 : for (MSStage* stage : stages) {
342 211449 : transportable->appendStage(stage, idx++);
343 : }
344 205506 : return result;
345 205506 : }
346 :
347 :
348 : void
349 205506 : MSStageTrip::proceed(MSNet* net, MSTransportable* transportable, SUMOTime now, MSStage* /* previous */) {
350 : // just skip the stage, every interesting happens in setArrived
351 205506 : transportable->proceed(net, now);
352 205459 : }
353 :
354 :
355 : std::string
356 47 : MSStageTrip::getOriginDescription() const {
357 47 : return (myOriginStop != nullptr
358 101 : ? toString(myOriginStop->getElement()) + " '" + myOriginStop->getID()
359 87 : : "edge '" + myOrigin->getID()) + "'";
360 : }
361 :
362 : std::string
363 47 : MSStageTrip::getDestinationDescription() const {
364 47 : return (myDestinationStop != nullptr
365 109 : ? toString(myDestinationStop->getElement()) + " '" + myDestinationStop->getID()
366 79 : : "edge '" + myDestination->getID()) + "'";
367 : }
368 :
369 : std::string
370 0 : MSStageTrip::getStageSummary(const bool) const {
371 0 : return "trip from " + getOriginDescription() + " to " + getDestinationDescription();
372 : }
373 :
374 : void
375 1598 : MSStageTrip::routeOutput(const bool /*isPerson*/, OutputDevice& os, const bool /*withRouteLength*/, const MSStage* const previous) const {
376 1598 : if (myArrived < 0) {
377 10 : const bool walkFactorSet = myWalkFactor != OptionsCont::getOptions().getFloat("persontrip.walkfactor");
378 10 : const bool groupSet = myGroup != OptionsCont::getOptions().getString("persontrip.default.group");
379 : // could still be a persontrip but most likely it was a walk in the input
380 10 : SumoXMLTag tag = myModeSet == 0 && !walkFactorSet && !groupSet ? SUMO_TAG_WALK : SUMO_TAG_PERSONTRIP;
381 10 : os.openTag(tag);
382 10 : if (previous == nullptr || previous->getStageType() == MSStageType::WAITING_FOR_DEPART) {
383 4 : os.writeAttr(SUMO_ATTR_FROM, myOrigin->getID());
384 : }
385 10 : if (myDestinationStop == nullptr) {
386 10 : os.writeAttr(SUMO_ATTR_TO, myDestination->getID());
387 10 : if (wasSet(VEHPARS_ARRIVALPOS_SET)) {
388 6 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, myArrivalPos);
389 : }
390 : } else {
391 0 : os.writeAttr(toString(myDestinationStop->getElement()), myDestinationStop->getID());
392 : }
393 : std::vector<std::string> modes;
394 10 : if ((myModeSet & SVC_PASSENGER) != 0) {
395 4 : modes.push_back("car");
396 : }
397 10 : if ((myModeSet & SVC_BICYCLE) != 0) {
398 0 : modes.push_back("bicycle");
399 : }
400 10 : if ((myModeSet & SVC_TAXI) != 0) {
401 0 : modes.push_back("taxi");
402 : }
403 10 : if ((myModeSet & SVC_BUS) != 0) {
404 0 : modes.push_back("public");
405 : }
406 10 : if (modes.size() > 0) {
407 2 : os.writeAttr(SUMO_ATTR_MODES, modes);
408 : }
409 10 : if (myVTypes.size() > 0) {
410 0 : os.writeAttr(SUMO_ATTR_VTYPES, myVTypes);
411 : }
412 10 : if (groupSet) {
413 0 : os.writeAttr(SUMO_ATTR_GROUP, myGroup);
414 : }
415 10 : if (walkFactorSet) {
416 0 : os.writeAttr(SUMO_ATTR_WALKFACTOR, myWalkFactor);
417 : }
418 20 : if (OptionsCont::getOptions().getBool("vehroute-output.cost")) {
419 0 : os.writeAttr(SUMO_ATTR_COST, getCosts());
420 : }
421 10 : os.closeTag();
422 10 : }
423 1598 : }
424 :
425 : /****************************************************************************/
|