Eclipse SUMO - Simulation of Urban MObility
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 != 0) {
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);
459 if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
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 }
466 if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
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_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
519 const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
520 const double pos = (startPos + endPos) / 2.;
521#ifdef IntermodalRouter_DEBUG_ACCESS
522 std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
523 << " access=" << isAccess << " tWait=" << taxiWait << "\n";
524#endif
525 if (myStopConnections.count(stopId) == 0) {
526 myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
527 addEdge(myStopConnections[stopId]);
528 }
529 _IntermodalEdge* const stopConn = myStopConnections[stopId];
530 const L* lane = getSidewalk<E, L>(stopEdge);
531 if (lane != nullptr) {
532 const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
533 double relPos;
534 bool needSplit;
535 const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
536 _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
537 splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
538 _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
539 splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
540 _IntermodalEdge* carSplit = nullptr;
541 if (myCarLookup.count(stopEdge) > 0) {
542 if (needSplit) {
543 carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
544 }
545 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
546 }
547 if (needSplit) {
548 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
549 // adding access from car to walk
550 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
551 for (_IntermodalEdge* conn : {
552 fwdSplit, backSplit
553 }) {
554 if (transferCarWalk) {
555 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
556 addEdge(access);
557 beforeSplit->addSuccessor(access);
558 access->addSuccessor(conn);
559 } else if (transferTaxiWalk) {
560 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
561 }
562 }
563 }
564 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
565 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
566 addEdge(access);
567 stopConn->addSuccessor(access);
568 access->addSuccessor(carSplit);
569 }
570
571 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
572 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
573 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
574 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
575 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
576 depConn->addSuccessor(fwdSplit);
577 depConn->addSuccessor(backBeforeSplit);
578 depConn->setLength(fwdSplit->getLength());
579 prevDep->removeSuccessor(backBeforeSplit);
580 prevDep->addSuccessor(backSplit);
581 prevDep->setLength(backSplit->getLength());
582 if (carSplit != nullptr) {
583 depConn->addSuccessor(carSplit);
584 }
585
586 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
587 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
588 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
589 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
590 fwdSplit->addSuccessor(arrConn);
591 backBeforeSplit->addSuccessor(arrConn);
592 arrConn->setLength(fwdSplit->getLength());
593 fwdSplit->removeSuccessor(prevArr);
594 fwdBeforeSplit->addSuccessor(prevArr);
595 prevArr->setLength(backSplit->getLength());
596 if (carSplit != nullptr) {
597 if (carSplit->removeSuccessor(prevArr)) {
598 carSplit->addSuccessor(arrConn);
599 myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
600 }
601 }
602 addConnectors(depConn, arrConn, splitIndex + 1);
603 }
604 } else {
605 // pedestrians cannot walk here:
606 // add stop edge as depart connector so that pedestrians may start at the stop
607 std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
608 assert(splitList.size() > 0);
609 typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
610 while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
611 ++splitIt;
612 }
613 splitList.insert(splitIt, stopConn);
614
615 if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
616 _IntermodalEdge* carEdge = myCarLookup[stopEdge];
617 double relPos;
618 bool needSplit;
619 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
620 if (needSplit) {
621 _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
622 splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
623
624 if (transferCarWalk || transferTaxiWalk) {
625 // adding access from car to walk
626 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
627 if (transferCarWalk) {
628 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
629 addEdge(access);
630 beforeSplit->addSuccessor(access);
631 access->addSuccessor(stopConn);
632 } else if (transferTaxiWalk) {
633 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
634 }
635 }
636 if (transferWalkTaxi) {
637 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
638 addEdge(access);
639 stopConn->addSuccessor(access);
640 access->addSuccessor(carSplit);
641 }
642 }
643 }
644 }
645 }
646
647 void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
648 SUMOTime lastUntil = 0;
649 std::vector<SUMOVehicleParameter::Stop> validStops;
650 if (addStops != nullptr) {
651 // stops are part of a stand-alone route. until times are offsets from vehicle departure
652 for (const SUMOVehicleParameter::Stop& stop : *addStops) {
653 if (myStopConnections.count(stop.busstop) > 0) {
654 // compute stop times for the first vehicle
655 const SUMOTime newUntil = stop.until + pars.depart;
656 if (newUntil >= lastUntil) {
657 validStops.push_back(stop);
658 validStops.back().until = newUntil;
659 lastUntil = newUntil;
660 } else {
661 WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
662 }
663 }
664 }
665 }
666 for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
667 // stops are part of the vehicle until times are absolute times for the first vehicle
668 if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
669 validStops.push_back(stop);
670 lastUntil = stop.until;
671 } else {
672 if (stop.busstop != "" && stop.until >= 0) {
673 WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
674 }
675 }
676 }
677 if (validStops.size() < 2 && pars.line != "taxi") {
678 WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
679 return;
680 }
681
682 typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
683 if (lineEdges.empty()) {
684 _IntermodalEdge* lastStop = nullptr;
685 Position lastPos;
686 SUMOTime lastTime = 0;
687 for (const SUMOVehicleParameter::Stop& s : validStops) {
688 _IntermodalEdge* currStop = myStopConnections[s.busstop];
689 Position stopPos = E::getStopPosition(s);
690 if (lastStop != nullptr) {
691 _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
692 addEdge(newEdge);
693 newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
694 lastStop->addSuccessor(newEdge);
695 newEdge->addSuccessor(currStop);
696 lineEdges.push_back(newEdge);
697 }
698 lastTime = s.until;
699 lastStop = currStop;
700 lastPos = stopPos;
701 }
702 if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
703 myLoopedLines.insert(pars.line);
704 }
705 } else {
706 if (validStops.size() != lineEdges.size() + 1) {
707 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
708 return;
709 }
710 if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
711 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
712 return;
713 }
714 typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
715 typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
716 for (; s != validStops.end(); ++s, ++lineEdge) {
717 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
718 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
719 return;
720 }
721 }
722 SUMOTime lastTime = validStops.front().until;
723 if (lineEdges.front()->hasSchedule(lastTime)) {
724 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
725 }
726 for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
727 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
728 lastTime = s->until;
729 }
730 }
731 }
732
737 void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
738 assert(edge != nullptr);
739 assert(myCarLookup.count(edge) != 0);
740 assert(myBidiLookup.count(edge) != 0);
741 EdgePair pedestrianEdges = myBidiLookup[edge];
742 _IntermodalEdge* carEdge = myCarLookup[edge];
743 _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
744 addEdge(access);
745 pedestrianEdges.first->addSuccessor(access);
746 pedestrianEdges.second->addSuccessor(access);
747 access->addSuccessor(carEdge);
748 }
749
756 _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
757 addEdge(access);
758 from->addSuccessor(access);
759 access->addSuccessor(to);
760 }
761
762 bool isLooped(const std::string lineID) const {
763 return myLoopedLines.count(lineID) != 0;
764 }
765
766private:
778 int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
779 relPos = pos;
780 needSplit = true;
781 int splitIndex = 0;
782 const auto& splitList = myAccessSplits.find(toSplit);
783 if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
784 for (const _IntermodalEdge* const split : splitList->second) {
785 if (relPos < split->getLength() + POSITION_EPS) {
786 break;
787 }
788 relPos -= split->getLength();
789 splitIndex++;
790 }
791 assert(splitIndex < (int)splitList->second.size());
792 if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
793 needSplit = false;
794 }
795 }
796 return splitIndex;
797 }
798
811 void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
812 _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
813 _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
814 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
815 if (splitList.empty()) {
816 splitList.push_back(toSplit);
817 }
818 if (!forward) {
819 splitIndex = (int)splitList.size() - 1 - splitIndex;
820 if (!needSplit) {
821 splitIndex--;
822 }
823 }
824 _IntermodalEdge* beforeSplit = splitList[splitIndex];
825 if (needSplit) {
826 addEdge(afterSplit);
827 beforeSplit->transferSuccessors(afterSplit);
828 beforeSplit->addSuccessor(afterSplit);
829 if (forward) {
830 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
831 beforeSplit->setLength(relPos);
832 } else {
833 afterSplit->setLength(relPos);
834 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
835 // rename backward edges for easier referencing
836 const std::string newID = beforeSplit->getID();
837 beforeSplit->setID(afterSplit->getID());
838 afterSplit->setID(newID);
839 }
840 splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
841 } else {
842 // don't split, use the present split edges
843 afterSplit = splitList[splitIndex + 1];
844 }
845 // add access to / from edge
846 if (addEntry) {
847 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
848 addEdge(access);
849 beforeSplit->addSuccessor(access);
850 access->addSuccessor(stopConn);
851 }
852 if (addExit) {
853 // pedestrian case only, exit from public to pedestrian
854 _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
855 addEdge(exit);
856 stopConn->addSuccessor(exit);
857 exit->addSuccessor(afterSplit);
858 }
859 }
860
861
862private:
864 std::vector<_IntermodalEdge*> myEdges;
865
867 std::map<const E*, EdgePair> myBidiLookup;
868
870 std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
871
873 std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
874
876 std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
877
879 std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
880
882 std::map<std::string, std::vector<_PTEdge*> > myPTLines;
883
885 std::map<std::string, _IntermodalEdge*> myStopConnections;
886
888 std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
889
891 std::set<std::string > myLoopedLines;
892
895
896private:
899
900};
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:288
#define TL(string)
Definition MsgHandler.h:305
#define TLF(string,...)
Definition MsgHandler.h:307
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
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:82
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
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 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.
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.
void addSchedule(const SUMOVehicleParameter &pars, const std::vector< SUMOVehicleParameter::Stop > *addStops=nullptr)
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