Eclipse SUMO - Simulation of Urban MObility
IntermodalNetwork.h
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2001-2022 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:
77 };
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 tazDepart->addSuccessor(getDepartConnector(out));
159 getArrivalConnector(out)->addSuccessor(otherTazArrive);
160 }
161 for (const E* in : edge->getPredecessors()) {
162 getArrivalConnector(in)->addSuccessor(tazArrive);
163 otherTazDepart->addSuccessor(getDepartConnector(in));
164 }
165 continue;
166 }
167 const L* const sidewalk = getSidewalk<E, L>(edge);
168 if (sidewalk == nullptr) {
169 continue;
170 }
171 // find all incoming and outgoing lanes for the sidewalk and
172 // connect the corresponding IntermodalEdges
173 const EdgePair& pair = getBothDirections(edge);
174#ifdef IntermodalRouter_DEBUG_NETWORK
175 std::cout << " building connections from " << sidewalk->getID() << "\n";
176#endif
177 if (haveSeenWalkingArea) {
178 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
179 // if one of the outgoing lanes is a walking area it must be used.
180 // All other connections shall be ignored
181 // if it has no outgoing walking area, it probably is a walking area itself
182 bool hasWalkingArea = false;
183 for (const auto& target : outgoing) {
184 if (target.first->getEdge().isWalkingArea()) {
185 hasWalkingArea = true;
186 break;
187 }
188 }
189 for (const auto& target : outgoing) {
190 const E* const targetEdge = &(target.first->getEdge());
191 const bool used = (target.first == getSidewalk<E, L>(targetEdge)
192 && (!hasWalkingArea || targetEdge->isWalkingArea()));
193#ifdef IntermodalRouter_DEBUG_NETWORK
194 const L* potTarget = getSidewalk<E, L>(targetEdge);
195 std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
196#endif
197 if (used) {
198 const EdgePair& targetPair = getBothDirections(targetEdge);
199 pair.first->addSuccessor(targetPair.first);
200 targetPair.second->addSuccessor(pair.second);
201#ifdef IntermodalRouter_DEBUG_NETWORK
202 std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
203 std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
204#endif
205 }
206 }
207 }
208 // We may have a network without pedestrian structures or a car-only edge.
209 // In the first case we assume that all sidewalks at a junction are interconnected,
210 // in the second we connect all car-only edges to all sidewalks.
211 _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
212 if (toNodeConn != nullptr) {
213 // Check for the outgoing vias and use the shortest one as an approximation
214 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
215 double minViaLength = std::numeric_limits<double>::max();
216 const E* minVia = nullptr;
217 for (const auto& target : outgoing) {
218 if (target.second != nullptr && target.second->getLength() < minViaLength) {
219 minViaLength = target.second->getLength();
220 minVia = target.second;
221 }
222 }
223 EdgePair interVia = std::make_pair(nullptr, nullptr);
224 if (minVia != nullptr) {
225 const auto it = myBidiLookup.find(minVia);
226 if (it != myBidiLookup.end()) {
227 interVia = it->second;
228 }
229 }
230 if (!haveSeenWalkingArea) {
231 // if we have walking areas we should use them and not the connector
232 pair.first->addSuccessor(toNodeConn, interVia.first);
233 }
234 toNodeConn->addSuccessor(pair.second, interVia.second);
235 }
236 _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
237 if (fromNodeConn != nullptr) {
238 if (!haveSeenWalkingArea) {
239 pair.second->addSuccessor(fromNodeConn);
240 }
241 fromNodeConn->addSuccessor(pair.first);
242 }
243 if (!edge->isWalkingArea()) {
244 // build connections from depart connector
245 _IntermodalEdge* startConnector = getDepartConnector(edge);
246 startConnector->addSuccessor(pair.first);
247 startConnector->addSuccessor(pair.second);
248 // build connections to arrival connector
249 _IntermodalEdge* endConnector = getArrivalConnector(edge);
250 pair.first->addSuccessor(endConnector);
251 pair.second->addSuccessor(endConnector);
252 }
253#ifdef IntermodalRouter_DEBUG_NETWORK
254 std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
255 std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
256 std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
257 std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
258#endif
259 }
260 }
261
263 for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
264 delete *it;
265 }
266 }
267
269 while ((int)myEdges.size() <= edge->getNumericalID()) {
270 myEdges.push_back(0);
271 }
272 myEdges[edge->getNumericalID()] = edge;
273 }
274
275 void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
276 addEdge(depConn);
277 addEdge(arrConn);
278 myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
279 myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
280 }
281
282 const std::vector<_IntermodalEdge*>& getAllEdges() {
283 return myEdges;
284 }
285
287 const EdgePair& getBothDirections(const E* e) const {
288 typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
289 if (it == myBidiLookup.end()) {
290 assert(false);
291 throw ProcessError("Edge '" + e->getID() + "' not found in intermodal network.'");
292 }
293 return (*it).second;
294 }
295
297 const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
298 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
299 if (it == myDepartLookup.end()) {
300 throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
301 }
302 if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
303 // use most specific split (best trainStop, quay etc)
304 double bestDist = std::numeric_limits<double>::max();
305 const _IntermodalEdge* best = nullptr;
306 for (const _IntermodalEdge* const split : it->second) {
307 if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
308 const double dist = split->getEndPos() - split->getStartPos();
309 if (dist < bestDist) {
310 bestDist = dist;
311 best = split;
312 }
313 }
314 }
315 assert(best != nullptr);
316 return best;
317 } else {
318 // use next downstream edge
319 const std::vector<_IntermodalEdge*>& splitList = it->second;
320 typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
321 double totalLength = 0.;
322 while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
323 totalLength += (*splitIt)->getLength();
324 ++splitIt;
325 }
326 return *splitIt;
327 }
328 }
329
331 _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
332 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
333 if (it == myDepartLookup.end()) {
334 throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
335 }
336 if (splitIndex >= (int)it->second.size()) {
337 throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
338 }
339 return it->second[splitIndex];
340 }
341
343 _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
344 typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
345 if (it == myArrivalLookup.end()) {
346 throw ProcessError("Arrival edge '" + e->getID() + "' not found in intermodal network.");
347 }
348 const std::vector<_IntermodalEdge*>& splitList = it->second;
349 typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
350 double totalLength = 0.;
351 while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
352 totalLength += (*splitIt)->getLength();
353 ++splitIt;
354 }
355 return *splitIt;
356 }
357
359 _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
360 return myArrivalLookup.find(e)->second[splitIndex];
361 }
362
365 typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
366 if (it == myWalkingConnectorLookup.end()) {
367 const L* const sidewalk = getSidewalk<E, L>(e);
368 if (e->isInternal() || sidewalk == 0) {
369 return 0;
370 }
371 for (const auto& target : sidewalk->getOutgoingViaLanes()) {
372 if (target.first->getEdge().isWalkingArea()) {
373 return getBothDirections(&target.first->getEdge()).first;
374 }
375 }
376 return 0;
377 }
378 return it->second;
379 }
380
381 void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
382 for (const E* const edge : edges) {
383 if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
384 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
385 addEdge(myCarLookup[edge]);
386 }
387 }
388 for (const auto& edgePair : myCarLookup) {
389 _IntermodalEdge* const carEdge = edgePair.second;
390 // connectivity within the car network
391 for (const auto& suc : edgePair.first->getViaSuccessors()) {
392 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
393 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
394 if (sucCarEdge != nullptr) {
395 carEdge->addSuccessor(sucCarEdge, sucViaEdge);
396 }
397 }
398 // connectivity to the pedestrian network (only for normal edges)
399 if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
400 continue;
401 }
402 if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
403 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
404 if (walkCon != 0) {
405 carEdge->addSuccessor(walkCon);
406 } else {
407 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
408 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
409 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
410 carEdge->addSuccessor(getBothDirections(out).first);
411 }
412 }
413 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
414 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
415 carEdge->addSuccessor(getBothDirections(in).second);
416 }
417 }
418 }
419 }
421 // add access edges that allow exiting a taxi
422 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
423 if (walkCon != 0) {
424 addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
425 } else {
426 // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
427 for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
428 if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
429 addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
430 }
431 }
432 for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
433 if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
434 addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
435 }
436 }
437 }
438 }
439 // use intermediate access edge that prevents taxi departure
440 _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
441 _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
442 addEdge(access);
443 departConn->addSuccessor(access);
444 access->addSuccessor(carEdge);
445 if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
446 // taxi may depart anywhere but there is a time penalty
447 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
448 addEdge(taxiAccess);
449 departConn->addSuccessor(taxiAccess);
450 taxiAccess->addSuccessor(carEdge);
451 }
452 if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
453 // taxi (as all other cars) may arrive anywhere
454 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
455 } else {
456 // use intermediate access edge that prevents taxi arrival
457 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
458 }
459 }
460 }
461
463 _IntermodalEdge* getCarEdge(const E* e) const {
464 if (e == nullptr) {
465 return nullptr;
466 }
467 auto it = myCarLookup.find(e);
468 if (it == myCarLookup.end()) {
469 return nullptr;
470 }
471 return it->second;
472 }
473
475 _IntermodalEdge* getStopEdge(const std::string& stopId) const {
476 auto it = myStopConnections.find(stopId);
477 if (it == myStopConnections.end()) {
478 return nullptr;
479 }
480 return it->second;
481 }
482
500 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) {
501 assert(stopEdge != nullptr);
502 const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
503 (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
504 const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
505 const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
506 const double pos = (startPos + endPos) / 2.;
507#ifdef IntermodalRouter_DEBUG_ACCESS
508 std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " cat=" << category << "\n";
509#endif
510 if (myStopConnections.count(stopId) == 0) {
511 myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
512 addEdge(myStopConnections[stopId]);
513 }
514 _IntermodalEdge* const stopConn = myStopConnections[stopId];
515 const L* lane = getSidewalk<E, L>(stopEdge);
516 if (lane != nullptr) {
517 const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
518 double relPos;
519 bool needSplit;
520 const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
521 _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
522 splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
523 _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
524 splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
525 _IntermodalEdge* carSplit = nullptr;
526 if (myCarLookup.count(stopEdge) > 0) {
527 if (needSplit) {
528 carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
529 }
530 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
531 }
532 if (needSplit) {
533 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
534 // adding access from car to walk
535 _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
536 for (_IntermodalEdge* conn : {
537 fwdSplit, backSplit
538 }) {
539 if (transferCarWalk) {
540 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
541 addEdge(access);
542 beforeSplit->addSuccessor(access);
543 access->addSuccessor(conn);
544 } else if (transferTaxiWalk) {
545 addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
546 }
547 }
548 }
549 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
550 _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
551 addEdge(access);
552 stopConn->addSuccessor(access);
553 access->addSuccessor(carSplit);
554 }
555
556 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
557 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
558 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
559 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
560 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
561 depConn->addSuccessor(fwdSplit);
562 depConn->addSuccessor(backBeforeSplit);
563 depConn->setLength(fwdSplit->getLength());
564 prevDep->removeSuccessor(backBeforeSplit);
565 prevDep->addSuccessor(backSplit);
566 prevDep->setLength(backSplit->getLength());
567 if (carSplit != nullptr) {
568 depConn->addSuccessor(carSplit);
569 }
570
571 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
572 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
573 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
574 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
575 fwdSplit->addSuccessor(arrConn);
576 backBeforeSplit->addSuccessor(arrConn);
577 arrConn->setLength(fwdSplit->getLength());
578 fwdSplit->removeSuccessor(prevArr);
579 fwdBeforeSplit->addSuccessor(prevArr);
580 prevArr->setLength(backSplit->getLength());
581 if (carSplit != nullptr) {
582 if (carSplit->removeSuccessor(prevArr)) {
583 carSplit->addSuccessor(arrConn);
584 myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
585 }
586 }
587 addConnectors(depConn, arrConn, splitIndex + 1);
588 }
589 } else {
590 // pedestrians cannot walk here:
591 // add stop edge as depart connector so that pedestrians may start at the stop
592 std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
593 assert(splitList.size() > 0);
594 typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
595 while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
596 ++splitIt;
597 }
598 splitList.insert(splitIt, stopConn);
599 }
600 }
601
602 void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
603 SUMOTime lastUntil = 0;
604 std::vector<SUMOVehicleParameter::Stop> validStops;
605 if (addStops != nullptr) {
606 // stops are part of a stand-alone route. until times are offsets from vehicle departure
607 for (const SUMOVehicleParameter::Stop& stop : *addStops) {
608 if (myStopConnections.count(stop.busstop) > 0) {
609 // compute stop times for the first vehicle
610 const SUMOTime newUntil = stop.until + pars.depart;
611 if (newUntil >= lastUntil) {
612 validStops.push_back(stop);
613 validStops.back().until = newUntil;
614 lastUntil = newUntil;
615 } else {
616 WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
617 }
618 }
619 }
620 }
621 for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
622 // stops are part of the vehicle until times are absolute times for the first vehicle
623 if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
624 validStops.push_back(stop);
625 lastUntil = stop.until;
626 } else {
627 if (stop.busstop != "" && stop.until >= 0) {
628 WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
629 }
630 }
631 }
632 if (validStops.size() < 2 && pars.line != "taxi") {
633 WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
634 return;
635 }
636
637 typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
638 if (lineEdges.empty()) {
639 _IntermodalEdge* lastStop = nullptr;
640 Position lastPos;
641 SUMOTime lastTime = 0;
642 for (const SUMOVehicleParameter::Stop& s : validStops) {
643 _IntermodalEdge* currStop = myStopConnections[s.busstop];
644 Position stopPos = E::getStopPosition(s);
645 if (lastStop != nullptr) {
646 _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
647 addEdge(newEdge);
648 newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
649 lastStop->addSuccessor(newEdge);
650 newEdge->addSuccessor(currStop);
651 lineEdges.push_back(newEdge);
652 }
653 lastTime = s.until;
654 lastStop = currStop;
655 lastPos = stopPos;
656 }
657 } else {
658 if (validStops.size() != lineEdges.size() + 1) {
659 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
660 return;
661 }
662 if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
663 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
664 return;
665 }
666 typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
667 typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
668 for (; s != validStops.end(); ++s, ++lineEdge) {
669 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
670 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
671 return;
672 }
673 }
674 SUMOTime lastTime = validStops.front().until;
675 if (lineEdges.front()->hasSchedule(lastTime)) {
676 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
677 }
678 for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
679 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
680 lastTime = s->until;
681 }
682 }
683 }
684
689 void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
690 assert(edge != nullptr);
691 assert(myCarLookup.count(edge) != 0);
692 assert(myBidiLookup.count(edge) != 0);
693 EdgePair pedestrianEdges = myBidiLookup[edge];
694 _IntermodalEdge* carEdge = myCarLookup[edge];
695 _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
696 addEdge(access);
697 pedestrianEdges.first->addSuccessor(access);
698 pedestrianEdges.second->addSuccessor(access);
699 access->addSuccessor(carEdge);
700 }
701
708 _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
709 addEdge(access);
710 from->addSuccessor(access);
711 access->addSuccessor(to);
712 }
713
714private:
726 int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
727 relPos = pos;
728 needSplit = true;
729 int splitIndex = 0;
730 const auto& splitList = myAccessSplits.find(toSplit);
731 if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
732 for (const _IntermodalEdge* const split : splitList->second) {
733 if (relPos < split->getLength() + POSITION_EPS) {
734 break;
735 }
736 relPos -= split->getLength();
737 splitIndex++;
738 }
739 assert(splitIndex < (int)splitList->second.size());
740 if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
741 needSplit = false;
742 }
743 }
744 return splitIndex;
745 }
746
759 void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
760 _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
761 _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
762 std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
763 if (splitList.empty()) {
764 splitList.push_back(toSplit);
765 }
766 if (!forward) {
767 splitIndex = (int)splitList.size() - 1 - splitIndex;
768 if (!needSplit) {
769 splitIndex--;
770 }
771 }
772 _IntermodalEdge* beforeSplit = splitList[splitIndex];
773 if (needSplit) {
774 addEdge(afterSplit);
775 beforeSplit->transferSuccessors(afterSplit);
776 beforeSplit->addSuccessor(afterSplit);
777 if (forward) {
778 afterSplit->setLength(beforeSplit->getLength() - relPos);
779 beforeSplit->setLength(relPos);
780 } else {
781 afterSplit->setLength(relPos);
782 beforeSplit->setLength(beforeSplit->getLength() - relPos);
783 // rename backward edges for easier referencing
784 const std::string newID = beforeSplit->getID();
785 beforeSplit->setID(afterSplit->getID());
786 afterSplit->setID(newID);
787 }
788 splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
789 } else {
790 // don't split, use the present split edges
791 afterSplit = splitList[splitIndex + 1];
792 }
793 // add access to / from edge
794 if (addEntry) {
795 _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
796 addEdge(access);
797 beforeSplit->addSuccessor(access);
798 access->addSuccessor(stopConn);
799 }
800 if (addExit) {
801 // pedestrian case only, exit from public to pedestrian
802 _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
803 addEdge(exit);
804 stopConn->addSuccessor(exit);
805 exit->addSuccessor(afterSplit);
806 }
807 }
808
809
810private:
812 std::vector<_IntermodalEdge*> myEdges;
813
815 std::map<const E*, EdgePair> myBidiLookup;
816
818 std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
819
821 std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
822
824 std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
825
827 std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
828
830 std::map<std::string, std::vector<_PTEdge*> > myPTLines;
831
833 std::map<std::string, _IntermodalEdge*> myStopConnections;
834
836 std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
837
840
841private:
844
845};
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:266
#define TL(string)
Definition: MsgHandler.h:282
std::string time2string(SUMOTime t)
convert SUMOTime to string
Definition: SUMOTime.cpp:68
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
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:242
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)
SUMOTime until
The time at which the vehicle may continue its journey.
std::string busstop
(Optional) bus stop if one is assigned to the stop
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