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