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 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 251653 : 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 251653 : const double departPosLat, const bool hasArrivalPos, const double arrivalPos):
57 : MSStage(MSStageType::TRIP, destination, toStop, arrivalPos, 0.0, group),
58 251653 : myOrigin(origin),
59 251653 : myOriginStop(fromStop),
60 251653 : myDuration(duration),
61 251653 : myModeSet(modeSet),
62 251653 : myVTypes(vTypes),
63 251653 : mySpeed(speed),
64 251653 : myWalkFactor(walkFactor),
65 251653 : myDepartPosLat(departPosLat),
66 251653 : myHaveArrivalPos(hasArrivalPos) {
67 251653 : }
68 :
69 :
70 503296 : MSStageTrip::~MSStageTrip() {}
71 :
72 : MSStage*
73 245955 : MSStageTrip::clone() const {
74 245955 : MSStage* const clon = new MSStageTrip(myOrigin, const_cast<MSStoppingPlace*>(myOriginStop),
75 245955 : myDestination, myDestinationStop, myDuration,
76 245955 : myModeSet, myVTypes, mySpeed, myWalkFactor, myGroup,
77 245955 : myDepartPosLat, myHaveArrivalPos, myArrivalPos);
78 245955 : clon->setParameters(*this);
79 245955 : 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 474872 : MSStageTrip::getEdge() const {
99 474872 : return myOrigin;
100 : }
101 :
102 :
103 : double
104 466181 : MSStageTrip::getEdgePos(SUMOTime /* now */) const {
105 466181 : return myDepartPos;
106 : }
107 :
108 :
109 : const std::string
110 234965 : MSStageTrip::setArrived(MSNet* net, MSTransportable* transportable, SUMOTime now, const bool vehicleArrived) {
111 234965 : MSStage::setArrived(net, transportable, now, vehicleArrived);
112 234965 : if (myOrigin->isTazConnector() && myOrigin->getSuccessors().size() == 0) {
113 : // previous stage ended at a taz sink-edge
114 21 : myOrigin = transportable->getNextStage(-1)->getDestination();
115 : }
116 : MSVehicleControl& vehControl = net->getVehicleControl();
117 : std::vector<SUMOVehicleParameter*> pars;
118 470023 : for (StringTokenizer st(myVTypes); st.hasNext();) {
119 186 : pars.push_back(new SUMOVehicleParameter());
120 186 : pars.back()->vtypeid = st.next();
121 93 : pars.back()->parametersSet |= VEHPARS_VTYPE_SET;
122 93 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
123 186 : pars.back()->id = transportable->getID() + "_" + toString(pars.size() - 1);
124 234965 : }
125 234965 : if (pars.empty()) {
126 234872 : if ((myModeSet & SVC_PASSENGER) != 0) {
127 3114 : pars.push_back(new SUMOVehicleParameter());
128 3114 : pars.back()->id = transportable->getID() + "_0";
129 1557 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
130 233315 : } else if ((myModeSet & SVC_TAXI) != 0) {
131 240 : pars.push_back(new SUMOVehicleParameter());
132 120 : pars.back()->vtypeid = DEFAULT_TAXITYPE_ID;
133 240 : pars.back()->id = transportable->getID() + "_taxi";
134 120 : pars.back()->line = "taxi";
135 233195 : } else if ((myModeSet & SVC_BICYCLE) != 0) {
136 14 : pars.push_back(new SUMOVehicleParameter());
137 7 : pars.back()->vtypeid = DEFAULT_BIKETYPE_ID;
138 14 : pars.back()->id = transportable->getID() + "_b0";
139 7 : pars.back()->departProcedure = DepartDefinition::TRIGGERED;
140 : } else {
141 : // allow shortcut via busStop even when not intending to ride
142 233188 : pars.push_back(nullptr);
143 : }
144 : }
145 : MSStage* previous;
146 234965 : SUMOTime time = MSNet::getInstance()->getCurrentTimeStep();
147 234965 : if (transportable->getCurrentStageIndex() == 0) {
148 0 : myDepartPos = transportable->getParameter().departPos;
149 0 : if (transportable->getParameter().departPosProcedure == DepartPosDefinition::RANDOM) {
150 : // TODO we should probably use the rng of the lane here
151 0 : myDepartPos = RandHelper::rand(myOrigin->getLength());
152 : }
153 0 : previous = new MSStageWaiting(myOrigin, nullptr, -1, transportable->getParameter().depart, myDepartPos, "start", true);
154 0 : time = transportable->getParameter().depart;
155 : } else {
156 : previous = transportable->getNextStage(-1);
157 234965 : myDepartPos = previous->getArrivalPos();
158 : }
159 : // TODO This works currently only for a single vehicle type
160 : const int oldNumStages = transportable->getNumStages();
161 469898 : for (SUMOVehicleParameter* vehPar : pars) {
162 : SUMOVehicle* vehicle = nullptr;
163 : bool isTaxi = false;
164 234965 : if (vehPar != nullptr) {
165 1777 : isTaxi = vehPar->vtypeid == DEFAULT_TAXITYPE_ID && vehPar->line == "taxi";
166 1777 : if (myDepartPos != 0) {
167 56 : vehPar->departPosProcedure = DepartPosDefinition::GIVEN;
168 56 : vehPar->departPos = myDepartPos;
169 56 : vehPar->parametersSet |= VEHPARS_DEPARTPOS_SET;
170 : }
171 1777 : pars.back()->parametersSet |= VEHPARS_ARRIVALPOS_SET;
172 1777 : pars.back()->arrivalPosProcedure = ArrivalPosDefinition::GIVEN;
173 1777 : pars.back()->parametersSet |= VEHPARS_ARRIVALSPEED_SET;
174 1777 : pars.back()->arrivalSpeedProcedure = ArrivalSpeedDefinition::GIVEN;
175 1777 : pars.back()->arrivalSpeed = 0;
176 :
177 1777 : MSVehicleType* type = vehControl.getVType(vehPar->vtypeid);
178 1777 : if (type->getVehicleClass() != SVC_IGNORING && (myOrigin->getPermissions() & type->getVehicleClass()) == 0 && !isTaxi) {
179 120 : WRITE_WARNINGF(TL("Ignoring vehicle type '%' when routing person '%' because it is not allowed on the start edge."), type->getID(), transportable->getID());
180 40 : delete vehPar;
181 : } else {
182 3474 : ConstMSRoutePtr const routeDummy = std::make_shared<MSRoute>(vehPar->id, ConstMSEdgeVector({ myOrigin }), false, nullptr, std::vector<SUMOVehicleParameter::Stop>());
183 3474 : vehicle = vehControl.buildVehicle(vehPar, routeDummy, type, !MSGlobals::gCheckRoutes);
184 : }
185 : }
186 : bool carUsed = false;
187 : std::vector<MSTransportableRouter::TripItem> result;
188 : int stageIndex = 1;
189 234965 : double departPos = previous->getArrivalPos();
190 : MSStoppingPlace* const prevStop = previous->getDestinationStop();
191 234965 : if (MSGlobals::gUseMesoSim && prevStop != nullptr) {
192 96 : departPos = (prevStop->getBeginLanePosition() + prevStop->getEndLanePosition()) / 2.;
193 : }
194 704895 : if (net->getIntermodalRouter(0).compute(myOrigin, myDestination,
195 469930 : departPos, myOriginStop == nullptr ? "" : myOriginStop->getID(),
196 437156 : myArrivalPos, myDestinationStop == nullptr ? "" : myDestinationStop->getID(),
197 234965 : transportable->getMaxSpeed() * myWalkFactor, vehicle, myModeSet, time, result)) {
198 : double totalCost = 0;
199 477317 : for (std::vector<MSTransportableRouter::TripItem>::iterator it = result.begin(); it != result.end(); ++it) {
200 242408 : totalCost += it->cost;
201 242408 : if (!it->edges.empty()) {
202 241935 : MSStoppingPlace* bs = MSNet::getInstance()->getStoppingPlace(it->destStop, SUMO_TAG_BUS_STOP);
203 241935 : double localArrivalPos = bs != nullptr ? bs->getAccessPos(it->edges.back()) : it->edges.back()->getLength() / 2.;
204 241935 : const MSEdge* const first = it->edges.front();
205 241935 : const MSEdge* const rideOrigin = myOrigin->isTazConnector() && (transportable->getNumStages() == oldNumStages) ? first : nullptr;
206 241935 : if (it + 1 == result.end() && myHaveArrivalPos) {
207 6050 : localArrivalPos = myArrivalPos;
208 : }
209 241935 : if (it->line == "") {
210 : // determine walk departPos
211 236835 : double depPos = previous->getArrivalPos();
212 236835 : if (previous->getDestinationStop() != nullptr) {
213 3945 : depPos = previous->getDestinationStop()->getAccessPos(first, first->getLanes()[0]->getRNG());
214 232890 : } else if (myOrigin->isTazConnector()) {
215 : // walk the whole length of the first edge
216 1891 : if (std::find(first->getPredecessors().begin(), first->getPredecessors().end(), myOrigin) != first->getPredecessors().end()) {
217 : depPos = 0;
218 : } else {
219 : depPos = first->getLength();
220 : }
221 230999 : } else if (previous->getDestination() != first) {
222 49 : if ((previous->getDestination()->getToJunction() == first->getToJunction())
223 49 : || (previous->getDestination()->getFromJunction() == first->getToJunction())) {
224 : depPos = first->getLength();
225 : } else {
226 : depPos = 0.;
227 : }
228 : }
229 236835 : if (myDestination->isTazConnector()) {
230 : // walk the whole length of the last edge
231 116 : const MSEdge* last = it->edges.back();
232 116 : if (std::find(last->getSuccessors().begin(), last->getSuccessors().end(), myDestination) != last->getSuccessors().end()) {
233 : localArrivalPos = last->getLength();
234 : } else {
235 : localArrivalPos = 0;
236 : }
237 : }
238 236835 : previous = new MSStageWalking(transportable->getID(), it->edges, bs, myDuration, mySpeed, depPos, localArrivalPos, myDepartPosLat);
239 236835 : previous->setParameters(*this);
240 236835 : previous->setCosts(it->cost);
241 236835 : transportable->appendStage(previous, stageIndex++);
242 5100 : } else if (isTaxi) {
243 102 : const ConstMSEdgeVector& prevEdges = previous->getEdges();
244 102 : if (prevEdges.size() >= 2 && previous->getDestinationStop() == nullptr) {
245 : // determine walking direction and let the previous
246 : // stage end after entering its final edge
247 6 : const MSEdge* last = prevEdges.back();
248 6 : const MSEdge* prev = prevEdges[prevEdges.size() - 2];
249 6 : if (last->getFromJunction() == prev->getToJunction() || prev->getFromJunction() == last->getFromJunction()) {
250 : previous->setArrivalPos(MIN2(last->getLength(), 10.0));
251 : } else {
252 6 : previous->setArrivalPos(MAX2(0.0, last->getLength() - 10));
253 : }
254 : }
255 306 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ "taxi" }), myGroup);
256 102 : previous->setParameters(*this);
257 102 : previous->setCosts(it->cost);
258 102 : transportable->appendStage(previous, stageIndex++);
259 4998 : } else if (vehicle != nullptr && it->line == vehicle->getID()) {
260 1488 : if (bs == nullptr && it + 1 != result.end()) {
261 : // we have no defined endpoint and are in the middle of the trip, drive as far as possible
262 49 : localArrivalPos = it->edges.back()->getLength();
263 : }
264 5952 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ it->line }));
265 1488 : previous->setParameters(*this);
266 1488 : previous->setCosts(it->cost);
267 1488 : transportable->appendStage(previous, stageIndex++);
268 1488 : vehicle->replaceRouteEdges(it->edges, -1, 0, "person:" + transportable->getID(), true);
269 1488 : vehicle->setArrivalPos(localArrivalPos);
270 1488 : const_cast<SUMOVehicleParameter&>(vehicle->getParameter()).arrivalPos = localArrivalPos;
271 1488 : vehControl.addVehicle(vehPar->id, vehicle);
272 : carUsed = true;
273 : } else {
274 10530 : previous = new MSStageDriving(rideOrigin, it->edges.back(), bs, localArrivalPos, 0.0, std::vector<std::string>({ it->line }), myGroup, it->intended, TIME2STEPS(it->depart));
275 3510 : previous->setParameters(*this);
276 3510 : previous->setCosts(it->cost);
277 3510 : transportable->appendStage(previous, stageIndex++);
278 : }
279 : }
280 : }
281 234909 : if (wasSet(VEHPARS_ARRIVALPOS_SET) && stageIndex > 1) {
282 : // mark the last stage
283 1179 : transportable->getNextStage(stageIndex - 1)->markSet(VEHPARS_ARRIVALPOS_SET);
284 : }
285 : setCosts(totalCost);
286 : } else {
287 : // append stage so the GUI won't crash due to inconsistent state
288 112 : previous = new MSStageWalking(transportable->getID(), ConstMSEdgeVector({ myOrigin, myDestination }), myDestinationStop, myDuration, mySpeed, previous->getArrivalPos(), myArrivalPos, myDepartPosLat);
289 56 : previous->setParameters(*this);
290 56 : transportable->appendStage(previous, stageIndex++);
291 56 : if (MSGlobals::gCheckRoutes) { // if not pedestrians will teleport
292 32 : if (vehicle != nullptr) {
293 7 : vehControl.deleteVehicle(vehicle, true);
294 : }
295 64 : return "No connection found between " + getOriginDescription() + " and " + getDestinationDescription() + " for person '" + transportable->getID() + "'.";
296 : }
297 : }
298 234933 : if (vehicle != nullptr && (isTaxi || !carUsed)) {
299 242 : vehControl.deleteVehicle(vehicle, true);
300 : }
301 234965 : }
302 234933 : if (transportable->getNumStages() == oldNumStages) {
303 : // append stage so the GUI won't crash due to inconsistent state
304 14 : if (myOriginStop != nullptr && myOriginStop == myDestinationStop) {
305 28 : transportable->appendStage(new MSStageWaiting(myDestination, myDestinationStop, 0, -1, previous->getArrivalPos(), "sameStop", false), 1);
306 : } else {
307 0 : transportable->appendStage(new MSStageWalking(transportable->getID(), ConstMSEdgeVector({ myOrigin, myDestination }), myDestinationStop, myDuration, mySpeed, previous->getArrivalPos(), myArrivalPos, myDepartPosLat), 1);
308 0 : if (MSGlobals::gCheckRoutes) { // if not pedestrians will teleport
309 0 : return "Empty route between " + getOriginDescription() + " and " + getDestinationDescription() + " for person '" + transportable->getID() + "'.";
310 : }
311 : }
312 : }
313 234933 : return "";
314 : }
315 :
316 :
317 : void
318 234965 : MSStageTrip::proceed(MSNet* net, MSTransportable* transportable, SUMOTime now, MSStage* /* previous */) {
319 : // just skip the stage, every interesting happens in setArrived
320 234965 : transportable->proceed(net, now);
321 234933 : }
322 :
323 :
324 : std::string
325 32 : MSStageTrip::getOriginDescription() const {
326 32 : return (myOriginStop != nullptr
327 71 : ? toString(myOriginStop->getElement()) + " '" + myOriginStop->getID()
328 64 : : "edge '" + myOrigin->getID()) + "'";
329 : }
330 :
331 : std::string
332 32 : MSStageTrip::getDestinationDescription() const {
333 32 : return (myDestinationStop != nullptr
334 71 : ? toString(myDestinationStop->getElement()) + " '" + myDestinationStop->getID()
335 64 : : "edge '" + myDestination->getID()) + "'";
336 : }
337 :
338 : std::string
339 0 : MSStageTrip::getStageSummary(const bool) const {
340 0 : return "trip from " + getOriginDescription() + " to " + getDestinationDescription();
341 : }
342 :
343 : void
344 1368 : MSStageTrip::routeOutput(const bool /*isPerson*/, OutputDevice& os, const bool /*withRouteLength*/, const MSStage* const previous) const {
345 1368 : if (myArrived < 0) {
346 12 : const bool walkFactorSet = myWalkFactor != OptionsCont::getOptions().getFloat("persontrip.walkfactor");
347 12 : const bool groupSet = myGroup != OptionsCont::getOptions().getString("persontrip.default.group");
348 : // could still be a persontrip but most likely it was a walk in the input
349 12 : SumoXMLTag tag = myModeSet == 0 && !walkFactorSet && !groupSet ? SUMO_TAG_WALK : SUMO_TAG_PERSONTRIP;
350 12 : os.openTag(tag);
351 12 : if (previous == nullptr || previous->getStageType() == MSStageType::WAITING_FOR_DEPART) {
352 6 : os.writeAttr(SUMO_ATTR_FROM, myOrigin->getID());
353 : }
354 12 : if (myDestinationStop == nullptr) {
355 12 : os.writeAttr(SUMO_ATTR_TO, myDestination->getID());
356 12 : if (wasSet(VEHPARS_ARRIVALPOS_SET)) {
357 6 : os.writeAttr(SUMO_ATTR_ARRIVALPOS, myArrivalPos);
358 : }
359 : } else {
360 0 : os.writeAttr(toString(myDestinationStop->getElement()), myDestinationStop->getID());
361 : }
362 : std::vector<std::string> modes;
363 12 : if ((myModeSet & SVC_PASSENGER) != 0) {
364 6 : modes.push_back("car");
365 : }
366 12 : if ((myModeSet & SVC_BICYCLE) != 0) {
367 0 : modes.push_back("bicycle");
368 : }
369 12 : if ((myModeSet & SVC_TAXI) != 0) {
370 0 : modes.push_back("taxi");
371 : }
372 12 : if ((myModeSet & SVC_BUS) != 0) {
373 0 : modes.push_back("public");
374 : }
375 12 : if (modes.size() > 0) {
376 : os.writeAttr(SUMO_ATTR_MODES, modes);
377 : }
378 12 : if (myVTypes.size() > 0) {
379 0 : os.writeAttr(SUMO_ATTR_VTYPES, myVTypes);
380 : }
381 12 : if (groupSet) {
382 : os.writeAttr(SUMO_ATTR_GROUP, myGroup);
383 : }
384 12 : if (walkFactorSet) {
385 0 : os.writeAttr(SUMO_ATTR_WALKFACTOR, myWalkFactor);
386 : }
387 24 : if (OptionsCont::getOptions().getBool("vehroute-output.cost")) {
388 0 : os.writeAttr(SUMO_ATTR_COST, getCosts());
389 : }
390 12 : os.closeTag();
391 12 : }
392 1368 : }
393 :
394 : /****************************************************************************/
|