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