Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/router/IntermodalNetwork.h
193871 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 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
/****************************************************************************/
14
/// @file IntermodalNetwork.h
15
/// @author Jakob Erdmann
16
/// @author Michael Behrisch
17
/// @author Robert Hilbrich
18
/// @date Mon, 03 March 2014
19
///
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>
29
#include <utils/common/MsgHandler.h>
30
#include <utils/common/Named.h>
31
#include <utils/common/SUMOTime.h>
32
#include <utils/common/ToString.h>
33
#include <utils/geom/Position.h>
34
#include <utils/vehicle/SUMOVehicleParameter.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
// ===========================================================================
49
/** @brief where mode changes are possible
50
*/
51
enum ModeChangeOptions {
52
/// @brief parking areas
53
PARKING_AREAS = 1 << 0,
54
/// @brief public transport stops and access
55
PT_STOPS = 1 << 1,
56
/// @brief junctions with edges allowing the additional mode
57
ALL_JUNCTIONS = 1 << 2,
58
/// @brief taxi customer may exit at parking areas
59
TAXI_DROPOFF_PARKING_AREAS = 1 << 3,
60
/// @brief taxi customer may exit at public transport stops
61
TAXI_DROPOFF_PT = 1 << 4,
62
/// @brief taxi customer may exit anywhere
63
TAXI_DROPOFF_ANYWHERE = 1 << 5,
64
/// @brief taxi customer may be picked up at parking areas
65
TAXI_PICKUP_PARKING_AREAS = 1 << 6,
66
/// @brief taxi customer may be picked up at public transport stops
67
TAXI_PICKUP_PT = 1 << 7,
68
/// @brief taxi customer may be picked up anywhere
69
TAXI_PICKUP_ANYWHERE = 1 << 8
70
};
71
72
73
74
/// @brief the intermodal network storing edges, connections and the mappings to the "real" edges
75
template<class E, class L, class N, class V>
76
class IntermodalNetwork {
77
private:
78
typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
79
typedef AccessEdge<E, L, N, V> _AccessEdge;
80
typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
81
typedef PublicTransportEdge<E, L, N, V> _PTEdge;
82
typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
83
84
public:
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), myHavePTSchedules(false) {
91
#ifdef IntermodalRouter_DEBUG_NETWORK
92
std::cout << "initIntermodalNetwork\n";
93
#endif
94
// build the pedestrian edges and the depart / arrival connectors with lookup tables
95
bool haveSeenWalkingArea = false;
96
for (const E* const edge : edges) {
97
if (edge->isTazConnector()) {
98
// only a single edge
99
_AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
100
addEdge(access);
101
myDepartLookup[edge].push_back(access);
102
myArrivalLookup[edge].push_back(access);
103
} else {
104
const L* lane = getSidewalk<E, L>(edge);
105
if (lane != nullptr) {
106
if (edge->isWalkingArea()) {
107
// only a single edge
108
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
109
myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
110
myDepartLookup[edge].push_back(myEdges.back());
111
myArrivalLookup[edge].push_back(myEdges.back());
112
haveSeenWalkingArea = true;
113
} else { // regular edge or crossing
114
// forward and backward edges
115
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
116
addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
117
myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
118
}
119
}
120
if (!edge->isWalkingArea()) {
121
// depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
122
_IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
123
_IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
124
addConnectors(departConn, arrivalConn, 0);
125
}
126
}
127
}
128
129
// build the walking connectors if there are no walking areas
130
for (const E* const edge : edges) {
131
if (edge->isTazConnector() || edge->isInternal()) {
132
continue;
133
}
134
if (haveSeenWalkingArea) {
135
// connectivity needs to be ensured only in the real intermodal case, for simple pedestrian routing we don't have connectors if we have walking areas
136
if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
137
const N* const node = edge->getToJunction();
138
if (myWalkingConnectorLookup.count(node) == 0) {
139
addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
140
myWalkingConnectorLookup[node] = myEdges.back();
141
}
142
}
143
} else {
144
for (const N* const node : {
145
edge->getFromJunction(), edge->getToJunction()
146
}) {
147
if (myWalkingConnectorLookup.count(node) == 0) {
148
addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
149
myWalkingConnectorLookup[node] = myEdges.back();
150
}
151
}
152
}
153
}
154
// build the connections
155
for (const E* const edge : edges) {
156
if (edge->isTazConnector()) {
157
// since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
158
_IntermodalEdge* const tazDepart = getDepartConnector(edge);
159
_IntermodalEdge* const tazArrive = getArrivalConnector(edge);
160
const E* other = edge->getOtherTazConnector();
161
_IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
162
_IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
163
for (const E* out : edge->getSuccessors()) {
164
if (out->isNormal()) {
165
tazDepart->addSuccessor(getDepartConnector(out));
166
getArrivalConnector(out)->addSuccessor(otherTazArrive);
167
}
168
}
169
for (const E* in : edge->getPredecessors()) {
170
if (in->isNormal()) {
171
getArrivalConnector(in)->addSuccessor(tazArrive);
172
otherTazDepart->addSuccessor(getDepartConnector(in));
173
}
174
}
175
continue;
176
}
177
const L* const sidewalk = getSidewalk<E, L>(edge);
178
if (sidewalk == nullptr) {
179
continue;
180
}
181
// find all incoming and outgoing lanes for the sidewalk and
182
// connect the corresponding IntermodalEdges
183
const EdgePair& pair = getBothDirections(edge);
184
#ifdef IntermodalRouter_DEBUG_NETWORK
185
std::cout << " building connections from " << sidewalk->getID() << "\n";
186
#endif
187
if (haveSeenWalkingArea) {
188
const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
189
// if one of the outgoing lanes is a walking area it must be used.
190
// All other connections shall be ignored
191
// if it has no outgoing walking area, it probably is a walking area itself
192
bool hasWalkingArea = false;
193
for (const auto& target : outgoing) {
194
if (target.first->getEdge().isWalkingArea()) {
195
hasWalkingArea = true;
196
break;
197
}
198
}
199
for (const auto& target : outgoing) {
200
const E* const targetEdge = &(target.first->getEdge());
201
const bool used = (target.first == getSidewalk<E, L>(targetEdge)
202
&& (!hasWalkingArea || targetEdge->isWalkingArea()));
203
#ifdef IntermodalRouter_DEBUG_NETWORK
204
const L* potTarget = getSidewalk<E, L>(targetEdge);
205
std::cout << " lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
206
#endif
207
if (used) {
208
const EdgePair& targetPair = getBothDirections(targetEdge);
209
pair.first->addSuccessor(targetPair.first);
210
targetPair.second->addSuccessor(pair.second);
211
#ifdef IntermodalRouter_DEBUG_NETWORK
212
std::cout << " " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
213
std::cout << " " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
214
#endif
215
}
216
}
217
}
218
// We may have a network without pedestrian structures or a car-only edge.
219
// In the first case we assume that all sidewalks at a junction are interconnected,
220
// in the second we connect all car-only edges to all sidewalks.
221
_IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
222
if (toNodeConn != nullptr) {
223
// Check for the outgoing vias and use the shortest one as an approximation
224
const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
225
double minViaLength = std::numeric_limits<double>::max();
226
const E* minVia = nullptr;
227
for (const auto& target : outgoing) {
228
if (target.second != nullptr && target.second->getLength() < minViaLength) {
229
minViaLength = target.second->getLength();
230
minVia = target.second;
231
}
232
}
233
EdgePair interVia = std::make_pair(nullptr, nullptr);
234
if (minVia != nullptr) {
235
const auto it = myBidiLookup.find(minVia);
236
if (it != myBidiLookup.end()) {
237
interVia = it->second;
238
}
239
}
240
if (!haveSeenWalkingArea) {
241
// if we have walking areas we should use them and not the connector
242
pair.first->addSuccessor(toNodeConn, interVia.first);
243
}
244
toNodeConn->addSuccessor(pair.second, interVia.second);
245
}
246
_IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
247
if (fromNodeConn != nullptr) {
248
if (!haveSeenWalkingArea) {
249
pair.second->addSuccessor(fromNodeConn);
250
}
251
fromNodeConn->addSuccessor(pair.first);
252
}
253
if (!edge->isWalkingArea()) {
254
// build connections from depart connector
255
_IntermodalEdge* startConnector = getDepartConnector(edge);
256
startConnector->addSuccessor(pair.first);
257
startConnector->addSuccessor(pair.second);
258
// build connections to arrival connector
259
_IntermodalEdge* endConnector = getArrivalConnector(edge);
260
pair.first->addSuccessor(endConnector);
261
pair.second->addSuccessor(endConnector);
262
#ifdef IntermodalRouter_DEBUG_NETWORK
263
std::cout << " " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
264
std::cout << " " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
265
std::cout << " " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
266
std::cout << " " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
267
#endif
268
}
269
}
270
}
271
272
~IntermodalNetwork() {
273
for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
274
delete *it;
275
}
276
}
277
278
void addEdge(_IntermodalEdge* edge) {
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
296
/// @brief Returns the pair of forward and backward edge
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
306
/// @brief Returns the departing intermodal edge
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
340
/// @brief Returns the departing intermodal connector at the given split offset
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
352
/// @brief Returns the arriving intermodal edge
353
_IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
354
typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
355
if (it == myArrivalLookup.end()) {
356
throw ProcessError(TLF("Arrival edge '%' not found in intermodal network.", e->getID()));
357
}
358
const std::vector<_IntermodalEdge*>& splitList = it->second;
359
typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
360
double totalLength = 0.;
361
while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
362
totalLength += (*splitIt)->getLength();
363
++splitIt;
364
}
365
if (splitIt != splitList.end()) {
366
return *splitIt;
367
} else {
368
return splitList.back();
369
}
370
}
371
372
/// @brief Returns the arriving intermodal connector at the given split offset
373
_IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
374
return myArrivalLookup.find(e)->second[splitIndex];
375
}
376
377
/// @brief Returns the outgoing pedestrian edge, which is either a walking area or a walking connector
378
_IntermodalEdge* getWalkingConnector(const E* e) const {
379
typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
380
if (it == myWalkingConnectorLookup.end()) {
381
const L* const sidewalk = getSidewalk<E, L>(e);
382
if (e->isInternal() || sidewalk == 0) {
383
return 0;
384
}
385
for (const auto& target : sidewalk->getOutgoingViaLanes()) {
386
if (target.first->getEdge().isWalkingArea()) {
387
return getBothDirections(&target.first->getEdge()).first;
388
}
389
}
390
return 0;
391
}
392
return it->second;
393
}
394
395
void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
396
for (const E* const edge : edges) {
397
if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
398
myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
399
addEdge(myCarLookup[edge]);
400
}
401
}
402
for (const auto& edgePair : myCarLookup) {
403
_IntermodalEdge* const carEdge = edgePair.second;
404
// connectivity within the car network
405
for (const auto& suc : edgePair.first->getViaSuccessors()) {
406
_IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
407
_IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
408
if (sucCarEdge != nullptr) {
409
carEdge->addSuccessor(sucCarEdge, sucViaEdge);
410
}
411
}
412
// connectivity to the pedestrian network (only for normal edges)
413
if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
414
continue;
415
}
416
if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
417
_IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
418
if (walkCon != 0) {
419
carEdge->addSuccessor(walkCon);
420
} else {
421
// we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
422
for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
423
if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
424
carEdge->addSuccessor(getBothDirections(out).first);
425
}
426
}
427
for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
428
if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
429
carEdge->addSuccessor(getBothDirections(in).second);
430
}
431
}
432
}
433
}
434
if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
435
// add access edges that allow exiting a taxi
436
_IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
437
if (walkCon != 0) {
438
addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
439
} else {
440
// we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
441
for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
442
if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
443
addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
444
}
445
}
446
for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
447
if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
448
addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
449
}
450
}
451
}
452
}
453
// use intermediate access edge that prevents taxi departure
454
_IntermodalEdge* departConn = getDepartConnector(edgePair.first);
455
_AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
456
addEdge(access);
457
departConn->addSuccessor(access);
458
access->addSuccessor(carEdge);
459
if ((myCarWalkTransfer & TAXI_PICKUP_ANYWHERE) != 0) {
460
// taxi may depart anywhere but there is a time penalty
461
_AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
462
addEdge(taxiAccess);
463
departConn->addSuccessor(taxiAccess);
464
taxiAccess->addSuccessor(carEdge);
465
}
466
if ((myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
467
// taxi (as all other cars) may arrive anywhere
468
carEdge->addSuccessor(getArrivalConnector(edgePair.first));
469
} else {
470
// use intermediate access edge that prevents taxi arrival
471
addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
472
}
473
}
474
}
475
476
/// @brief Returns the associated car edge
477
_IntermodalEdge* getCarEdge(const E* e) const {
478
if (e == nullptr) {
479
return nullptr;
480
}
481
auto it = myCarLookup.find(e);
482
if (it == myCarLookup.end()) {
483
return nullptr;
484
}
485
return it->second;
486
}
487
488
/// @brief Returns the associated stop edge
489
_IntermodalEdge* getStopEdge(const std::string& stopId) const {
490
auto it = myStopConnections.find(stopId);
491
if (it == myStopConnections.end()) {
492
return nullptr;
493
}
494
return it->second;
495
}
496
497
/** @brief Adds access edges for stopping places to the intermodal network
498
*
499
* This method creates an intermodal stop edge to represent the stopping place
500
* (if not present yet) and determines the edges which need to be splitted (usually the forward
501
* and the backward pedestrian edges and the car edge) and calls splitEdge for the
502
* actual split and the connection of the stop edge with access edges. After that it adds and adapts
503
* the depart and arrival connectors to the new edge(s).
504
*
505
* @param[in] stopId The id of the stop to add
506
* @param[in] stopEdge The edge on which the stop is located
507
* @param[in] startPos The relative position on the edge where the stop starts
508
* @param[in] endPos The relative position on the edge where the stop ends
509
* @param[in] length The length of the access edge to build
510
* @param[in] category The type of stop
511
* @param[in] isAccess Whether an <access> element is being connected
512
* @param[in] taxiWait Expected time to wait for a taxi
513
*/
514
void addAccess(const std::string& stopId, const E* stopEdge, const double startPos, const double endPos, const double length, const SumoXMLTag category, bool isAccess, double taxiWait) {
515
assert(stopEdge != nullptr);
516
const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
517
(category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
518
const bool transferTaxiWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_DROPOFF_PARKING_AREAS) != 0) ||
519
(category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0));
520
const bool transferWalkTaxi = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_PICKUP_PARKING_AREAS) != 0) ||
521
(category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0));
522
const double pos = (startPos + endPos) / 2.;
523
#ifdef IntermodalRouter_DEBUG_ACCESS
524
std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
525
<< " access=" << isAccess << " tWait=" << taxiWait << "\n";
526
#endif
527
if (myStopConnections.count(stopId) == 0) {
528
myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
529
addEdge(myStopConnections[stopId]);
530
}
531
_IntermodalEdge* const stopConn = myStopConnections[stopId];
532
const L* lane = getSidewalk<E, L>(stopEdge);
533
if (lane != nullptr) {
534
const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
535
double relPos;
536
bool needSplit;
537
const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
538
_IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
539
splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
540
_IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
541
splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
542
_IntermodalEdge* carSplit = nullptr;
543
if (myCarLookup.count(stopEdge) > 0) {
544
if (needSplit) {
545
carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
546
}
547
splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
548
}
549
if (needSplit) {
550
if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
551
// adding access from car to walk
552
_IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
553
for (_IntermodalEdge* conn : {
554
fwdSplit, backSplit
555
}) {
556
if (transferCarWalk) {
557
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
558
addEdge(access);
559
beforeSplit->addSuccessor(access);
560
access->addSuccessor(conn);
561
} else if (transferTaxiWalk) {
562
addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
563
}
564
}
565
}
566
if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
567
_AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
568
addEdge(access);
569
stopConn->addSuccessor(access);
570
access->addSuccessor(carSplit);
571
}
572
573
// fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
574
_IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
575
const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
576
_IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
577
_IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
578
depConn->addSuccessor(fwdSplit);
579
depConn->addSuccessor(backBeforeSplit);
580
depConn->setLength(fwdSplit->getLength());
581
prevDep->removeSuccessor(backBeforeSplit);
582
prevDep->addSuccessor(backSplit);
583
prevDep->setLength(backSplit->getLength());
584
if (carSplit != nullptr) {
585
depConn->addSuccessor(carSplit);
586
}
587
588
// fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
589
_IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
590
_IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
591
_IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
592
fwdSplit->addSuccessor(arrConn);
593
backBeforeSplit->addSuccessor(arrConn);
594
arrConn->setLength(fwdSplit->getLength());
595
fwdSplit->removeSuccessor(prevArr);
596
fwdBeforeSplit->addSuccessor(prevArr);
597
prevArr->setLength(backSplit->getLength());
598
if (carSplit != nullptr) {
599
if (carSplit->removeSuccessor(prevArr)) {
600
carSplit->addSuccessor(arrConn);
601
myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
602
} else {
603
// check for restricted access
604
for (_IntermodalEdge* out : carSplit->getSuccessors()) {
605
_AccessEdge* aOut = dynamic_cast<_AccessEdge*>(out);
606
if (aOut != nullptr && aOut->removeSuccessor(prevArr)) {
607
aOut->addSuccessor(arrConn);
608
addRestrictedCarExit(
609
myAccessSplits[myCarLookup[stopEdge]][splitIndex],
610
prevArr,
611
aOut->getVehicleRetriction());
612
break;
613
}
614
}
615
}
616
}
617
addConnectors(depConn, arrConn, splitIndex + 1);
618
}
619
} else {
620
// pedestrians cannot walk here:
621
// add stop edge as depart connector so that pedestrians may start at the stop
622
std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
623
assert(splitList.size() > 0);
624
typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
625
while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
626
++splitIt;
627
}
628
splitList.insert(splitIt, stopConn);
629
630
if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
631
_IntermodalEdge* carEdge = myCarLookup[stopEdge];
632
double relPos;
633
bool needSplit;
634
const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
635
if (needSplit) {
636
_IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
637
splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
638
639
if (transferCarWalk || transferTaxiWalk) {
640
// adding access from car to walk
641
_IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
642
if (transferCarWalk) {
643
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
644
addEdge(access);
645
beforeSplit->addSuccessor(access);
646
access->addSuccessor(stopConn);
647
} else if (transferTaxiWalk) {
648
addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
649
}
650
}
651
if (transferWalkTaxi) {
652
_AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
653
addEdge(access);
654
stopConn->addSuccessor(access);
655
access->addSuccessor(carSplit);
656
}
657
}
658
}
659
}
660
}
661
662
bool hasPTSchedules() const {
663
return myHavePTSchedules;
664
}
665
666
void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
667
SUMOTime lastUntil = 0;
668
StopParVector validStops;
669
if (addStops != nullptr) {
670
// stops are part of a stand-alone route. until times are offsets from vehicle departure
671
for (const SUMOVehicleParameter::Stop& stop : *addStops) {
672
if (myStopConnections.count(stop.busstop) > 0) {
673
// compute stop times for the first vehicle
674
const SUMOTime newUntil = stop.until + pars.depart;
675
if (newUntil >= lastUntil) {
676
validStops.push_back(stop);
677
validStops.back().until = newUntil;
678
lastUntil = newUntil;
679
} else {
680
WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
681
}
682
}
683
}
684
}
685
for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
686
// stops are part of the vehicle until times are absolute times for the first vehicle
687
if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
688
validStops.push_back(stop);
689
lastUntil = stop.until;
690
} else {
691
if (stop.busstop != "" && stop.until >= 0) {
692
WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
693
}
694
}
695
}
696
if (validStops.size() < 2 && pars.line != "taxi") {
697
WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
698
return;
699
}
700
701
typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
702
if (lineEdges.empty()) {
703
_IntermodalEdge* lastStop = nullptr;
704
Position lastPos;
705
SUMOTime lastTime = 0;
706
for (const SUMOVehicleParameter::Stop& s : validStops) {
707
_IntermodalEdge* currStop = myStopConnections[s.busstop];
708
Position stopPos = E::getStopPosition(s);
709
if (lastStop != nullptr) {
710
_PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
711
addEdge(newEdge);
712
newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
713
myHavePTSchedules = true;
714
lastStop->addSuccessor(newEdge);
715
newEdge->addSuccessor(currStop);
716
lineEdges.push_back(newEdge);
717
}
718
lastTime = s.until;
719
lastStop = currStop;
720
lastPos = stopPos;
721
}
722
if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
723
myLoopedLines.insert(pars.line);
724
}
725
} else {
726
if (validStops.size() != lineEdges.size() + 1) {
727
WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
728
return;
729
}
730
if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
731
WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
732
return;
733
}
734
typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
735
typename StopParVector::const_iterator s = validStops.begin() + 1;
736
for (; s != validStops.end(); ++s, ++lineEdge) {
737
if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
738
WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
739
return;
740
}
741
}
742
SUMOTime lastTime = validStops.front().until;
743
if (lineEdges.front()->hasSchedule(lastTime)) {
744
WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
745
}
746
for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
747
(*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
748
myHavePTSchedules = true;
749
lastTime = s->until;
750
}
751
}
752
}
753
754
/** @brief Adds access edges for transfering from walking to vehicle use
755
* @param[in] edge The edge on which the transfer takes place
756
* @param[in] svc The permitted vehicle class for transfering
757
*/
758
void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
759
assert(edge != nullptr);
760
assert(myCarLookup.count(edge) != 0);
761
assert(myBidiLookup.count(edge) != 0);
762
EdgePair pedestrianEdges = myBidiLookup[edge];
763
_IntermodalEdge* carEdge = myCarLookup[edge];
764
_AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
765
addEdge(access);
766
pedestrianEdges.first->addSuccessor(access);
767
pedestrianEdges.second->addSuccessor(access);
768
access->addSuccessor(carEdge);
769
}
770
771
/** @brief Adds access edges for transfering from driving to walking that are only usable by a particular vehicle class
772
* @param[in] from The origin edge of the transfer
773
* @param[in] to The destination edge of the transfer
774
* @param[in] svc The permitted vehicle class for transfering
775
*/
776
void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
777
_AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
778
addEdge(access);
779
from->addSuccessor(access);
780
access->addSuccessor(to);
781
}
782
783
bool isLooped(const std::string lineID) const {
784
return myLoopedLines.count(lineID) != 0;
785
}
786
787
private:
788
/** @brief Returns where to insert or use the split edge
789
*
790
* This method determines whether an edge needs to be split at the given position
791
* (if there is not already a split nearby) and returns the corresponding index in the split list.
792
*
793
* @param[in] toSplit The first edge in the split list
794
* @param[in] pos The relative position on the edge where the stop is located
795
* @param[out] relPos The relative position on the splitted edge
796
* @param[out] needSplit whether a new split is needed or we reuse an exisiting one
797
* @return the index in the split list where the split edge needs to be added or reused
798
*/
799
int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
800
relPos = pos;
801
needSplit = true;
802
int splitIndex = 0;
803
const auto& splitList = myAccessSplits.find(toSplit);
804
if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
805
for (const _IntermodalEdge* const split : splitList->second) {
806
if (relPos < split->getLength() + POSITION_EPS) {
807
break;
808
}
809
relPos -= split->getLength();
810
splitIndex++;
811
}
812
assert(splitIndex < (int)splitList->second.size());
813
if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
814
needSplit = false;
815
}
816
}
817
return splitIndex;
818
}
819
820
/** @brief Splits an edge (if necessary) and connects it to a stopping edge
821
*
822
* This method determines whether an edge needs to be split at the given position
823
* (if there is not already a split nearby) and connects the stop edge via new access edges.
824
*
825
* @param[in] toSplit The first edge in the split list
826
* @param[in] afterSplit The edge to add if a split is performed
827
* @param[in] pos The relative position on the edge where the stop is located
828
* @param[in] stopConn The stop edge to connect to
829
* @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
830
* @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
831
*/
832
void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
833
_IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
834
_IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
835
std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
836
if (splitList.empty()) {
837
splitList.push_back(toSplit);
838
}
839
if (!forward) {
840
splitIndex = (int)splitList.size() - 1 - splitIndex;
841
if (!needSplit) {
842
splitIndex--;
843
}
844
}
845
_IntermodalEdge* beforeSplit = splitList[splitIndex];
846
if (needSplit) {
847
addEdge(afterSplit);
848
beforeSplit->transferSuccessors(afterSplit);
849
beforeSplit->addSuccessor(afterSplit);
850
if (forward) {
851
afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
852
beforeSplit->setLength(relPos);
853
} else {
854
afterSplit->setLength(relPos);
855
beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
856
// rename backward edges for easier referencing
857
const std::string newID = beforeSplit->getID();
858
beforeSplit->setID(afterSplit->getID());
859
afterSplit->setID(newID);
860
}
861
splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
862
} else {
863
// don't split, use the present split edges
864
afterSplit = splitList[splitIndex + 1];
865
}
866
// add access to / from edge
867
if (addEntry) {
868
_AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
869
addEdge(access);
870
beforeSplit->addSuccessor(access);
871
access->addSuccessor(stopConn);
872
}
873
if (addExit) {
874
// pedestrian case only, exit from public to pedestrian
875
_AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
876
addEdge(exit);
877
stopConn->addSuccessor(exit);
878
exit->addSuccessor(afterSplit);
879
}
880
}
881
882
883
private:
884
/// @brief the edge dictionary
885
std::vector<_IntermodalEdge*> myEdges;
886
887
/// @brief retrieve the forward and backward edge for the given input edge E
888
std::map<const E*, EdgePair> myBidiLookup;
889
890
/// @brief retrieve the depart edges for the given input edge E
891
std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
892
893
/// @brief retrieve the arrival edges for the given input edge E
894
std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
895
896
/// @brief the walking connector edge (fake walking area)
897
std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
898
899
/// @brief retrieve the car edge for the given input edge E
900
std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
901
902
/// @brief retrieve the public transport edges for the given line
903
std::map<std::string, std::vector<_PTEdge*> > myPTLines;
904
905
/// @brief retrieve the representing edge for the given stopping place
906
std::map<std::string, _IntermodalEdge*> myStopConnections;
907
908
/// @brief retrieve the splitted edges for the given "original"
909
std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
910
911
/// @brief looped lines need extra checking when building itineraries
912
std::set<std::string > myLoopedLines;
913
914
int myNumericalID;
915
const int myCarWalkTransfer;
916
bool myHavePTSchedules;
917
918
private:
919
/// @brief Invalidated assignment operator
920
IntermodalNetwork& operator=(const IntermodalNetwork& s);
921
922
};
923
924