1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    IntermodalNetwork.h
11 /// @author  Jakob Erdmann
12 /// @author  Michael Behrisch
13 /// @author  Robert Hilbrich
14 /// @date    Mon, 03 March 2014
15 /// @version $Id$
16 ///
17 // The Edge definition for the Intermodal Router
18 /****************************************************************************/
19 #ifndef IntermodalNetwork_h
20 #define IntermodalNetwork_h
21 
22 
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27 
28 #include <string>
29 #include <vector>
30 #include <algorithm>
31 #include <assert.h>
32 #include <utils/common/MsgHandler.h>
33 #include <utils/common/Named.h>
34 #include <utils/common/SUMOTime.h>
35 #include <utils/common/ToString.h>
36 #include <utils/geom/Position.h>
37 #include <utils/vehicle/SUMOVehicleParameter.h>
38 #include "AccessEdge.h"
39 #include "CarEdge.h"
40 #include "IntermodalEdge.h"
41 #include "PedestrianEdge.h"
42 #include "PublicTransportEdge.h"
43 #include "StopEdge.h"
44 
45 //#define IntermodalRouter_DEBUG_NETWORK
46 
47 
48 // ===========================================================================
49 // function definitions
50 // ===========================================================================
51 template <class E, class L>
getSidewalk(const E * edge)52 inline const L* getSidewalk(const E* edge) {
53     if (edge == nullptr) {
54         return nullptr;
55     }
56     // prefer lanes that are exclusive to pedestrians
57     const std::vector<L*>& lanes = edge->getLanes();
58     for (const L* const lane : lanes) {
59         if (lane->getPermissions() == SVC_PEDESTRIAN) {
60             return lane;
61         }
62     }
63     for (const L* const lane : lanes) {
64         if (lane->allowsVehicleClass(SVC_PEDESTRIAN)) {
65             return lane;
66         }
67     }
68     return nullptr;
69 }
70 
71 
72 // ===========================================================================
73 // class definitions
74 // ===========================================================================
75 /// @brief the intermodal network storing edges, connections and the mappings to the "real" edges
76 template<class E, class L, class N, class V>
77 class IntermodalNetwork {
78 private:
79     typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
80     typedef AccessEdge<E, L, N, V> _AccessEdge;
81     typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
82     typedef PublicTransportEdge<E, L, N, V> _PTEdge;
83     typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
84 
85 public:
86     /** @brief where mode changes are possible
87     */
88     enum ModeChangeOptions {
89         /// @brief parking areas
90         PARKING_AREAS = 1,
91         /// @brief public transport stops and access
92         PT_STOPS = 2,
93         /// @brief junctions with edges allowing the additional mode
94         ALL_JUNCTIONS = 4
95     };
96 
97     /* @brief build the pedestrian part of the intermodal network (once)
98      * @param edges The list of MSEdge or ROEdge to build from
99      * @param numericalID the start number for the creation of new edges
100      */
101     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
102         : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
103 #ifdef IntermodalRouter_DEBUG_NETWORK
104         std::cout << "initIntermodalNetwork\n";
105 #endif
106         // build the pedestrian edges and the depart / arrival connectors with lookup tables
107         bool haveSeenWalkingArea = false;
108         for (const E* const edge : edges) {
109             if (edge->isTazConnector()) {
110                 continue;
111             }
112             const L* lane = getSidewalk<E, L>(edge);
113             if (lane != 0) {
114                 if (edge->isWalkingArea()) {
115                     // only a single edge
116                     addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
117                     myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
118                     myDepartLookup[edge].push_back(myEdges.back());
119                     myArrivalLookup[edge].push_back(myEdges.back());
120                     haveSeenWalkingArea = true;
121                 } else { // regular edge or crossing
122                     // forward and backward edges
123                     addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
124                     addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
125                     myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
126                 }
127             }
128             if (!edge->isWalkingArea()) {
129                 // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
130                 _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
131                 _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
132                 addConnectors(departConn, arrivalConn, 0);
133             }
134         }
135 
136         // build the walking connectors if there are no walking areas
137         for (const E* const edge : edges) {
138             if (edge->isTazConnector() || edge->isInternal()) {
139                 continue;
140             }
141             if (haveSeenWalkingArea) {
142                 // 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
143                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
144                     const N* const node = edge->getToJunction();
145                     if (myWalkingConnectorLookup.count(node) == 0) {
146                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
147                         myWalkingConnectorLookup[node] = myEdges.back();
148                     }
149                 }
150             } else {
151                 for (const N* const node : {
152                             edge->getFromJunction(), edge->getToJunction()
153                         }) {
154                     if (myWalkingConnectorLookup.count(node) == 0) {
155                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
156                         myWalkingConnectorLookup[node] = myEdges.back();
157                     }
158                 }
159             }
160         }
161         // build the connections
162         for (const E* const edge : edges) {
163             const L* const sidewalk = getSidewalk<E, L>(edge);
164             if (sidewalk == nullptr) {
165                 continue;
166             }
167             // find all incoming and outgoing lanes for the sidewalk and
168             // connect the corresponding IntermodalEdges
169             const EdgePair& pair = getBothDirections(edge);
170 #ifdef IntermodalRouter_DEBUG_NETWORK
171             std::cout << "  building connections from " << sidewalk->getID() << "\n";
172 #endif
173             if (haveSeenWalkingArea) {
174                 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
175                 // if one of the outgoing lanes is a walking area it must be used.
176                 // All other connections shall be ignored
177                 // if it has no outgoing walking area, it probably is a walking area itself
178                 bool hasWalkingArea = false;
179                 for (const auto& target : outgoing) {
180                     if (target.first->getEdge().isWalkingArea()) {
181                         hasWalkingArea = true;
182                         break;
183                     }
184                 }
185                 for (const auto& target : outgoing) {
186                     const E* const targetEdge = &(target.first->getEdge());
187                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
188                                        && (!hasWalkingArea || targetEdge->isWalkingArea()));
189 #ifdef IntermodalRouter_DEBUG_NETWORK
190                     const L* potTarget = getSidewalk<E, L>(targetEdge);
191                     std::cout << "   lane=" << (potTarget == 0 ? "NULL" : potTarget->getID()) << (used ? "(used)" : "") << "\n";
192 #endif
193                     if (used) {
194                         const EdgePair& targetPair = getBothDirections(targetEdge);
195                         pair.first->addSuccessor(targetPair.first);
196                         targetPair.second->addSuccessor(pair.second);
197 #ifdef IntermodalRouter_DEBUG_NETWORK
198                         std::cout << "     " << pair.first->getID() << " -> " << targetPair.first->getID() << "\n";
199                         std::cout << "     " << targetPair.second->getID() << " -> " << pair.second->getID() << "\n";
200 #endif
201                     }
202                 }
203             }
204             // We may have a network without pedestrian structures or a car-only edge.
205             // In the first case we assume that all sidewalks at a junction are interconnected,
206             // in the second we connect all car-only edges to all sidewalks.
207             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
208             if (toNodeConn != nullptr) {
209                 // Check for the outgoing vias and use the shortest one as an approximation
210                 const std::vector<std::pair<const L*, const E*> > outgoing = sidewalk->getOutgoingViaLanes();
211                 double minViaLength = std::numeric_limits<double>::max();
212                 const E* minVia = nullptr;
213                 for (const auto& target : outgoing) {
214                     if (target.second != nullptr && target.second->getLength() < minViaLength) {
215                         minViaLength = target.second->getLength();
216                         minVia = target.second;
217                     }
218                 }
219                 EdgePair interVia = std::make_pair(nullptr, nullptr);
220                 if (minVia != nullptr) {
221                     const auto it = myBidiLookup.find(minVia);
222                     if (it != myBidiLookup.end()) {
223                         interVia = it->second;
224                     }
225                 }
226                 if (!haveSeenWalkingArea) {
227                     // if we have walking areas we should use them and not the connector
228                     pair.first->addSuccessor(toNodeConn, interVia.first);
229                 }
230                 toNodeConn->addSuccessor(pair.second, interVia.second);
231             }
232             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
233             if (fromNodeConn != nullptr) {
234                 if (!haveSeenWalkingArea) {
235                     pair.second->addSuccessor(fromNodeConn);
236                 }
237                 fromNodeConn->addSuccessor(pair.first);
238             }
239             if (!edge->isWalkingArea()) {
240                 // build connections from depart connector
241                 _IntermodalEdge* startConnector = getDepartConnector(edge);
242                 startConnector->addSuccessor(pair.first);
243                 startConnector->addSuccessor(pair.second);
244                 // build connections to arrival connector
245                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
246                 pair.first->addSuccessor(endConnector);
247                 pair.second->addSuccessor(endConnector);
248             }
249 #ifdef IntermodalRouter_DEBUG_NETWORK
250             std::cout << "     " << startConnector->getID() << " -> " << pair.first->getID() << "\n";
251             std::cout << "     " << startConnector->getID() << " -> " << pair.second->getID() << "\n";
252             std::cout << "     " << pair.first->getID() << " -> " << endConnector->getID() << "\n";
253             std::cout << "     " << pair.second->getID() << " -> " << endConnector->getID() << "\n";
254 #endif
255         }
256     }
257 
~IntermodalNetwork()258     ~IntermodalNetwork() {
259         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
260             delete *it;
261         }
262     }
263 
addEdge(_IntermodalEdge * edge)264     void addEdge(_IntermodalEdge* edge) {
265         while ((int)myEdges.size() <= edge->getNumericalID()) {
266             myEdges.push_back(0);
267         }
268         myEdges[edge->getNumericalID()] = edge;
269     }
270 
addConnectors(_IntermodalEdge * const depConn,_IntermodalEdge * const arrConn,const int index)271     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
272         addEdge(depConn);
273         addEdge(arrConn);
274         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
275         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
276     }
277 
getAllEdges()278     const std::vector<_IntermodalEdge*>& getAllEdges() {
279         return myEdges;
280     }
281 
282     /// @brief Returns the pair of forward and backward edge
getBothDirections(const E * e)283     const EdgePair& getBothDirections(const E* e) const {
284         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
285         if (it == myBidiLookup.end()) {
286             assert(false);
287             throw ProcessError("Edge '" + e->getID() + "' not found in intermodal network.'");
288         }
289         return (*it).second;
290     }
291 
292     /// @brief Returns the departing intermodal edge
getDepartEdge(const E * e,const double pos)293     const _IntermodalEdge* getDepartEdge(const E* e, const double pos) const {
294         typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
295         if (it == myDepartLookup.end()) {
296             throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
297         }
298         double totalLength = 0.;
299         double bestDist = std::numeric_limits<double>::max();
300         const _IntermodalEdge* best = nullptr;
301         for (const _IntermodalEdge* split : it->second) {
302             totalLength += split->getLength();
303             double dist = fabs(totalLength - pos);
304             if (dist < bestDist) {
305                 bestDist = dist;
306                 best = split;
307             } else {
308                 break;
309             }
310         }
311         assert(best != 0);
312         return best;
313     }
314 
315     /// @brief Returns the departing intermodal connector at the given split offset
316     _IntermodalEdge* getDepartConnector(const E* e, const int splitIndex = 0) const {
317         typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myDepartLookup.find(e);
318         if (it == myDepartLookup.end()) {
319             throw ProcessError("Depart edge '" + e->getID() + "' not found in intermodal network.");
320         }
321         if (splitIndex >= (int)it->second.size()) {
322             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
323         }
324         return it->second[splitIndex];
325     }
326 
327     /// @brief Returns the arriving intermodal edge
getArrivalEdge(const E * e,const double pos)328     _IntermodalEdge* getArrivalEdge(const E* e, const double pos) const {
329         typename std::map<const E*, std::vector<_IntermodalEdge*> >::const_iterator it = myArrivalLookup.find(e);
330         if (it == myArrivalLookup.end()) {
331             throw ProcessError("Arrival edge '" + e->getID() + "' not found in intermodal network.");
332         }
333         const std::vector<_IntermodalEdge*>& splitList = it->second;
334         typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
335         double totalLength = 0.;
336         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
337             totalLength += (*splitIt)->getLength();
338             ++splitIt;
339         }
340         return *splitIt;
341     }
342 
343     /// @brief Returns the arriving intermodal connector at the given split offset
344     _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
345         return myArrivalLookup.find(e)->second[splitIndex];
346     }
347 
348     /// @brief Returns the outgoing pedestrian edge, which is either a walking area or a walking connector
getWalkingConnector(const E * e)349     _IntermodalEdge* getWalkingConnector(const E* e) const {
350         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
351         if (it == myWalkingConnectorLookup.end()) {
352             const L* const sidewalk = getSidewalk<E, L>(e);
353             if (e->isInternal() || sidewalk == 0) {
354                 return 0;
355             }
356             for (const auto& target : sidewalk->getOutgoingViaLanes()) {
357                 if (target.first->getEdge().isWalkingArea()) {
358                     return getBothDirections(&target.first->getEdge()).first;
359                 }
360             }
361             return 0;
362         }
363         return it->second;
364     }
365 
addCarEdges(const std::vector<E * > & edges)366     void addCarEdges(const std::vector<E*>& edges) {
367         for (const E* const edge : edges) {
368             if (edge->getFunction() == EDGEFUNC_NORMAL || edge->getFunction() == EDGEFUNC_INTERNAL) {
369                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
370                 addEdge(myCarLookup[edge]);
371             }
372         }
373         for (const auto& edgePair : myCarLookup) {
374             _IntermodalEdge* const carEdge = edgePair.second;
375             for (const auto& suc : edgePair.first->getViaSuccessors()) {
376                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
377                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
378                 if (sucCarEdge != nullptr) {
379                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
380                 }
381             }
382             if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
383                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
384                 if (walkCon != 0) {
385                     carEdge->addSuccessor(walkCon);
386                 } else {
387                     // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
388                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
389                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
390                             carEdge->addSuccessor(getBothDirections(out).first);
391                         }
392                     }
393                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
394                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
395                             carEdge->addSuccessor(getBothDirections(in).second);
396                         }
397                     }
398                 }
399             }
400             getDepartConnector(edgePair.first)->addSuccessor(carEdge);
401             carEdge->addSuccessor(getArrivalConnector(edgePair.first));
402         }
403     }
404 
405     /// @brief Returns the associated car edge
getCarEdge(const E * e)406     _IntermodalEdge* getCarEdge(const E* e) const {
407         typename std::map<const E*, _IntermodalEdge*>::const_iterator it = myCarLookup.find(e);
408         if (it == myCarLookup.end()) {
409             return nullptr;
410         }
411         return it->second;
412     }
413 
414     /// @brief Returns the associated stop edge
getStopEdge(const std::string & stopId)415     _IntermodalEdge* getStopEdge(const std::string& stopId) const {
416         auto it = myStopConnections.find(stopId);
417         if (it == myStopConnections.end()) {
418             return nullptr;
419         }
420         return it->second;
421     }
422 
423     /** @brief Adds access edges for stopping places to the intermodal network
424     *
425     * This method creates an intermodal stop edge to represent the stopping place
426     *  (if not present yet) and determines the edges which need to be splitted (usually the forward
427     *  and the backward pedestrian edges and the car edge) and calls splitEdge for the
428     *  actual split and the connection of the stop edge with access edges. After that it adds and adapts
429     *  the depart and arrival connectors to the new edge(s).
430     *
431     * @param[in] stopId The id of the stop to add
432     * @param[in] stopEdge The edge on which the stop is located
433     * @param[in] pos The relative position on the edge where the stop is located
434     * @param[in] category The type of stop
435     */
addAccess(const std::string & stopId,const E * stopEdge,const double pos,const double length,const SumoXMLTag category)436     void addAccess(const std::string& stopId, const E* stopEdge, const double pos, const double length, const SumoXMLTag category) {
437         assert(stopEdge != 0);
438         //std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " cat=" << category << "\n";
439         if (myStopConnections.count(stopId) == 0) {
440             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge);
441             addEdge(myStopConnections[stopId]);
442         }
443         _IntermodalEdge* const stopConn = myStopConnections[stopId];
444         const L* lane = getSidewalk<E, L>(stopEdge);
445         if (lane != 0) {
446             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
447             double relPos;
448             bool needSplit;
449             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
450             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
451             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
452             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
453             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
454             _IntermodalEdge* carSplit = nullptr;
455             if (myCarLookup.count(stopEdge) > 0) {
456                 if (needSplit) {
457                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
458                 }
459                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false);
460             }
461             if (needSplit) {
462                 if (carSplit != nullptr && ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) || (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0))) {
463                     // adding access from car to walk
464                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
465                     for (_IntermodalEdge* conn : {
466                                 fwdSplit, backSplit
467                             }) {
468                         _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
469                         addEdge(access);
470                         beforeSplit->addSuccessor(access);
471                         access->addSuccessor(conn);
472                     }
473                 }
474 
475                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
476                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
477                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
478                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
479                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
480                 depConn->addSuccessor(fwdSplit);
481                 depConn->addSuccessor(backBeforeSplit);
482                 depConn->setLength(fwdSplit->getLength());
483                 prevDep->removeSuccessor(backBeforeSplit);
484                 prevDep->addSuccessor(backSplit);
485                 prevDep->setLength(backSplit->getLength());
486                 if (carSplit != nullptr) {
487                     depConn->addSuccessor(carSplit);
488                 }
489 
490                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
491                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
492                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
493                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
494                 fwdSplit->addSuccessor(arrConn);
495                 backBeforeSplit->addSuccessor(arrConn);
496                 arrConn->setLength(fwdSplit->getLength());
497                 fwdSplit->removeSuccessor(prevArr);
498                 fwdBeforeSplit->addSuccessor(prevArr);
499                 prevArr->setLength(backSplit->getLength());
500                 if (carSplit != nullptr) {
501                     carSplit->addSuccessor(arrConn);
502                     carSplit->removeSuccessor(prevArr);
503                     myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
504                 }
505                 addConnectors(depConn, arrConn, splitIndex + 1);
506             }
507         } else {
508             // pedestrians cannot walk here:
509             // add depart connectors on the stop edge so that pedestrians may start at the stop
510             auto& splitList = myDepartLookup[stopEdge];
511             assert(splitList.size() > 0);
512             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
513             double totalLength = 0.;
514             _IntermodalEdge* last = nullptr;
515             while (splitIt != splitList.end() && totalLength < pos) {
516                 totalLength += (*splitIt)->getLength();
517                 last = *splitIt;
518                 ++splitIt;
519             }
520             auto* stopConnector = myStopConnections[stopId];
521             // insert before last
522             const double newLength = pos - (totalLength - last->getLength());
523             stopConnector->setLength(newLength);
524             splitList.insert(splitIt - 1, stopConnector);
525             // correct length of subsequent edge
526             last->setLength(last->getLength() - newLength);
527         }
528     }
529 
530     void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
531         SUMOTime lastUntil = 0;
532         std::vector<SUMOVehicleParameter::Stop> validStops;
533         if (addStops != nullptr) {
534             // stops are part of a stand-alone route. until times are offsets from vehicle departure
535             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
536                 if (myStopConnections.count(stop.busstop) > 0) {
537                     // compute stop times for the first vehicle
538                     const SUMOTime newUntil = stop.until + pars.depart;
539                     if (newUntil >= lastUntil) {
540                         validStops.push_back(stop);
541                         validStops.back().until = newUntil;
542                         lastUntil = newUntil;
543                     } else {
544                         WRITE_WARNING("Ignoring unordered stop at '" + stop.busstop + "' until " + time2string(stop.until) + "  for vehicle '" + pars.id + "'.");
545                     }
546                 }
547             }
548         }
549         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
550             // stops are part of the vehicle until times are absolute times for the first vehicle
551             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
552                 validStops.push_back(stop);
553                 lastUntil = stop.until;
554             } else {
555                 WRITE_WARNING("Ignoring stop at '" + stop.busstop + "' until " + time2string(stop.until) + "  for vehicle '" + pars.id + "'.");
556             }
557         }
558         if (validStops.size() < 2) {
559             WRITE_WARNING("Not using public transport line '" + pars.line + "' for routing persons. It has less than two usable stops.");
560             return;
561         }
562 
563         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
564         if (lineEdges.empty()) {
565             _IntermodalEdge* lastStop = nullptr;
566             Position lastPos;
567             SUMOTime lastTime = 0;
568             for (const SUMOVehicleParameter::Stop& s : validStops) {
569                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
570                 Position stopPos = E::getStopPosition(s);
571                 if (lastStop != nullptr) {
572                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
573                     addEdge(newEdge);
574                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
575                     lastStop->addSuccessor(newEdge);
576                     newEdge->addSuccessor(currStop);
577                     lineEdges.push_back(newEdge);
578                 }
579                 lastTime = s.until;
580                 lastStop = currStop;
581                 lastPos = stopPos;
582             }
583         } else {
584             if (validStops.size() != lineEdges.size() + 1) {
585                 WRITE_WARNING("Number of stops for public transport line '" + pars.line + "' does not match earlier definitions, ignoring schedule.");
586                 return;
587             }
588             if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
589                 WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
590                 return;
591             }
592             typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
593             typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
594             for (; s != validStops.end(); ++s, ++lineEdge) {
595                 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
596                     WRITE_WARNING("Different stop for '" + pars.line + "' compared to earlier definitions, ignoring schedule.");
597                     return;
598                 }
599             }
600             SUMOTime lastTime = validStops.front().until;
601             if (lineEdges.front()->hasSchedule(lastTime)) {
602                 WRITE_WARNING("Duplicate schedule for '" + pars.line + "' at time " + time2string(lastTime) + ".");
603             }
604             for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
605                 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
606                 lastTime = s->until;
607             }
608         }
609     }
610 
611 
612 private:
613     /** @brief Returns where to insert or use the split edge
614     *
615     * This method determines whether an edge needs to be split at the given position
616     *  (if there is not already a split nearby) and returns the corresponding index in the split list.
617     *
618     * @param[in] toSplit The first edge in the split list
619     * @param[in] pos The relative position on the edge where the stop is located
620     * @param[out] relPos The relative position on the splitted edge
621     * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
622     * @return the index in the split list where the split edge needs to be added or reused
623     */
findSplitIndex(_IntermodalEdge * const toSplit,const double pos,double & relPos,bool & needSplit)624     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) {
625         relPos = pos;
626         needSplit = true;
627         int splitIndex = 0;
628         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
629         if (!splitList.empty()) {
630             for (const _IntermodalEdge* const split : splitList) {
631                 if (relPos < split->getLength() + POSITION_EPS) {
632                     break;
633                 }
634                 relPos -= split->getLength();
635                 splitIndex++;
636             }
637             assert(splitIndex < (int)splitList.size());
638             if (splitIndex + 1 < (int)splitList.size() && fabs(relPos - splitList[splitIndex]->getLength()) < POSITION_EPS) {
639                 needSplit = false;
640             }
641         }
642         return splitIndex;
643     }
644 
645     /** @brief Splits an edge (if necessary) and connects it to a stopping edge
646     *
647     * This method determines whether an edge needs to be split at the given position
648     *  (if there is not already a split nearby) and connects the stop edge via new access edges.
649     *
650     * @param[in] toSplit The first edge in the split list
651     * @param[in] afterSplit The edge to add if a split is performed
652     * @param[in] pos The relative position on the edge where the stop is located
653     * @param[in] stopConn The stop edge to connect to
654     * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
655     * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
656     */
657     void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
658                    _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
659                    _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true) {
660         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
661         if (splitList.empty()) {
662             splitList.push_back(toSplit);
663         }
664         if (!forward) {
665             splitIndex = (int)splitList.size() - 1 - splitIndex;
666             if (!needSplit) {
667                 splitIndex--;
668             }
669         }
670         _IntermodalEdge* beforeSplit = splitList[splitIndex];
671         if (needSplit) {
672             addEdge(afterSplit);
673             beforeSplit->transferSuccessors(afterSplit);
674             beforeSplit->addSuccessor(afterSplit);
675             if (forward) {
676                 afterSplit->setLength(beforeSplit->getLength() - relPos);
677                 beforeSplit->setLength(relPos);
678             } else {
679                 afterSplit->setLength(relPos);
680                 beforeSplit->setLength(beforeSplit->getLength() - relPos);
681                 // rename backward edges for easier referencing
682                 const std::string newID = beforeSplit->getID();
683                 beforeSplit->setID(afterSplit->getID());
684                 afterSplit->setID(newID);
685             }
686             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
687         } else {
688             // don't split, use the present split edges
689             afterSplit = splitList[splitIndex + 1];
690         }
691         // add access to / from edge
692         _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
693         addEdge(access);
694         beforeSplit->addSuccessor(access);
695         access->addSuccessor(stopConn);
696         if (addExit) {
697             // pedestrian case only, exit from public to pedestrian
698             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
699             addEdge(exit);
700             stopConn->addSuccessor(exit);
701             exit->addSuccessor(afterSplit);
702         }
703     }
704 
705 
706 private:
707     /// @brief the edge dictionary
708     std::vector<_IntermodalEdge*> myEdges;
709 
710     /// @brief retrieve the forward and backward edge for the given input edge E
711     std::map<const E*, EdgePair> myBidiLookup;
712 
713     /// @brief retrieve the depart edges for the given input edge E
714     std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
715 
716     /// @brief retrieve the arrival edges for the given input edge E
717     std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
718 
719     /// @brief the walking connector edge (fake walking area)
720     std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
721 
722     /// @brief retrieve the car edge for the given input edge E
723     std::map<const E*, _IntermodalEdge*> myCarLookup;
724 
725     /// @brief retrieve the public transport edges for the given line
726     std::map<std::string, std::vector<_PTEdge*> > myPTLines;
727 
728     /// @brief retrieve the representing edge for the given stopping place
729     std::map<std::string, _IntermodalEdge*> myStopConnections;
730 
731     /// @brief retrieve the splitted edges for the given "original"
732     std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
733 
734     int myNumericalID;
735     const int myCarWalkTransfer;
736 
737 private:
738     /// @brief Invalidated assignment operator
739     IntermodalNetwork& operator=(const IntermodalNetwork& s);
740 
741 };
742 
743 
744 #endif
745 
746 /****************************************************************************/
747