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-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/****************************************************************************/
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 return *splitIt;
366 }
367
369 _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
370 return myArrivalLookup.find(e)->second[splitIndex];
371 }
372
375 typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
376 if (it == myWalkingConnectorLookup.end()) {
377 const L* const sidewalk = getSidewalk<E, L>(e);
378 if (e->isInternal() || sidewalk == 0) {
379 return 0;
380 }
381 for (const auto& target : sidewalk->getOutgoingViaLanes()) {
382 if (target.first->getEdge().isWalkingArea()) {
383 return getBothDirections(&target.first->getEdge()).first;
384 }
385 }
386 return 0;
387 }
388 return it->second;
389 }
390
391 void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
392 for (const E* const edge : edges) {
393 if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
394 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
395 addEdge(myCarLookup[edge]);
396 }
397 }
398 for (const auto& edgePair : myCarLookup) {
399 _IntermodalEdge* const carEdge = edgePair.second;
400 // connectivity within the car network
401 for (const auto& suc : edgePair.first->getViaSuccessors()) {
402 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
403 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
404 if (sucCarEdge != nullptr) {
405 carEdge->addSuccessor(sucCarEdge, sucViaEdge);
406 }
407 }
408 // connectivity to the pedestrian network (only for normal edges)
409 if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
410 continue;
411 }
412 if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
413 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
414 if (walkCon != 0) {
415 carEdge->addSuccessor(walkCon);
416 } else {
417 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
418 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
419 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
420 carEdge->addSuccessor(getBothDirections(out).first);
421 }
422 }
423 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
424 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
425 carEdge->addSuccessor(getBothDirections(in).second);
426 }
427 }
428 }
429 }
431 // add access edges that allow exiting a taxi
432 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
433 if (walkCon != 0) {
434 addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
435 } else {
436 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
437 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
438 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
439 addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
440 }
441 }
442 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
443 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
444 addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
445 }
446 }
447 }
448 }
449 // use intermediate access edge that prevents taxi departure
450 _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
451 _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
452 addEdge(access);
453 departConn->addSuccessor(access);
454 access->addSuccessor(carEdge);
455 if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
456 // taxi may depart anywhere but there is a time penalty
457 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
458 addEdge(taxiAccess);
459 departConn->addSuccessor(taxiAccess);
460 taxiAccess->addSuccessor(carEdge);
461 }
462 if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
463 // taxi (as all other cars) may arrive anywhere
464 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
465 } else {
466 // use intermediate access edge that prevents taxi arrival
467 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
468 }
469 }
470 }
471
473 _IntermodalEdge* getCarEdge(const E* e) const {
474 if (e == nullptr) {
475 return nullptr;
476 }
477 auto it = myCarLookup.find(e);
478 if (it == myCarLookup.end()) {
479 return nullptr;
480 }
481 return it->second;
482 }
483
485 _IntermodalEdge* getStopEdge(const std::string& stopId) const {
486 auto it = myStopConnections.find(stopId);
487 if (it == myStopConnections.end()) {
488 return nullptr;
489 }
490 return it->second;
491 }
492
510 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) {
511 assert(stopEdge != nullptr);
512 const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
513 (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
514 const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
515 const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
516 const double pos = (startPos + endPos) / 2.;
517#ifdef IntermodalRouter_DEBUG_ACCESS
518 std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
519 << " access=" << isAccess << " tWait=" << taxiWait << "\n";
520#endif
521 if (myStopConnections.count(stopId) == 0) {
522 myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
523 addEdge(myStopConnections[stopId]);
524 }
525 _IntermodalEdge* const stopConn = myStopConnections[stopId];
526 const L* lane = getSidewalk<E, L>(stopEdge);
527 if (lane != nullptr) {
528 const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
529 double relPos;
530 bool needSplit;
531 const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
532 _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
533 splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
534 _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
535 splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
536 _IntermodalEdge* carSplit = nullptr;
537 if (myCarLookup.count(stopEdge) > 0) {
538 if (needSplit) {
539 carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
540 }
541 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
542 }
543 if (needSplit) {
544 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
545 // adding access from car to walk
546 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
547 for (_IntermodalEdge* conn : {
548 fwdSplit, backSplit
549 }) {
550 if (transferCarWalk) {
551 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
552 addEdge(access);
553 beforeSplit->addSuccessor(access);
554 access->addSuccessor(conn);
555 } else if (transferTaxiWalk) {
556 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
557 }
558 }
559 }
560 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
561 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
562 addEdge(access);
563 stopConn->addSuccessor(access);
564 access->addSuccessor(carSplit);
565 }
566
567 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
568 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
569 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
570 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
571 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
572 depConn->addSuccessor(fwdSplit);
573 depConn->addSuccessor(backBeforeSplit);
574 depConn->setLength(fwdSplit->getLength());
575 prevDep->removeSuccessor(backBeforeSplit);
576 prevDep->addSuccessor(backSplit);
577 prevDep->setLength(backSplit->getLength());
578 if (carSplit != nullptr) {
579 depConn->addSuccessor(carSplit);
580 }
581
582 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
583 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
584 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
585 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
586 fwdSplit->addSuccessor(arrConn);
587 backBeforeSplit->addSuccessor(arrConn);
588 arrConn->setLength(fwdSplit->getLength());
589 fwdSplit->removeSuccessor(prevArr);
590 fwdBeforeSplit->addSuccessor(prevArr);
591 prevArr->setLength(backSplit->getLength());
592 if (carSplit != nullptr) {
593 if (carSplit->removeSuccessor(prevArr)) {
594 carSplit->addSuccessor(arrConn);
595 myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
596 }
597 }
598 addConnectors(depConn, arrConn, splitIndex + 1);
599 }
600 } else {
601 // pedestrians cannot walk here:
602 // add stop edge as depart connector so that pedestrians may start at the stop
603 std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
604 assert(splitList.size() > 0);
605 typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
606 while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
607 ++splitIt;
608 }
609 splitList.insert(splitIt, stopConn);
610
611 if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
612 _IntermodalEdge* carEdge = myCarLookup[stopEdge];
613 double relPos;
614 bool needSplit;
615 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
616 if (needSplit) {
617 _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
618 splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
619
620 if (transferCarWalk || transferTaxiWalk) {
621 // adding access from car to walk
622 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
623 if (transferCarWalk) {
624 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
625 addEdge(access);
626 beforeSplit->addSuccessor(access);
627 access->addSuccessor(stopConn);
628 } else if (transferTaxiWalk) {
629 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
630 }
631 }
632 if (transferWalkTaxi) {
633 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
634 addEdge(access);
635 stopConn->addSuccessor(access);
636 access->addSuccessor(carSplit);
637 }
638 }
639 }
640 }
641 }
642
643 void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
644 SUMOTime lastUntil = 0;
645 std::vector<SUMOVehicleParameter::Stop> validStops;
646 if (addStops != nullptr) {
647 // stops are part of a stand-alone route. until times are offsets from vehicle departure
648 for (const SUMOVehicleParameter::Stop& stop : *addStops) {
649 if (myStopConnections.count(stop.busstop) > 0) {
650 // compute stop times for the first vehicle
651 const SUMOTime newUntil = stop.until + pars.depart;
652 if (newUntil >= lastUntil) {
653 validStops.push_back(stop);
654 validStops.back().until = newUntil;
655 lastUntil = newUntil;
656 } else {
657 WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
658 }
659 }
660 }
661 }
662 for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
663 // stops are part of the vehicle until times are absolute times for the first vehicle
664 if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
665 validStops.push_back(stop);
666 lastUntil = stop.until;
667 } else {
668 if (stop.busstop != "" && stop.until >= 0) {
669 WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
670 }
671 }
672 }
673 if (validStops.size() < 2 && pars.line != "taxi") {
674 WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
675 return;
676 }
677
678 typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
679 if (lineEdges.empty()) {
680 _IntermodalEdge* lastStop = nullptr;
681 Position lastPos;
682 SUMOTime lastTime = 0;
683 for (const SUMOVehicleParameter::Stop& s : validStops) {
684 _IntermodalEdge* currStop = myStopConnections[s.busstop];
685 Position stopPos = E::getStopPosition(s);
686 if (lastStop != nullptr) {
687 _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
688 addEdge(newEdge);
689 newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
690 lastStop->addSuccessor(newEdge);
691 newEdge->addSuccessor(currStop);
692 lineEdges.push_back(newEdge);
693 }
694 lastTime = s.until;
695 lastStop = currStop;
696 lastPos = stopPos;
697 }
698 if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
699 myLoopedLines.insert(pars.line);
700 }
701 } else {
702 if (validStops.size() != lineEdges.size() + 1) {
703 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
704 return;
705 }
706 if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
707 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
708 return;
709 }
710 typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
711 typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
712 for (; s != validStops.end(); ++s, ++lineEdge) {
713 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
714 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
715 return;
716 }
717 }
718 SUMOTime lastTime = validStops.front().until;
719 if (lineEdges.front()->hasSchedule(lastTime)) {
720 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
721 }
722 for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
723 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
724 lastTime = s->until;
725 }
726 }
727 }
728
733 void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
734 assert(edge != nullptr);
735 assert(myCarLookup.count(edge) != 0);
736 assert(myBidiLookup.count(edge) != 0);
737 EdgePair pedestrianEdges = myBidiLookup[edge];
738 _IntermodalEdge* carEdge = myCarLookup[edge];
739 _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
740 addEdge(access);
741 pedestrianEdges.first->addSuccessor(access);
742 pedestrianEdges.second->addSuccessor(access);
743 access->addSuccessor(carEdge);
744 }
745
752 _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
753 addEdge(access);
754 from->addSuccessor(access);
755 access->addSuccessor(to);
756 }
757
758 bool isLooped(const std::string lineID) const {
759 return myLoopedLines.count(lineID) != 0;
760 }
761
762private:
774 int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
775 relPos = pos;
776 needSplit = true;
777 int splitIndex = 0;
778 const auto& splitList = myAccessSplits.find(toSplit);
779 if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
780 for (const _IntermodalEdge* const split : splitList->second) {
781 if (relPos < split->getLength() + POSITION_EPS) {
782 break;
783 }
784 relPos -= split->getLength();
785 splitIndex++;
786 }
787 assert(splitIndex < (int)splitList->second.size());
788 if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
789 needSplit = false;
790 }
791 }
792 return splitIndex;
793 }
794
807 void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
808 _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
809 _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
810 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
811 if (splitList.empty()) {
812 splitList.push_back(toSplit);
813 }
814 if (!forward) {
815 splitIndex = (int)splitList.size() - 1 - splitIndex;
816 if (!needSplit) {
817 splitIndex--;
818 }
819 }
820 _IntermodalEdge* beforeSplit = splitList[splitIndex];
821 if (needSplit) {
822 addEdge(afterSplit);
823 beforeSplit->transferSuccessors(afterSplit);
824 beforeSplit->addSuccessor(afterSplit);
825 if (forward) {
826 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
827 beforeSplit->setLength(relPos);
828 } else {
829 afterSplit->setLength(relPos);
830 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
831 // rename backward edges for easier referencing
832 const std::string newID = beforeSplit->getID();
833 beforeSplit->setID(afterSplit->getID());
834 afterSplit->setID(newID);
835 }
836 splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
837 } else {
838 // don't split, use the present split edges
839 afterSplit = splitList[splitIndex + 1];
840 }
841 // add access to / from edge
842 if (addEntry) {
843 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
844 addEdge(access);
845 beforeSplit->addSuccessor(access);
846 access->addSuccessor(stopConn);
847 }
848 if (addExit) {
849 // pedestrian case only, exit from public to pedestrian
850 _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
851 addEdge(exit);
852 stopConn->addSuccessor(exit);
853 exit->addSuccessor(afterSplit);
854 }
855 }
856
857
858private:
860 std::vector<_IntermodalEdge*> myEdges;
861
863 std::map<const E*, EdgePair> myBidiLookup;
864
866 std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
867
869 std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
870
872 std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
873
875 std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
876
878 std::map<std::string, std::vector<_PTEdge*> > myPTLines;
879
881 std::map<std::string, _IntermodalEdge*> myStopConnections;
882
884 std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
885
887 std::set<std::string > myLoopedLines;
888
891
892private:
895
896};
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:296
#define TL(string)
Definition MsgHandler.h:315
#define TLF(string,...)
Definition MsgHandler.h:317
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:69
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:266
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