Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
IntermodalNetwork.h
Go to the documentation of this file.
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/****************************************************************************/
20// The Edge definition for the Intermodal Router
21/****************************************************************************/
22#pragma once
23#include <config.h>
24
25#include <string>
26#include <vector>
27#include <algorithm>
28#include <assert.h>
30#include <utils/common/Named.h>
33#include <utils/geom/Position.h>
35#include "AccessEdge.h"
36#include "CarEdge.h"
37#include "IntermodalEdge.h"
38#include "PedestrianEdge.h"
39#include "PublicTransportEdge.h"
40#include "StopEdge.h"
41
42//#define IntermodalRouter_DEBUG_NETWORK
43//#define IntermodalRouter_DEBUG_ACCESS
44
45
46// ===========================================================================
47// class definitions
48// ===========================================================================
71
72
73
75template<class E, class L, class N, class V>
77private:
82 typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
83
84public:
85 /* @brief build the pedestrian part of the intermodal network (once)
86 * @param edges The list of MSEdge or ROEdge to build from
87 * @param numericalID the start number for the creation of new edges
88 */
89 IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
90 : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
91#ifdef IntermodalRouter_DEBUG_NETWORK
92 std::cout << "initIntermodalNetwork\n";
93#endif
94 // build the pedestrian edges and the depart / arrival connectors with lookup tables
95 bool haveSeenWalkingArea = false;
96 for (const E* const edge : edges) {
97 if (edge->isTazConnector()) {
98 // only a single edge
99 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
100 addEdge(access);
101 myDepartLookup[edge].push_back(access);
102 myArrivalLookup[edge].push_back(access);
103 } else {
104 const L* lane = getSidewalk<E, L>(edge);
105 if (lane != nullptr) {
106 if (edge->isWalkingArea()) {
107 // only a single edge
108 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
109 myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
110 myDepartLookup[edge].push_back(myEdges.back());
111 myArrivalLookup[edge].push_back(myEdges.back());
112 haveSeenWalkingArea = true;
113 } else { // regular edge or crossing
114 // forward and backward edges
115 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
116 addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
117 myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
118 }
119 }
120 if (!edge->isWalkingArea()) {
121 // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
122 _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
123 _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
124 addConnectors(departConn, arrivalConn, 0);
125 }
126 }
127 }
128
129 // build the walking connectors if there are no walking areas
130 for (const E* const edge : edges) {
131 if (edge->isTazConnector() || edge->isInternal()) {
132 continue;
133 }
134 if (haveSeenWalkingArea) {
135 // connectivity needs to be ensured only in the real intermodal case, for simple pedestrian routing we don't have connectors if we have walking areas
136 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
137 const N* const node = edge->getToJunction();
138 if (myWalkingConnectorLookup.count(node) == 0) {
139 addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
140 myWalkingConnectorLookup[node] = myEdges.back();
141 }
142 }
143 } else {
144 for (const N* const node : {
145 edge->getFromJunction(), edge->getToJunction()
146 }) {
147 if (myWalkingConnectorLookup.count(node) == 0) {
148 addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
149 myWalkingConnectorLookup[node] = myEdges.back();
150 }
151 }
152 }
153 }
154 // build the connections
155 for (const E* const edge : edges) {
156 if (edge->isTazConnector()) {
157 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
158 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
159 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
160 const E* other = edge->getOtherTazConnector();
161 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
162 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
163 for (const E* out : edge->getSuccessors()) {
164 if (out->isNormal()) {
165 tazDepart->addSuccessor(getDepartConnector(out));
166 getArrivalConnector(out)->addSuccessor(otherTazArrive);
167 }
168 }
169 for (const E* in : edge->getPredecessors()) {
170 if (in->isNormal()) {
171 getArrivalConnector(in)->addSuccessor(tazArrive);
172 otherTazDepart->addSuccessor(getDepartConnector(in));
173 }
174 }
175 continue;
176 }
177 const L* const sidewalk = getSidewalk<E, L>(edge);
178 if (sidewalk == nullptr) {
179 continue;
180 }
181 // find all incoming and outgoing lanes for the sidewalk and
182 // connect the corresponding IntermodalEdges
183 const EdgePair& pair = getBothDirections(edge);
184#ifdef IntermodalRouter_DEBUG_NETWORK
185 std::cout << " building connections from " << sidewalk->getID() << "\n";
186#endif
187 if (haveSeenWalkingArea) {
188 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
189 // if one of the outgoing lanes is a walking area it must be used.
190 // All other connections shall be ignored
191 // if it has no outgoing walking area, it probably is a walking area itself
192 bool hasWalkingArea = false;
193 for (const auto& target : outgoing) {
194 if (target.first->getEdge().isWalkingArea()) {
195 hasWalkingArea = true;
196 break;
197 }
198 }
199 for (const auto& target : outgoing) {
200 const E* const targetEdge = &(target.first->getEdge());
201 const bool used = (target.first == getSidewalk<E, L>(targetEdge)
202 && (!hasWalkingArea || targetEdge->isWalkingArea()));
203#ifdef IntermodalRouter_DEBUG_NETWORK
204 const L* potTarget = getSidewalk<E, L>(targetEdge);
205 std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
206#endif
207 if (used) {
208 const EdgePair& targetPair = getBothDirections(targetEdge);
209 pair.first->addSuccessor(targetPair.first);
210 targetPair.second->addSuccessor(pair.second);
211#ifdef IntermodalRouter_DEBUG_NETWORK
212 std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
213 std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
214#endif
215 }
216 }
217 }
218 // We may have a network without pedestrian structures or a car-only edge.
219 // In the first case we assume that all sidewalks at a junction are interconnected,
220 // in the second we connect all car-only edges to all sidewalks.
221 _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
222 if (toNodeConn != nullptr) {
223 // Check for the outgoing vias and use the shortest one as an approximation
224 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
225 double minViaLength = std::numeric_limits<double>::max();
226 const E* minVia = nullptr;
227 for (const auto& target : outgoing) {
228 if (target.second != nullptr && target.second->getLength() < minViaLength) {
229 minViaLength = target.second->getLength();
230 minVia = target.second;
231 }
232 }
233 EdgePair interVia = std::make_pair(nullptr, nullptr);
234 if (minVia != nullptr) {
235 const auto it = myBidiLookup.find(minVia);
236 if (it != myBidiLookup.end()) {
237 interVia = it->second;
238 }
239 }
240 if (!haveSeenWalkingArea) {
241 // if we have walking areas we should use them and not the connector
242 pair.first->addSuccessor(toNodeConn, interVia.first);
243 }
244 toNodeConn->addSuccessor(pair.second, interVia.second);
245 }
246 _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
247 if (fromNodeConn != nullptr) {
248 if (!haveSeenWalkingArea) {
249 pair.second->addSuccessor(fromNodeConn);
250 }
251 fromNodeConn->addSuccessor(pair.first);
252 }
253 if (!edge->isWalkingArea()) {
254 // build connections from depart connector
255 _IntermodalEdge* startConnector = getDepartConnector(edge);
256 startConnector->addSuccessor(pair.first);
257 startConnector->addSuccessor(pair.second);
258 // build connections to arrival connector
259 _IntermodalEdge* endConnector = getArrivalConnector(edge);
260 pair.first->addSuccessor(endConnector);
261 pair.second->addSuccessor(endConnector);
262#ifdef IntermodalRouter_DEBUG_NETWORK
263 std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
264 std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
265 std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
266 std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
267#endif
268 }
269 }
270 }
271
273 for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
274 delete *it;
275 }
276 }
277
279 while ((int)myEdges.size() <= edge->getNumericalID()) {
280 myEdges.push_back(0);
281 }
282 myEdges[edge->getNumericalID()] = edge;
283 }
284
285 void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
286 addEdge(depConn);
287 addEdge(arrConn);
288 myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
289 myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
290 }
291
292 const std::vector<_IntermodalEdge*>& getAllEdges() {
293 return myEdges;
294 }
295
297 const EdgePair& getBothDirections(const E* e) const {
298 typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
299 if (it == myBidiLookup.end()) {
300 assert(false);
301 throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
302 }
303 return (*it).second;
304 }
305
307 const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
308 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
309 if (it == myDepartLookup.end()) {
310 throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
311 }
312 if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
313 // use most specific split (best trainStop, quay etc)
314 double bestDist = std::numeric_limits<double>::max();
315 const _IntermodalEdge* best = nullptr;
316 for (const _IntermodalEdge* const split : it->second) {
317 if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
318 const double dist = split->getEndPos() - split->getStartPos();
319 if (dist < bestDist) {
320 bestDist = dist;
321 best = split;
322 }
323 }
324 }
325 assert(best != nullptr);
326 return best;
327 } else {
328 // use next downstream edge
329 const std::vector<_IntermodalEdge*>& splitList = it->second;
330 typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
331 double totalLength = 0.;
332 while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
333 totalLength += (*splitIt)->getLength();
334 ++splitIt;
335 }
336 return *splitIt;
337 }
338 }
339
341 _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
342 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
343 if (it == myDepartLookup.end()) {
344 throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
345 }
346 if (splitIndex >= (int)it->second.size()) {
347 throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
348 }
349 return it->second[splitIndex];
350 }
351
353 _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
354 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
355 if (it == myArrivalLookup.end()) {
356 throw ProcessError(TLF("Arrival edge '%' not found in intermodal network.", e->getID()));
357 }
358 const std::vector<_IntermodalEdge*>& splitList = it->second;
359 typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
360 double totalLength = 0.;
361 while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
362 totalLength += (*splitIt)->getLength();
363 ++splitIt;
364 }
365 if (splitIt != splitList.end()) {
366 return *splitIt;
367 } else {
368 return splitList.back();
369 }
370 }
371
373 _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
374 return myArrivalLookup.find(e)->second[splitIndex];
375 }
376
379 typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
380 if (it == myWalkingConnectorLookup.end()) {
381 const L* const sidewalk = getSidewalk<E, L>(e);
382 if (e->isInternal() || sidewalk == 0) {
383 return 0;
384 }
385 for (const auto& target : sidewalk->getOutgoingViaLanes()) {
386 if (target.first->getEdge().isWalkingArea()) {
387 return getBothDirections(&target.first->getEdge()).first;
388 }
389 }
390 return 0;
391 }
392 return it->second;
393 }
394
395 void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
396 for (const E* const edge : edges) {
397 if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
398 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
399 addEdge(myCarLookup[edge]);
400 }
401 }
402 for (const auto& edgePair : myCarLookup) {
403 _IntermodalEdge* const carEdge = edgePair.second;
404 // connectivity within the car network
405 for (const auto& suc : edgePair.first->getViaSuccessors()) {
406 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
407 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
408 if (sucCarEdge != nullptr) {
409 carEdge->addSuccessor(sucCarEdge, sucViaEdge);
410 }
411 }
412 // connectivity to the pedestrian network (only for normal edges)
413 if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
414 continue;
415 }
416 if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
417 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
418 if (walkCon != 0) {
419 carEdge->addSuccessor(walkCon);
420 } else {
421 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
422 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
423 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
424 carEdge->addSuccessor(getBothDirections(out).first);
425 }
426 }
427 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
428 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
429 carEdge->addSuccessor(getBothDirections(in).second);
430 }
431 }
432 }
433 }
435 // add access edges that allow exiting a taxi
436 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
437 if (walkCon != 0) {
438 addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
439 } else {
440 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
441 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
442 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
443 addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
444 }
445 }
446 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
447 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
448 addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
449 }
450 }
451 }
452 }
453 // use intermediate access edge that prevents taxi departure
454 _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
455 _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
456 addEdge(access);
457 departConn->addSuccessor(access);
458 access->addSuccessor(carEdge);
460 // taxi may depart anywhere but there is a time penalty
461 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
462 addEdge(taxiAccess);
463 departConn->addSuccessor(taxiAccess);
464 taxiAccess->addSuccessor(carEdge);
465 }
467 // taxi (as all other cars) may arrive anywhere
468 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
469 } else {
470 // use intermediate access edge that prevents taxi arrival
471 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
472 }
473 }
474 }
475
477 _IntermodalEdge* getCarEdge(const E* e) const {
478 if (e == nullptr) {
479 return nullptr;
480 }
481 auto it = myCarLookup.find(e);
482 if (it == myCarLookup.end()) {
483 return nullptr;
484 }
485 return it->second;
486 }
487
489 _IntermodalEdge* getStopEdge(const std::string& stopId) const {
490 auto it = myStopConnections.find(stopId);
491 if (it == myStopConnections.end()) {
492 return nullptr;
493 }
494 return it->second;
495 }
496
514 void addAccess(const std::string& stopId, const E* stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait) {
515 assert(stopEdge != nullptr);
516 const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
517 (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
518 const bool transferTaxiWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_DROPOFF_PARKING_AREAS) != 0) ||
519 (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0));
520 const bool transferWalkTaxi = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_PICKUP_PARKING_AREAS) != 0) ||
521 (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0));
522 const double pos = (startPos + endPos) / 2.;
523#ifdef IntermodalRouter_DEBUG_ACCESS
524 std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
525 << " access=" << isAccess << " tWait=" << taxiWait << "\n";
526#endif
527 if (myStopConnections.count(stopId) == 0) {
528 myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
529 addEdge(myStopConnections[stopId]);
530 }
531 _IntermodalEdge* const stopConn = myStopConnections[stopId];
532 const L* lane = getSidewalk<E, L>(stopEdge);
533 if (lane != nullptr) {
534 const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
535 double relPos;
536 bool needSplit;
537 const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
538 _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
539 splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
540 _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
541 splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
542 _IntermodalEdge* carSplit = nullptr;
543 if (myCarLookup.count(stopEdge) > 0) {
544 if (needSplit) {
545 carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
546 }
547 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
548 }
549 if (needSplit) {
550 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
551 // adding access from car to walk
552 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
553 for (_IntermodalEdge* conn : {
554 fwdSplit, backSplit
555 }) {
556 if (transferCarWalk) {
557 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
558 addEdge(access);
559 beforeSplit->addSuccessor(access);
560 access->addSuccessor(conn);
561 } else if (transferTaxiWalk) {
562 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
563 }
564 }
565 }
566 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
567 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
568 addEdge(access);
569 stopConn->addSuccessor(access);
570 access->addSuccessor(carSplit);
571 }
572
573 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
574 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
575 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
576 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
577 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
578 depConn->addSuccessor(fwdSplit);
579 depConn->addSuccessor(backBeforeSplit);
580 depConn->setLength(fwdSplit->getLength());
581 prevDep->removeSuccessor(backBeforeSplit);
582 prevDep->addSuccessor(backSplit);
583 prevDep->setLength(backSplit->getLength());
584 if (carSplit != nullptr) {
585 depConn->addSuccessor(carSplit);
586 }
587
588 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
589 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
590 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
591 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
592 fwdSplit->addSuccessor(arrConn);
593 backBeforeSplit->addSuccessor(arrConn);
594 arrConn->setLength(fwdSplit->getLength());
595 fwdSplit->removeSuccessor(prevArr);
596 fwdBeforeSplit->addSuccessor(prevArr);
597 prevArr->setLength(backSplit->getLength());
598 if (carSplit != nullptr) {
599 if (carSplit->removeSuccessor(prevArr)) {
600 carSplit->addSuccessor(arrConn);
601 myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
602 } else {
603 // check for restricted access
604 for (_IntermodalEdge* out : carSplit->getSuccessors()) {
605 _AccessEdge* aOut = dynamic_cast<_AccessEdge*>(out);
606 if (aOut != nullptr && aOut->removeSuccessor(prevArr)) {
607 aOut->addSuccessor(arrConn);
609 myAccessSplits[myCarLookup[stopEdge]][splitIndex],
610 prevArr,
611 aOut->getVehicleRetriction());
612 break;
613 }
614 }
615 }
616 }
617 addConnectors(depConn, arrConn, splitIndex + 1);
618 }
619 } else {
620 // pedestrians cannot walk here:
621 // add stop edge as depart connector so that pedestrians may start at the stop
622 std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
623 assert(splitList.size() > 0);
624 typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
625 while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
626 ++splitIt;
627 }
628 splitList.insert(splitIt, stopConn);
629
630 if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
631 _IntermodalEdge* carEdge = myCarLookup[stopEdge];
632 double relPos;
633 bool needSplit;
634 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
635 if (needSplit) {
636 _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
637 splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
638
639 if (transferCarWalk || transferTaxiWalk) {
640 // adding access from car to walk
641 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
642 if (transferCarWalk) {
643 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
644 addEdge(access);
645 beforeSplit->addSuccessor(access);
646 access->addSuccessor(stopConn);
647 } else if (transferTaxiWalk) {
648 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
649 }
650 }
651 if (transferWalkTaxi) {
652 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
653 addEdge(access);
654 stopConn->addSuccessor(access);
655 access->addSuccessor(carSplit);
656 }
657 }
658 }
659 }
660 }
661
662 void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
663 SUMOTime lastUntil = 0;
664 StopParVector validStops;
665 if (addStops != nullptr) {
666 // stops are part of a stand-alone route. until times are offsets from vehicle departure
667 for (const SUMOVehicleParameter::Stop& stop : *addStops) {
668 if (myStopConnections.count(stop.busstop) > 0) {
669 // compute stop times for the first vehicle
670 const SUMOTime newUntil = stop.until + pars.depart;
671 if (newUntil >= lastUntil) {
672 validStops.push_back(stop);
673 validStops.back().until = newUntil;
674 lastUntil = newUntil;
675 } else {
676 WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
677 }
678 }
679 }
680 }
681 for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
682 // stops are part of the vehicle until times are absolute times for the first vehicle
683 if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
684 validStops.push_back(stop);
685 lastUntil = stop.until;
686 } else {
687 if (stop.busstop != "" && stop.until >= 0) {
688 WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
689 }
690 }
691 }
692 if (validStops.size() < 2 && pars.line != "taxi") {
693 WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
694 return;
695 }
696
697 typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
698 if (lineEdges.empty()) {
699 _IntermodalEdge* lastStop = nullptr;
700 Position lastPos;
701 SUMOTime lastTime = 0;
702 for (const SUMOVehicleParameter::Stop& s : validStops) {
703 _IntermodalEdge* currStop = myStopConnections[s.busstop];
704 Position stopPos = E::getStopPosition(s);
705 if (lastStop != nullptr) {
706 _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
707 addEdge(newEdge);
708 newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
709 lastStop->addSuccessor(newEdge);
710 newEdge->addSuccessor(currStop);
711 lineEdges.push_back(newEdge);
712 }
713 lastTime = s.until;
714 lastStop = currStop;
715 lastPos = stopPos;
716 }
717 if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
718 myLoopedLines.insert(pars.line);
719 }
720 } else {
721 if (validStops.size() != lineEdges.size() + 1) {
722 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
723 return;
724 }
725 if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
726 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
727 return;
728 }
729 typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
730 typename StopParVector::const_iterator s = validStops.begin() + 1;
731 for (; s != validStops.end(); ++s, ++lineEdge) {
732 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
733 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
734 return;
735 }
736 }
737 SUMOTime lastTime = validStops.front().until;
738 if (lineEdges.front()->hasSchedule(lastTime)) {
739 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
740 }
741 for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
742 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
743 lastTime = s->until;
744 }
745 }
746 }
747
752 void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
753 assert(edge != nullptr);
754 assert(myCarLookup.count(edge) != 0);
755 assert(myBidiLookup.count(edge) != 0);
756 EdgePair pedestrianEdges = myBidiLookup[edge];
757 _IntermodalEdge* carEdge = myCarLookup[edge];
758 _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
759 addEdge(access);
760 pedestrianEdges.first->addSuccessor(access);
761 pedestrianEdges.second->addSuccessor(access);
762 access->addSuccessor(carEdge);
763 }
764
771 _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
772 addEdge(access);
773 from->addSuccessor(access);
774 access->addSuccessor(to);
775 }
776
777 bool isLooped(const std::string lineID) const {
778 return myLoopedLines.count(lineID) != 0;
779 }
780
781private:
793 int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
794 relPos = pos;
795 needSplit = true;
796 int splitIndex = 0;
797 const auto& splitList = myAccessSplits.find(toSplit);
798 if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
799 for (const _IntermodalEdge* const split : splitList->second) {
800 if (relPos < split->getLength() + POSITION_EPS) {
801 break;
802 }
803 relPos -= split->getLength();
804 splitIndex++;
805 }
806 assert(splitIndex < (int)splitList->second.size());
807 if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
808 needSplit = false;
809 }
810 }
811 return splitIndex;
812 }
813
826 void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
827 _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
828 _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
829 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
830 if (splitList.empty()) {
831 splitList.push_back(toSplit);
832 }
833 if (!forward) {
834 splitIndex = (int)splitList.size() - 1 - splitIndex;
835 if (!needSplit) {
836 splitIndex--;
837 }
838 }
839 _IntermodalEdge* beforeSplit = splitList[splitIndex];
840 if (needSplit) {
841 addEdge(afterSplit);
842 beforeSplit->transferSuccessors(afterSplit);
843 beforeSplit->addSuccessor(afterSplit);
844 if (forward) {
845 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
846 beforeSplit->setLength(relPos);
847 } else {
848 afterSplit->setLength(relPos);
849 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
850 // rename backward edges for easier referencing
851 const std::string newID = beforeSplit->getID();
852 beforeSplit->setID(afterSplit->getID());
853 afterSplit->setID(newID);
854 }
855 splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
856 } else {
857 // don't split, use the present split edges
858 afterSplit = splitList[splitIndex + 1];
859 }
860 // add access to / from edge
861 if (addEntry) {
862 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
863 addEdge(access);
864 beforeSplit->addSuccessor(access);
865 access->addSuccessor(stopConn);
866 }
867 if (addExit) {
868 // pedestrian case only, exit from public to pedestrian
869 _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
870 addEdge(exit);
871 stopConn->addSuccessor(exit);
872 exit->addSuccessor(afterSplit);
873 }
874 }
875
876
877private:
879 std::vector<_IntermodalEdge*> myEdges;
880
882 std::map<const E*, EdgePair> myBidiLookup;
883
885 std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
886
888 std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
889
891 std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
892
894 std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
895
897 std::map<std::string, std::vector<_PTEdge*> > myPTLines;
898
900 std::map<std::string, _IntermodalEdge*> myStopConnections;
901
903 std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
904
906 std::set<std::string > myLoopedLines;
907
910
911private:
914
915};
long long int SUMOTime
Definition GUI.h:36
ModeChangeOptions
where mode changes are possible
@ TAXI_DROPOFF_ANYWHERE
taxi customer may exit anywhere
@ PT_STOPS
public transport stops and access
@ TAXI_PICKUP_ANYWHERE
taxi customer may be picked up anywhere
@ TAXI_PICKUP_PARKING_AREAS
taxi customer may be picked up at parking areas
@ TAXI_DROPOFF_PT
taxi customer may exit at public transport stops
@ TAXI_DROPOFF_PARKING_AREAS
taxi customer may exit at parking areas
@ ALL_JUNCTIONS
junctions with edges allowing the additional mode
@ TAXI_PICKUP_PT
taxi customer may be picked up at public transport stops
@ PARKING_AREAS
parking areas
std::vector< std::string > & split(const std::string &s, char delim, std::vector< std::string > &elems)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:287
#define TL(string)
Definition MsgHandler.h:304
#define TLF(string,...)
Definition MsgHandler.h:306
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:91
const SVCPermissions SVCAll
all VClasses are allowed
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_TAXI
vehicle is a taxi
@ SVC_PEDESTRIAN
pedestrian
std::vector< SUMOVehicleParameter::Stop > StopParVector
SumoXMLTag
Numbers representing SUMO-XML - element names.
@ SUMO_TAG_BUS_STOP
A bus stop.
@ SUMO_TAG_PARKING_AREA
A parking area.
T MAX2(T a, T b)
Definition StdDefs.h:86
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
the access edge connecting different modes that is given to the internal router (SUMOAbstractRouter)
Definition AccessEdge.h:31
SVCPermissions getVehicleRetriction() const
Definition AccessEdge.h:66
the car edge type that is given to the internal router (SUMOAbstractRouter)
Definition CarEdge.h:34
the base edge type that is given to the internal router (SUMOAbstractRouter)
const E * getEdge() const
void setLength(const double length)
void transferSuccessors(IntermodalEdge *to)
bool removeSuccessor(const IntermodalEdge *const edge)
void addSuccessor(IntermodalEdge *const s, IntermodalEdge *const via=nullptr)
double getLength() const
required by DijkstraRouter et al for external effort computation
int getNumericalID() const
virtual const std::vector< IntermodalEdge * > & getSuccessors(SUMOVehicleClass vClass=SVC_IGNORING) const
virtual double getEndPos() const
the intermodal network storing edges, connections and the mappings to the "real" edges
_IntermodalEdge * getWalkingConnector(const E *e) const
Returns the outgoing pedestrian edge, which is either a walking area or a walking connector.
const std::vector< _IntermodalEdge * > & getAllEdges()
_IntermodalEdge * getArrivalConnector(const E *e, const int splitIndex=0) const
Returns the arriving intermodal connector at the given split offset.
PublicTransportEdge< E, L, N, V > _PTEdge
_IntermodalEdge * getCarEdge(const E *e) const
Returns the associated car edge.
std::map< const E *, _IntermodalEdge *, ComparatorNumericalIdLess > myCarLookup
retrieve the car edge for the given input edge E
void addCarAccess(const E *edge, SUMOVehicleClass svc, double traveltime)
Adds access edges for transfering from walking to vehicle use.
void addAccess(const std::string &stopId, const E *stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait)
Adds access edges for stopping places to the intermodal network.
std::map< const E *, std::vector< _IntermodalEdge * > > myArrivalLookup
retrieve the arrival edges for the given input edge E
std::map< const N *, _IntermodalEdge * > myWalkingConnectorLookup
the walking connector edge (fake walking area)
std::map< std::string, _IntermodalEdge * > myStopConnections
retrieve the representing edge for the given stopping place
std::map< _IntermodalEdge *, std::vector< _IntermodalEdge * > > myAccessSplits
retrieve the splitted edges for the given "original"
_IntermodalEdge * getStopEdge(const std::string &stopId) const
Returns the associated stop edge.
void addEdge(_IntermodalEdge *edge)
std::vector< _IntermodalEdge * > myEdges
the edge dictionary
AccessEdge< E, L, N, V > _AccessEdge
IntermodalNetwork & operator=(const IntermodalNetwork &s)
Invalidated assignment operator.
std::map< const E *, EdgePair > myBidiLookup
retrieve the forward and backward edge for the given input edge E
std::set< std::string > myLoopedLines
looped lines need extra checking when building itineraries
bool isLooped(const std::string lineID) const
std::map< std::string, std::vector< _PTEdge * > > myPTLines
retrieve the public transport edges for the given line
void addRestrictedCarExit(_IntermodalEdge *from, _IntermodalEdge *to, SVCPermissions vehicleRestriction)
Adds access edges for transfering from driving to walking that are only usable by a particular vehicl...
void addConnectors(_IntermodalEdge *const depConn, _IntermodalEdge *const arrConn, const int index)
const EdgePair & getBothDirections(const E *e) const
Returns the pair of forward and backward edge.
void addSchedule(const SUMOVehicleParameter &pars, const StopParVector *addStops=nullptr)
int findSplitIndex(_IntermodalEdge *const toSplit, const double pos, double &relPos, bool &needSplit) const
Returns where to insert or use the split edge.
void addCarEdges(const std::vector< E * > &edges, double taxiWait)
PedestrianEdge< E, L, N, V > _PedestrianEdge
void splitEdge(_IntermodalEdge *const toSplit, int splitIndex, _IntermodalEdge *afterSplit, const double relPos, const double length, const bool needSplit, _IntermodalEdge *const stopConn, const bool forward=true, const bool addExit=true, const bool addEntry=true)
Splits an edge (if necessary) and connects it to a stopping edge.
IntermodalNetwork(const std::vector< E * > &edges, const bool pedestrianOnly, const int carWalkTransfer=0)
_IntermodalEdge * getArrivalEdge(const E *e, const double pos) const
Returns the arriving intermodal edge.
IntermodalEdge< E, L, N, V > _IntermodalEdge
std::map< const E *, std::vector< _IntermodalEdge * > > myDepartLookup
retrieve the depart edges for the given input edge E
_IntermodalEdge * getDepartConnector(const E *e, const int splitIndex=0) const
Returns the departing intermodal connector at the given split offset.
std::pair< _IntermodalEdge *, _IntermodalEdge * > EdgePair
const _IntermodalEdge * getDepartEdge(const E *e, const double pos) const
Returns the departing intermodal edge.
virtual void setID(const std::string &newID)
resets the id
Definition Named.h:82
const std::string & getID() const
Returns the id.
Definition Named.h:74
the pedestrian edge type that is given to the internal router (SUMOAbstractRouter)
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
double distanceTo(const Position &p2) const
returns the euclidean distance in 3 dimensions
Definition Position.h:263
the public transport edge type connecting the stop edges
void addSchedule(const std::string id, const SUMOTime begin, const int repetitionNumber, const SUMOTime period, const SUMOTime travelTime)
Definition of vehicle stop (position and duration)
Structure representing possible vehicle parameter.
SUMOTime repetitionOffset
The time offset between vehicle reinsertions.
std::string id
The vehicle's id.
std::vector< Stop > stops
List of the stops the vehicle will make, TraCI may add entries here.
std::string line
The vehicle's line (mainly for public transport)
the stop edge type representing bus and train stops
Definition StopEdge.h:31