Line data Source code
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 12149 : IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
90 12149 : : 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 1044045 : for (const E* const edge : edges) {
97 1031896 : if (edge->isTazConnector()) {
98 : // only a single edge
99 44319 : _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
100 44319 : addEdge(access);
101 44319 : myDepartLookup[edge].push_back(access);
102 44319 : myArrivalLookup[edge].push_back(access);
103 : } else {
104 443119 : const L* lane = getSidewalk<E, L>(edge);
105 987577 : if (lane != nullptr) {
106 670900 : if (edge->isWalkingArea()) {
107 : // only a single edge
108 136870 : addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
109 136870 : myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
110 136870 : myDepartLookup[edge].push_back(myEdges.back());
111 136870 : myArrivalLookup[edge].push_back(myEdges.back());
112 : haveSeenWalkingArea = true;
113 : } else { // regular edge or crossing
114 : // forward and backward edges
115 534030 : addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
116 534030 : addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
117 534030 : myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
118 : }
119 : }
120 987577 : if (!edge->isWalkingArea()) {
121 : // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
122 1701414 : _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
123 1701414 : _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
124 850707 : addConnectors(departConn, arrivalConn, 0);
125 : }
126 : }
127 : }
128 :
129 : // build the walking connectors if there are no walking areas
130 1044045 : for (const E* const edge : edges) {
131 1031896 : if (edge->isTazConnector() || edge->isInternal()) {
132 546286 : continue;
133 : }
134 485610 : 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 490689 : if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
137 17418 : const N* const node = edge->getToJunction();
138 : if (myWalkingConnectorLookup.count(node) == 0) {
139 23384 : addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
140 11692 : myWalkingConnectorLookup[node] = myEdges.back();
141 : }
142 : }
143 : } else {
144 319485 : for (const N* const node : {
145 : edge->getFromJunction(), edge->getToJunction()
146 : }) {
147 : if (myWalkingConnectorLookup.count(node) == 0) {
148 128924 : addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
149 64462 : myWalkingConnectorLookup[node] = myEdges.back();
150 : }
151 : }
152 : }
153 : }
154 : // build the connections
155 1044045 : for (const E* const edge : edges) {
156 1031896 : if (edge->isTazConnector()) {
157 : // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
158 44319 : _IntermodalEdge* const tazDepart = getDepartConnector(edge);
159 : _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
160 : const E* other = edge->getOtherTazConnector();
161 44319 : _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
162 : _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
163 89254 : for (const E* out : edge->getSuccessors()) {
164 44935 : if (out->isNormal()) {
165 73648 : tazDepart->addSuccessor(getDepartConnector(out));
166 36824 : getArrivalConnector(out)->addSuccessor(otherTazArrive);
167 : }
168 : }
169 89277 : for (const E* in : edge->getPredecessors()) {
170 44958 : if (in->isNormal()) {
171 36847 : getArrivalConnector(in)->addSuccessor(tazArrive);
172 73694 : otherTazDepart->addSuccessor(getDepartConnector(in));
173 : }
174 : }
175 44319 : continue;
176 44319 : }
177 443119 : const L* const sidewalk = getSidewalk<E, L>(edge);
178 987577 : if (sidewalk == nullptr) {
179 316677 : continue;
180 : }
181 : // find all incoming and outgoing lanes for the sidewalk and
182 : // connect the corresponding IntermodalEdges
183 670900 : const EdgePair& pair = getBothDirections(edge);
184 : #ifdef IntermodalRouter_DEBUG_NETWORK
185 : std::cout << " building connections from " << sidewalk->getID() << "\n";
186 : #endif
187 670900 : if (haveSeenWalkingArea) {
188 408951 : 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 730888 : for (const auto& target : outgoing) {
194 517864 : if (target.first->getEdge().isWalkingArea()) {
195 : hasWalkingArea = true;
196 : break;
197 : }
198 : }
199 926815 : for (const auto& target : outgoing) {
200 517864 : const E* const targetEdge = &(target.first->getEdge());
201 173656 : const bool used = (target.first == getSidewalk<E, L>(targetEdge)
202 517864 : && (!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 450521 : const EdgePair& targetPair = getBothDirections(targetEdge);
209 450521 : pair.first->addSuccessor(targetPair.first);
210 901042 : 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 408951 : }
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 670900 : _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
222 670900 : if (toNodeConn != nullptr) {
223 : // Check for the outgoing vias and use the shortest one as an approximation
224 278956 : 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 678168 : for (const auto& target : outgoing) {
228 399212 : 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 278956 : if (minVia != nullptr) {
235 : const auto it = myBidiLookup.find(minVia);
236 80082 : if (it != myBidiLookup.end()) {
237 : interVia = it->second;
238 : }
239 : }
240 278956 : if (!haveSeenWalkingArea) {
241 : // if we have walking areas we should use them and not the connector
242 523898 : pair.first->addSuccessor(toNodeConn, interVia.first);
243 : }
244 278956 : toNodeConn->addSuccessor(pair.second, interVia.second);
245 278956 : }
246 670900 : _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
247 670900 : if (fromNodeConn != nullptr) {
248 279202 : if (!haveSeenWalkingArea) {
249 523898 : pair.second->addSuccessor(fromNodeConn);
250 : }
251 558404 : fromNodeConn->addSuccessor(pair.first);
252 : }
253 670900 : if (!edge->isWalkingArea()) {
254 : // build connections from depart connector
255 534030 : _IntermodalEdge* startConnector = getDepartConnector(edge);
256 534030 : startConnector->addSuccessor(pair.first);
257 1068060 : startConnector->addSuccessor(pair.second);
258 : // build connections to arrival connector
259 : _IntermodalEdge* endConnector = getArrivalConnector(edge);
260 1068060 : pair.first->addSuccessor(endConnector);
261 1068060 : 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 12149 : }
271 :
272 12148 : ~IntermodalNetwork() {
273 4726446 : for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
274 4714298 : delete *it;
275 : }
276 12148 : }
277 :
278 4714390 : void addEdge(_IntermodalEdge* edge) {
279 9428780 : while ((int)myEdges.size() <= edge->getNumericalID()) {
280 4714390 : myEdges.push_back(0);
281 : }
282 4714390 : myEdges[edge->getNumericalID()] = edge;
283 4714390 : }
284 :
285 878958 : void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
286 878958 : addEdge(depConn);
287 878958 : addEdge(arrConn);
288 878958 : myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
289 878958 : myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
290 878958 : }
291 :
292 : const std::vector<_IntermodalEdge*>& getAllEdges() {
293 18368 : return myEdges;
294 : }
295 :
296 : /// @brief Returns the pair of forward and backward edge
297 3274575 : const EdgePair& getBothDirections(const E* e) const {
298 : typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
299 3274575 : if (it == myBidiLookup.end()) {
300 : assert(false);
301 0 : throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
302 : }
303 3274575 : return (*it).second;
304 : }
305 :
306 : /// @brief Returns the departing intermodal edge
307 154364 : 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 154364 : if (it == myDepartLookup.end()) {
310 0 : throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
311 : }
312 154364 : 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 141 : for (const _IntermodalEdge* const split : it->second) {
317 78 : if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
318 68 : const double dist = split->getEndPos() - split->getStartPos();
319 68 : if (dist < bestDist) {
320 : bestDist = dist;
321 : best = split;
322 : }
323 : }
324 : }
325 : assert(best != nullptr);
326 63 : 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 155592 : while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
333 : totalLength += (*splitIt)->getLength();
334 : ++splitIt;
335 : }
336 154301 : return *splitIt;
337 : }
338 : }
339 :
340 : /// @brief Returns the departing intermodal connector at the given split offset
341 1702663 : _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 1702663 : if (it == myDepartLookup.end()) {
344 0 : throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
345 : }
346 1702663 : if (splitIndex >= (int)it->second.size()) {
347 0 : throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
348 : }
349 1702663 : return it->second[splitIndex];
350 : }
351 :
352 : /// @brief Returns the arriving intermodal edge
353 143334 : _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 143334 : if (it == myArrivalLookup.end()) {
356 0 : 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 377477 : while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
362 : totalLength += (*splitIt)->getLength();
363 : ++splitIt;
364 : }
365 143334 : if (splitIt != splitList.end()) {
366 143334 : return *splitIt;
367 : } else {
368 0 : 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 1600915 : 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 87869 : _IntermodalEdge* getWalkingConnector(const E* e) const {
379 : typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
380 87869 : if (it == myWalkingConnectorLookup.end()) {
381 0 : const L* const sidewalk = getSidewalk<E, L>(e);
382 0 : if (e->isInternal() || sidewalk == 0) {
383 : return 0;
384 : }
385 0 : for (const auto& target : sidewalk->getOutgoingViaLanes()) {
386 0 : if (target.first->getEdge().isWalkingArea()) {
387 0 : return getBothDirections(&target.first->getEdge()).first;
388 : }
389 : }
390 0 : return 0;
391 : }
392 87869 : return it->second;
393 : }
394 :
395 5880 : void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
396 567107 : for (const E* const edge : edges) {
397 561227 : if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
398 445129 : myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
399 445129 : addEdge(myCarLookup[edge]);
400 : }
401 : }
402 451009 : for (const auto& edgePair : myCarLookup) {
403 445129 : _IntermodalEdge* const carEdge = edgePair.second;
404 : // connectivity within the car network
405 1103250 : for (const auto& suc : edgePair.first->getViaSuccessors()) {
406 1316242 : _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
407 1316242 : _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
408 658121 : if (sucCarEdge != nullptr) {
409 555559 : carEdge->addSuccessor(sucCarEdge, sucViaEdge);
410 : }
411 : }
412 : // connectivity to the pedestrian network (only for normal edges)
413 445129 : if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
414 284027 : continue;
415 : }
416 161102 : if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
417 324 : _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
418 324 : if (walkCon != 0) {
419 268 : 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 216 : for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
423 160 : if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
424 208 : carEdge->addSuccessor(getBothDirections(out).first);
425 : }
426 : }
427 232 : for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
428 176 : if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
429 240 : carEdge->addSuccessor(getBothDirections(in).second);
430 : }
431 : }
432 : }
433 : }
434 161102 : if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
435 : // add access edges that allow exiting a taxi
436 87545 : _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
437 87545 : if (walkCon != 0) {
438 31954 : addRestrictedCarExit(carEdge, walkCon, gTaxiClasses);
439 : } else {
440 : // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
441 612912 : for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
442 743942 : if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
443 243183 : addRestrictedCarExit(carEdge, getBothDirections(out).first, gTaxiClasses);
444 : }
445 : }
446 623684 : for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
447 765567 : if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
448 254445 : addRestrictedCarExit(carEdge, getBothDirections(in).second, gTaxiClasses);
449 : }
450 : }
451 : }
452 : }
453 : // use intermediate access edge that prevents taxi departure
454 161102 : _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
455 161102 : _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
456 161102 : addEdge(access);
457 161102 : departConn->addSuccessor(access);
458 161102 : access->addSuccessor(carEdge);
459 161102 : if ((myCarWalkTransfer & TAXI_PICKUP_ANYWHERE) != 0) {
460 : // taxi may depart anywhere but there is a time penalty
461 87105 : _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
462 87105 : addEdge(taxiAccess);
463 87105 : departConn->addSuccessor(taxiAccess);
464 87105 : taxiAccess->addSuccessor(carEdge);
465 : }
466 161102 : if ((myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
467 : // taxi (as all other cars) may arrive anywhere
468 175210 : carEdge->addSuccessor(getArrivalConnector(edgePair.first));
469 : } else {
470 : // use intermediate access edge that prevents taxi arrival
471 146994 : addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~gTaxiClasses));
472 : }
473 : }
474 5880 : }
475 :
476 : /// @brief Returns the associated car edge
477 : _IntermodalEdge* getCarEdge(const E* e) const {
478 1316242 : if (e == nullptr) {
479 : return nullptr;
480 : }
481 : auto it = myCarLookup.find(e);
482 935659 : if (it == myCarLookup.end()) {
483 : return nullptr;
484 : }
485 833097 : 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 50524 : if (it == myStopConnections.end()) {
492 : return nullptr;
493 : }
494 50524 : 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 31765 : 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 31765 : const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
517 17072 : (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
518 31765 : const bool transferTaxiWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_DROPOFF_PARKING_AREAS) != 0) ||
519 17072 : (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0));
520 31765 : const bool transferWalkTaxi = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_PICKUP_PARKING_AREAS) != 0) ||
521 17072 : (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0));
522 31765 : 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 56714 : myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
529 28357 : addEdge(myStopConnections[stopId]);
530 : }
531 31765 : _IntermodalEdge* const stopConn = myStopConnections[stopId];
532 31765 : const L* lane = getSidewalk<E, L>(stopEdge);
533 31765 : if (lane != nullptr) {
534 28390 : const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
535 : double relPos;
536 : bool needSplit;
537 28390 : const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
538 28390 : _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
539 28390 : splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
540 28390 : _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
541 28390 : splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
542 : _IntermodalEdge* carSplit = nullptr;
543 : if (myCarLookup.count(stopEdge) > 0) {
544 28390 : if (needSplit) {
545 28251 : carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
546 : }
547 28390 : splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
548 : }
549 28390 : if (needSplit) {
550 28251 : if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
551 : // adding access from car to walk
552 5883 : _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
553 17649 : for (_IntermodalEdge* conn : {
554 : fwdSplit, backSplit
555 : }) {
556 11766 : if (transferCarWalk) {
557 11296 : _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
558 11296 : addEdge(access);
559 11296 : beforeSplit->addSuccessor(access);
560 11296 : access->addSuccessor(conn);
561 470 : } else if (transferTaxiWalk) {
562 470 : addRestrictedCarExit(beforeSplit, stopConn, gTaxiClasses);
563 : }
564 : }
565 : }
566 28251 : if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
567 178 : _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
568 178 : addEdge(access);
569 178 : stopConn->addSuccessor(access);
570 178 : access->addSuccessor(carSplit);
571 : }
572 :
573 : // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
574 28251 : _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
575 28251 : const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
576 28251 : _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
577 56502 : _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
578 28251 : depConn->addSuccessor(fwdSplit);
579 28251 : depConn->addSuccessor(backBeforeSplit);
580 : depConn->setLength(fwdSplit->getLength());
581 28251 : prevDep->removeSuccessor(backBeforeSplit);
582 28251 : prevDep->addSuccessor(backSplit);
583 : prevDep->setLength(backSplit->getLength());
584 28251 : if (carSplit != nullptr) {
585 28251 : depConn->addSuccessor(carSplit);
586 : }
587 :
588 : // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
589 28251 : _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
590 28251 : _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
591 56502 : _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
592 28251 : fwdSplit->addSuccessor(arrConn);
593 28251 : backBeforeSplit->addSuccessor(arrConn);
594 : arrConn->setLength(fwdSplit->getLength());
595 28251 : fwdSplit->removeSuccessor(prevArr);
596 28251 : fwdBeforeSplit->addSuccessor(prevArr);
597 : prevArr->setLength(backSplit->getLength());
598 28251 : if (carSplit != nullptr) {
599 28251 : if (carSplit->removeSuccessor(prevArr)) {
600 3254 : carSplit->addSuccessor(arrConn);
601 6508 : myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
602 : } else {
603 : // check for restricted access
604 53043 : for (_IntermodalEdge* out : carSplit->getSuccessors()) {
605 53043 : _AccessEdge* aOut = dynamic_cast<_AccessEdge*>(out);
606 53043 : if (aOut != nullptr && aOut->removeSuccessor(prevArr)) {
607 24997 : aOut->addSuccessor(arrConn);
608 24997 : addRestrictedCarExit(
609 24997 : myAccessSplits[myCarLookup[stopEdge]][splitIndex],
610 : prevArr,
611 : aOut->getVehicleRetriction());
612 : break;
613 : }
614 : }
615 : }
616 : }
617 28251 : 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 3375 : std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
623 : assert(splitList.size() > 0);
624 : typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
625 4096 : while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
626 : ++splitIt;
627 : }
628 3375 : splitList.insert(splitIt, stopConn);
629 :
630 3375 : if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
631 582 : _IntermodalEdge* carEdge = myCarLookup[stopEdge];
632 : double relPos;
633 : bool needSplit;
634 582 : const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
635 582 : if (needSplit) {
636 582 : _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
637 582 : splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
638 :
639 582 : if (transferCarWalk || transferTaxiWalk) {
640 : // adding access from car to walk
641 582 : _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
642 582 : if (transferCarWalk) {
643 417 : _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
644 417 : addEdge(access);
645 417 : beforeSplit->addSuccessor(access);
646 417 : access->addSuccessor(stopConn);
647 165 : } else if (transferTaxiWalk) {
648 165 : addRestrictedCarExit(beforeSplit, stopConn, gTaxiClasses);
649 : }
650 : }
651 582 : if (transferWalkTaxi) {
652 165 : _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
653 165 : addEdge(access);
654 165 : stopConn->addSuccessor(access);
655 165 : access->addSuccessor(carSplit);
656 : }
657 : }
658 : }
659 : }
660 31765 : }
661 :
662 : bool hasPTSchedules() const {
663 279 : return myHavePTSchedules;
664 : }
665 :
666 1721 : void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
667 : SUMOTime lastUntil = 0;
668 : StopParVector validStops;
669 1721 : if (addStops != nullptr) {
670 : // stops are part of a stand-alone route. until times are offsets from vehicle departure
671 5159 : for (const SUMOVehicleParameter::Stop& stop : *addStops) {
672 3540 : if (myStopConnections.count(stop.busstop) > 0) {
673 : // compute stop times for the first vehicle
674 3538 : const SUMOTime newUntil = stop.until + pars.depart;
675 3538 : if (newUntil >= lastUntil) {
676 3523 : validStops.push_back(stop);
677 3523 : validStops.back().until = newUntil;
678 : lastUntil = newUntil;
679 : } else {
680 60 : WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
681 : }
682 : }
683 : }
684 : }
685 3617 : for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
686 : // stops are part of the vehicle until times are absolute times for the first vehicle
687 1896 : if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
688 1738 : validStops.push_back(stop);
689 1738 : lastUntil = stop.until;
690 : } else {
691 158 : if (stop.busstop != "" && stop.until >= 0) {
692 0 : WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
693 : }
694 : }
695 : }
696 1721 : if (validStops.size() < 2 && pars.line != "taxi") {
697 204 : WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
698 68 : return;
699 : }
700 :
701 1653 : typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
702 1653 : if (lineEdges.empty()) {
703 : _IntermodalEdge* lastStop = nullptr;
704 : Position lastPos;
705 : SUMOTime lastTime = 0;
706 6624 : for (const SUMOVehicleParameter::Stop& s : validStops) {
707 5036 : _IntermodalEdge* currStop = myStopConnections[s.busstop];
708 5036 : Position stopPos = E::getStopPosition(s);
709 5036 : if (lastStop != nullptr) {
710 7054 : _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
711 3527 : addEdge(newEdge);
712 7054 : newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
713 3527 : myHavePTSchedules = true;
714 3527 : lastStop->addSuccessor(newEdge);
715 3527 : newEdge->addSuccessor(currStop);
716 3527 : lineEdges.push_back(newEdge);
717 : }
718 5036 : lastTime = s.until;
719 : lastStop = currStop;
720 5036 : lastPos = stopPos;
721 : }
722 1588 : if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
723 : myLoopedLines.insert(pars.line);
724 : }
725 : } else {
726 65 : if (validStops.size() != lineEdges.size() + 1) {
727 15 : WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
728 5 : return;
729 : }
730 60 : if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
731 39 : WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
732 13 : return;
733 : }
734 : typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
735 : typename StopParVector::const_iterator s = validStops.begin() + 1;
736 159 : for (; s != validStops.end(); ++s, ++lineEdge) {
737 112 : if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
738 0 : WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
739 0 : return;
740 : }
741 : }
742 47 : SUMOTime lastTime = validStops.front().until;
743 94 : if (lineEdges.front()->hasSchedule(lastTime)) {
744 24 : WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
745 : }
746 159 : for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
747 224 : (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
748 112 : myHavePTSchedules = true;
749 112 : lastTime = s->until;
750 : }
751 : }
752 1721 : }
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 60518 : 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 60518 : EdgePair pedestrianEdges = myBidiLookup[edge];
763 60518 : _IntermodalEdge* carEdge = myCarLookup[edge];
764 60518 : _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
765 60518 : addEdge(access);
766 60518 : pedestrianEdges.first->addSuccessor(access);
767 60518 : pedestrianEdges.second->addSuccessor(access);
768 60518 : access->addSuccessor(carEdge);
769 60518 : }
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 628711 : void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
777 628711 : _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
778 628711 : addEdge(access);
779 628711 : from->addSuccessor(access);
780 628711 : access->addSuccessor(to);
781 628711 : }
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 28972 : int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
800 28972 : relPos = pos;
801 28972 : needSplit = true;
802 : int splitIndex = 0;
803 : const auto& splitList = myAccessSplits.find(toSplit);
804 28972 : if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
805 31825 : for (const _IntermodalEdge* const split : splitList->second) {
806 31825 : if (relPos < split->getLength() + POSITION_EPS) {
807 : break;
808 : }
809 15741 : relPos -= split->getLength();
810 15741 : splitIndex++;
811 : }
812 : assert(splitIndex < (int)splitList->second.size());
813 16084 : if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
814 139 : needSplit = false;
815 : }
816 : }
817 28972 : 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 85752 : 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 85752 : std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
836 85752 : if (splitList.empty()) {
837 37722 : splitList.push_back(toSplit);
838 : }
839 85752 : if (!forward) {
840 28390 : splitIndex = (int)splitList.size() - 1 - splitIndex;
841 28390 : if (!needSplit) {
842 139 : splitIndex--;
843 : }
844 : }
845 85752 : _IntermodalEdge* beforeSplit = splitList[splitIndex];
846 85752 : if (needSplit) {
847 85335 : addEdge(afterSplit);
848 85335 : beforeSplit->transferSuccessors(afterSplit);
849 85335 : beforeSplit->addSuccessor(afterSplit);
850 85335 : if (forward) {
851 57084 : afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
852 : beforeSplit->setLength(relPos);
853 : } else {
854 : afterSplit->setLength(relPos);
855 28251 : beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
856 : // rename backward edges for easier referencing
857 : const std::string newID = beforeSplit->getID();
858 28251 : beforeSplit->setID(afterSplit->getID());
859 28251 : afterSplit->setID(newID);
860 : }
861 85335 : splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
862 : } else {
863 : // don't split, use the present split edges
864 417 : afterSplit = splitList[splitIndex + 1];
865 : }
866 : // add access to / from edge
867 85752 : if (addEntry) {
868 62451 : _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
869 62451 : addEdge(access);
870 62451 : beforeSplit->addSuccessor(access);
871 62451 : access->addSuccessor(stopConn);
872 : }
873 85752 : if (addExit) {
874 : // pedestrian case only, exit from public to pedestrian
875 56780 : _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
876 56780 : addEdge(exit);
877 56780 : stopConn->addSuccessor(exit);
878 56780 : exit->addSuccessor(afterSplit);
879 : }
880 85752 : }
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 : };
|