LCOV - code coverage report
Current view: top level - src/utils/router - IntermodalNetwork.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.5 % 382 365
Test Date: 2025-07-11 16:46:44 Functions: 100.0 % 32 32

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 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,
      54              :     /// @brief public transport stops and access
      55              :     PT_STOPS = 2,
      56              :     /// @brief junctions with edges allowing the additional mode
      57              :     ALL_JUNCTIONS = 2 << 2,
      58              :     /// @brief taxi customer may exit at parking areas
      59              :     TAXI_DROPOFF_PARKING_AREAS = 2 << 3,
      60              :     /// @brief taxi customer may exit at public transport stops
      61              :     TAXI_DROPOFF_PT = 2 << 4,
      62              :     /// @brief taxi customer may exit anywhere
      63              :     TAXI_DROPOFF_ANYWHERE = 2 << 5,
      64              :     /// @brief taxi customer may be picked up at parking areas
      65              :     TAXI_PICKUP_PARKING_AREAS = 2 << 6,
      66              :     /// @brief taxi customer may be picked up at public transport stops
      67              :     TAXI_PICKUP_PT = 2 << 7,
      68              :     /// @brief taxi customer may be picked up anywhere
      69              :     TAXI_PICKUP_ANYWHERE = 2 << 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        13840 :     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
      90        13840 :         : myNumericalID(0), myCarWalkTransfer(carWalkTransfer) {
      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       959526 :         for (const E* const edge : edges) {
      97       945686 :             if (edge->isTazConnector()) {
      98              :                 // only a single edge
      99       108178 :                 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
     100       108178 :                 addEdge(access);
     101       108178 :                 myDepartLookup[edge].push_back(access);
     102       108178 :                 myArrivalLookup[edge].push_back(access);
     103              :             } else {
     104       404694 :                 const L* lane = getSidewalk<E, L>(edge);
     105       837508 :                 if (lane != 0) {
     106       623098 :                     if (edge->isWalkingArea()) {
     107              :                         // only a single edge
     108        88513 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     109        88513 :                         myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
     110        88513 :                         myDepartLookup[edge].push_back(myEdges.back());
     111        88513 :                         myArrivalLookup[edge].push_back(myEdges.back());
     112              :                         haveSeenWalkingArea = true;
     113              :                     } else { // regular edge or crossing
     114              :                         // forward and backward edges
     115       534585 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     116       534585 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
     117       534585 :                         myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
     118              :                     }
     119              :                 }
     120       837508 :                 if (!edge->isWalkingArea()) {
     121              :                     // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
     122      1497990 :                     _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
     123      1497990 :                     _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
     124       748995 :                     addConnectors(departConn, arrivalConn, 0);
     125              :                 }
     126              :             }
     127              :         }
     128              : 
     129              :         // build the walking connectors if there are no walking areas
     130       959526 :         for (const E* const edge : edges) {
     131       945686 :             if (edge->isTazConnector() || edge->isInternal()) {
     132       579394 :                 continue;
     133              :             }
     134       366292 :             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       247417 :                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
     137         6568 :                     const N* const node = edge->getToJunction();
     138              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     139        10896 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     140         5448 :                         myWalkingConnectorLookup[node] = myEdges.back();
     141              :                     }
     142              :                 }
     143              :             } else {
     144       388734 :                 for (const N* const node : {
     145              :                             edge->getFromJunction(), edge->getToJunction()
     146              :                         }) {
     147              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     148       167794 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     149        83897 :                         myWalkingConnectorLookup[node] = myEdges.back();
     150              :                     }
     151              :                 }
     152              :             }
     153              :         }
     154              :         // build the connections
     155       959526 :         for (const E* const edge : edges) {
     156       945686 :             if (edge->isTazConnector()) {
     157              :                 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
     158       108178 :                 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
     159              :                 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
     160              :                 const E* other = edge->getOtherTazConnector();
     161       108178 :                 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
     162              :                 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
     163       230030 :                 for (const E* out : edge->getSuccessors()) {
     164       121852 :                     if (out->isNormal()) {
     165       160454 :                         tazDepart->addSuccessor(getDepartConnector(out));
     166        80227 :                         getArrivalConnector(out)->addSuccessor(otherTazArrive);
     167              :                     }
     168              :                 }
     169       230049 :                 for (const E* in : edge->getPredecessors()) {
     170       121871 :                     if (in->isNormal()) {
     171        80246 :                         getArrivalConnector(in)->addSuccessor(tazArrive);
     172       160492 :                         otherTazDepart->addSuccessor(getDepartConnector(in));
     173              :                     }
     174              :                 }
     175       108178 :                 continue;
     176       108178 :             }
     177       404694 :             const L* const sidewalk = getSidewalk<E, L>(edge);
     178       837508 :             if (sidewalk == nullptr) {
     179       214410 :                 continue;
     180              :             }
     181              :             // find all incoming and outgoing lanes for the sidewalk and
     182              :             // connect the corresponding IntermodalEdges
     183       623098 :             const EdgePair& pair = getBothDirections(edge);
     184              : #ifdef IntermodalRouter_DEBUG_NETWORK
     185              :             std::cout << "  building connections from " << sidewalk->getID() << "\n";
     186              : #endif
     187       623098 :             if (haveSeenWalkingArea) {
     188       251146 :                 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       436998 :                 for (const auto& target : outgoing) {
     194       306305 :                     if (target.first->getEdge().isWalkingArea()) {
     195              :                         hasWalkingArea = true;
     196              :                         break;
     197              :                     }
     198              :                 }
     199       557451 :                 for (const auto& target : outgoing) {
     200       306305 :                     const E* const targetEdge = &(target.first->getEdge());
     201       154345 :                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
     202       306305 :                                        && (!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       269710 :                         const EdgePair& targetPair = getBothDirections(targetEdge);
     209       269710 :                         pair.first->addSuccessor(targetPair.first);
     210       539420 :                         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       251146 :             }
     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       623098 :             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
     222       623098 :             if (toNodeConn != nullptr) {
     223              :                 // Check for the outgoing vias and use the shortest one as an approximation
     224       373998 :                 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       843080 :                 for (const auto& target : outgoing) {
     228       469082 :                     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       373998 :                 if (minVia != nullptr) {
     235              :                     const auto it = myBidiLookup.find(minVia);
     236       120981 :                     if (it != myBidiLookup.end()) {
     237              :                         interVia = it->second;
     238              :                     }
     239              :                 }
     240       373998 :                 if (!haveSeenWalkingArea) {
     241              :                     // if we have walking areas we should use them and not the connector
     242       743904 :                     pair.first->addSuccessor(toNodeConn, interVia.first);
     243              :                 }
     244       373998 :                 toNodeConn->addSuccessor(pair.second, interVia.second);
     245       373998 :             }
     246       623098 :             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
     247       623098 :             if (fromNodeConn != nullptr) {
     248       374035 :                 if (!haveSeenWalkingArea) {
     249       743904 :                     pair.second->addSuccessor(fromNodeConn);
     250              :                 }
     251       748070 :                 fromNodeConn->addSuccessor(pair.first);
     252              :             }
     253       623098 :             if (!edge->isWalkingArea()) {
     254              :                 // build connections from depart connector
     255       534585 :                 _IntermodalEdge* startConnector = getDepartConnector(edge);
     256       534585 :                 startConnector->addSuccessor(pair.first);
     257      1069170 :                 startConnector->addSuccessor(pair.second);
     258              :                 // build connections to arrival connector
     259              :                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
     260      1069170 :                 pair.first->addSuccessor(endConnector);
     261      1069170 :                 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        13840 :     }
     271              : 
     272        13840 :     ~IntermodalNetwork() {
     273      3624708 :         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
     274      3610868 :             delete *it;
     275              :         }
     276        13840 :     }
     277              : 
     278      3610868 :     void addEdge(_IntermodalEdge* edge) {
     279      7221736 :         while ((int)myEdges.size() <= edge->getNumericalID()) {
     280      3610868 :             myEdges.push_back(0);
     281              :         }
     282      3610868 :         myEdges[edge->getNumericalID()] = edge;
     283      3610868 :     }
     284              : 
     285       772614 :     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
     286       772614 :         addEdge(depConn);
     287       772614 :         addEdge(arrConn);
     288       772614 :         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
     289       772614 :         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
     290       772614 :     }
     291              : 
     292              :     const std::vector<_IntermodalEdge*>& getAllEdges() {
     293        16399 :         return myEdges;
     294              :     }
     295              : 
     296              :     /// @brief Returns the pair of forward and backward edge
     297      2611156 :     const EdgePair& getBothDirections(const E* e) const {
     298              :         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
     299      2611156 :         if (it == myBidiLookup.end()) {
     300              :             assert(false);
     301            0 :             throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
     302              :         }
     303      2611156 :         return (*it).second;
     304              :     }
     305              : 
     306              :     /// @brief Returns the departing intermodal edge
     307       173868 :     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       173868 :         if (it == myDepartLookup.end()) {
     310            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     311              :         }
     312       173868 :         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           42 :             for (const _IntermodalEdge* const split : it->second) {
     317           27 :                 if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
     318           19 :                     const double dist = split->getEndPos() - split->getStartPos();
     319           19 :                     if (dist < bestDist) {
     320              :                         bestDist = dist;
     321              :                         best = split;
     322              :                     }
     323              :                 }
     324              :             }
     325              :             assert(best != nullptr);
     326           15 :             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       175154 :             while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     333              :                 totalLength += (*splitIt)->getLength();
     334              :                 ++splitIt;
     335              :             }
     336       173853 :             return *splitIt;
     337              :         }
     338              :     }
     339              : 
     340              :     /// @brief Returns the departing intermodal connector at the given split offset
     341      1859020 :     _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      1859020 :         if (it == myDepartLookup.end()) {
     344            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     345              :         }
     346      1859020 :         if (splitIndex >= (int)it->second.size()) {
     347            0 :             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
     348              :         }
     349      1859020 :         return it->second[splitIndex];
     350              :     }
     351              : 
     352              :     /// @brief Returns the arriving intermodal edge
     353       162974 :     _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       162974 :         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       463749 :         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     362              :             totalLength += (*splitIt)->getLength();
     363              :             ++splitIt;
     364              :         }
     365       162974 :         if (splitIt != splitList.end()) {
     366       162974 :             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      1833942 :         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         6310 :     _IntermodalEdge* getWalkingConnector(const E* e) const {
     379              :         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
     380         6310 :         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         6310 :         return it->second;
     393              :     }
     394              : 
     395         4921 :     void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
     396       367472 :         for (const E* const edge : edges) {
     397       362551 :             if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
     398       273805 :                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
     399       273805 :                 addEdge(myCarLookup[edge]);
     400              :             }
     401              :         }
     402       278726 :         for (const auto& edgePair : myCarLookup) {
     403       273805 :             _IntermodalEdge* const carEdge = edgePair.second;
     404              :             // connectivity within the car network
     405       683859 :             for (const auto& suc : edgePair.first->getViaSuccessors()) {
     406       820108 :                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
     407       820108 :                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
     408       410054 :                 if (sucCarEdge != nullptr) {
     409       339834 :                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
     410              :                 }
     411              :             }
     412              :             // connectivity to the pedestrian network (only for normal edges)
     413       273805 :             if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
     414       184503 :                 continue;
     415              :             }
     416        89302 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
     417          295 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     418          295 :                 if (walkCon != 0) {
     419          253 :                     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          162 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     423          120 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     424          156 :                             carEdge->addSuccessor(getBothDirections(out).first);
     425              :                         }
     426              :                     }
     427          174 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     428          132 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     429          180 :                             carEdge->addSuccessor(getBothDirections(in).second);
     430              :                         }
     431              :                     }
     432              :                 }
     433              :             }
     434        89302 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     435              :                 // add access edges that allow exiting a taxi
     436         6015 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     437         6015 :                 if (walkCon != 0) {
     438         3414 :                     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        49291 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     442        47302 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     443        16134 :                             addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
     444              :                         }
     445              :                     }
     446        49192 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     447        47167 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     448        16035 :                             addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
     449              :                         }
     450              :                     }
     451              :                 }
     452              :             }
     453              :             // use intermediate access edge that prevents taxi departure
     454        89302 :             _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
     455        89302 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
     456        89302 :             addEdge(access);
     457        89302 :             departConn->addSuccessor(access);
     458        89302 :             access->addSuccessor(carEdge);
     459        89302 :             if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
     460              :                 // taxi may depart anywhere but there is a time penalty
     461        87141 :                 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     462        87141 :                 addEdge(taxiAccess);
     463        87141 :                 departConn->addSuccessor(taxiAccess);
     464        87141 :                 taxiAccess->addSuccessor(carEdge);
     465              :             }
     466        89302 :             if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
     467              :                 // taxi (as all other cars) may arrive anywhere
     468       175686 :                 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
     469              :             } else {
     470              :                 // use intermediate access edge that prevents taxi arrival
     471         2918 :                 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
     472              :             }
     473              :         }
     474         4921 :     }
     475              : 
     476              :     /// @brief Returns the associated car edge
     477              :     _IntermodalEdge* getCarEdge(const E* e) const {
     478       820108 :         if (e == nullptr) {
     479              :             return nullptr;
     480              :         }
     481              :         auto it = myCarLookup.find(e);
     482       568813 :         if (it == myCarLookup.end()) {
     483              :             return nullptr;
     484              :         }
     485       498593 :         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        50172 :         if (it == myStopConnections.end()) {
     492              :             return nullptr;
     493              :         }
     494        50172 :         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        26789 :     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        26789 :         const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
     517        13802 :                                       (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
     518        18805 :         const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
     519        21786 :         const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
     520        26789 :         const double pos = (startPos + endPos) / 2.;
     521              : #ifdef IntermodalRouter_DEBUG_ACCESS
     522              :         std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
     523              :                   << " access=" << isAccess << " tWait=" << taxiWait << "\n";
     524              : #endif
     525              :         if (myStopConnections.count(stopId) == 0) {
     526        49324 :             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
     527        24662 :             addEdge(myStopConnections[stopId]);
     528              :         }
     529        26789 :         _IntermodalEdge* const stopConn = myStopConnections[stopId];
     530        26789 :         const L* lane = getSidewalk<E, L>(stopEdge);
     531        26789 :         if (lane != nullptr) {
     532        23661 :             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
     533              :             double relPos;
     534              :             bool needSplit;
     535        23661 :             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
     536        23661 :             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
     537        23661 :             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
     538        23661 :             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
     539        23661 :             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
     540              :             _IntermodalEdge* carSplit = nullptr;
     541              :             if (myCarLookup.count(stopEdge) > 0) {
     542        23661 :                 if (needSplit) {
     543        23619 :                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     544              :                 }
     545        23661 :                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
     546              :             }
     547        23661 :             if (needSplit) {
     548        23619 :                 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
     549              :                     // adding access from car to walk
     550         5090 :                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     551        15270 :                     for (_IntermodalEdge* conn : {
     552              :                                 fwdSplit, backSplit
     553              :                             }) {
     554        10180 :                         if (transferCarWalk) {
     555         9732 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
     556         9732 :                             addEdge(access);
     557         9732 :                             beforeSplit->addSuccessor(access);
     558         9732 :                             access->addSuccessor(conn);
     559          448 :                         } else if (transferTaxiWalk) {
     560          448 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     561              :                         }
     562              :                     }
     563              :                 }
     564        23619 :                 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
     565          148 :                     _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     566          148 :                     addEdge(access);
     567          148 :                     stopConn->addSuccessor(access);
     568          148 :                     access->addSuccessor(carSplit);
     569              :                 }
     570              : 
     571              :                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
     572        23619 :                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
     573        23619 :                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
     574        23619 :                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
     575        47238 :                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     576        23619 :                 depConn->addSuccessor(fwdSplit);
     577        23619 :                 depConn->addSuccessor(backBeforeSplit);
     578              :                 depConn->setLength(fwdSplit->getLength());
     579        23619 :                 prevDep->removeSuccessor(backBeforeSplit);
     580        23619 :                 prevDep->addSuccessor(backSplit);
     581              :                 prevDep->setLength(backSplit->getLength());
     582        23619 :                 if (carSplit != nullptr) {
     583        23619 :                     depConn->addSuccessor(carSplit);
     584              :                 }
     585              : 
     586              :                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
     587        23619 :                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
     588        23619 :                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
     589        47238 :                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     590        23619 :                 fwdSplit->addSuccessor(arrConn);
     591        23619 :                 backBeforeSplit->addSuccessor(arrConn);
     592              :                 arrConn->setLength(fwdSplit->getLength());
     593        23619 :                 fwdSplit->removeSuccessor(prevArr);
     594        23619 :                 fwdBeforeSplit->addSuccessor(prevArr);
     595              :                 prevArr->setLength(backSplit->getLength());
     596        23619 :                 if (carSplit != nullptr) {
     597        23619 :                     if (carSplit->removeSuccessor(prevArr)) {
     598        23395 :                         carSplit->addSuccessor(arrConn);
     599        46790 :                         myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
     600              :                     }
     601              :                 }
     602        23619 :                 addConnectors(depConn, arrConn, splitIndex + 1);
     603              :             }
     604              :         } else {
     605              :             // pedestrians cannot walk here:
     606              :             // add stop edge as depart connector so that pedestrians may start at the stop
     607         3128 :             std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
     608              :             assert(splitList.size() > 0);
     609              :             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
     610         3713 :             while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
     611              :                 ++splitIt;
     612              :             }
     613         3128 :             splitList.insert(splitIt, stopConn);
     614              : 
     615         3128 :             if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
     616          573 :                 _IntermodalEdge* carEdge =  myCarLookup[stopEdge];
     617              :                 double relPos;
     618              :                 bool needSplit;
     619          573 :                 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
     620          573 :                 if (needSplit) {
     621          573 :                     _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     622          573 :                     splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
     623              : 
     624          573 :                     if (transferCarWalk || transferTaxiWalk) {
     625              :                         // adding access from car to walk
     626          573 :                         _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     627          573 :                         if (transferCarWalk) {
     628          468 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     629          468 :                             addEdge(access);
     630          468 :                             beforeSplit->addSuccessor(access);
     631          468 :                             access->addSuccessor(stopConn);
     632          105 :                         } else if (transferTaxiWalk) {
     633          105 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     634              :                         }
     635              :                     }
     636          573 :                     if (transferWalkTaxi) {
     637          105 :                         _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     638          105 :                         addEdge(access);
     639          105 :                         stopConn->addSuccessor(access);
     640          105 :                         access->addSuccessor(carSplit);
     641              :                     }
     642              :                 }
     643              :             }
     644              :         }
     645        26789 :     }
     646              : 
     647         1271 :     void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
     648              :         SUMOTime lastUntil = 0;
     649              :         StopParVector validStops;
     650         1271 :         if (addStops != nullptr) {
     651              :             // stops are part of a stand-alone route. until times are offsets from vehicle departure
     652         2832 :             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
     653         1644 :                 if (myStopConnections.count(stop.busstop) > 0) {
     654              :                     // compute stop times for the first vehicle
     655         1642 :                     const SUMOTime newUntil = stop.until + pars.depart;
     656         1642 :                     if (newUntil >= lastUntil) {
     657         1627 :                         validStops.push_back(stop);
     658         1627 :                         validStops.back().until = newUntil;
     659              :                         lastUntil = newUntil;
     660              :                     } else {
     661           60 :                         WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     662              :                     }
     663              :                 }
     664              :             }
     665              :         }
     666         3029 :         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
     667              :             // stops are part of the vehicle until times are absolute times for the first vehicle
     668         1758 :             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
     669         1603 :                 validStops.push_back(stop);
     670         1603 :                 lastUntil = stop.until;
     671              :             } else {
     672          155 :                 if (stop.busstop != "" && stop.until >= 0) {
     673            0 :                     WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     674              :                 }
     675              :             }
     676              :         }
     677         1271 :         if (validStops.size() < 2 && pars.line != "taxi") {
     678          141 :             WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
     679           47 :             return;
     680              :         }
     681              : 
     682         1224 :         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
     683         1224 :         if (lineEdges.empty()) {
     684              :             _IntermodalEdge* lastStop = nullptr;
     685              :             Position lastPos;
     686              :             SUMOTime lastTime = 0;
     687         4223 :             for (const SUMOVehicleParameter::Stop& s : validStops) {
     688         3054 :                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
     689         3054 :                 Position stopPos = E::getStopPosition(s);
     690         3054 :                 if (lastStop != nullptr) {
     691         3852 :                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
     692         1926 :                     addEdge(newEdge);
     693         5778 :                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
     694         1926 :                     lastStop->addSuccessor(newEdge);
     695         1926 :                     newEdge->addSuccessor(currStop);
     696         1926 :                     lineEdges.push_back(newEdge);
     697              :                 }
     698         3054 :                 lastTime = s.until;
     699              :                 lastStop = currStop;
     700         3054 :                 lastPos = stopPos;
     701              :             }
     702         1169 :             if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
     703              :                 myLoopedLines.insert(pars.line);
     704              :             }
     705              :         } else {
     706           55 :             if (validStops.size() != lineEdges.size() + 1) {
     707            0 :                 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
     708            0 :                 return;
     709              :             }
     710           55 :             if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
     711           36 :                 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     712           12 :                 return;
     713              :             }
     714              :             typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
     715              :             typename StopParVector::const_iterator s = validStops.begin() + 1;
     716          143 :             for (; s != validStops.end(); ++s, ++lineEdge) {
     717          100 :                 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
     718            0 :                     WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     719            0 :                     return;
     720              :                 }
     721              :             }
     722           43 :             SUMOTime lastTime = validStops.front().until;
     723           86 :             if (lineEdges.front()->hasSchedule(lastTime)) {
     724           24 :                 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
     725              :             }
     726          143 :             for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
     727          200 :                 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
     728          100 :                 lastTime = s->until;
     729              :             }
     730              :         }
     731         1271 :     }
     732              : 
     733              :     /** @brief Adds access edges for transfering from walking to vehicle use
     734              :     * @param[in] edge The edge on which the transfer takes place
     735              :     * @param[in] svc The permitted vehicle class for transfering
     736              :     */
     737        14602 :     void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
     738              :         assert(edge != nullptr);
     739              :         assert(myCarLookup.count(edge) != 0);
     740              :         assert(myBidiLookup.count(edge) != 0);
     741        14602 :         EdgePair pedestrianEdges = myBidiLookup[edge];
     742        14602 :         _IntermodalEdge* carEdge = myCarLookup[edge];
     743        14602 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
     744        14602 :         addEdge(access);
     745        14602 :         pedestrianEdges.first->addSuccessor(access);
     746        14602 :         pedestrianEdges.second->addSuccessor(access);
     747        14602 :         access->addSuccessor(carEdge);
     748        14602 :     }
     749              : 
     750              :     /** @brief Adds access edges for transfering from driving to walking that are only usable by a particular vehicle class
     751              :     * @param[in] from The origin edge of the transfer
     752              :     * @param[in] to The destination edge of the transfer
     753              :     * @param[in] svc The permitted vehicle class for transfering
     754              :     */
     755        37595 :     void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
     756        37595 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
     757        37595 :         addEdge(access);
     758        37595 :         from->addSuccessor(access);
     759        37595 :         access->addSuccessor(to);
     760        37595 :     }
     761              : 
     762              :     bool isLooped(const std::string lineID) const {
     763              :         return myLoopedLines.count(lineID) != 0;
     764              :     }
     765              : 
     766              : private:
     767              :     /** @brief Returns where to insert or use the split edge
     768              :     *
     769              :     * This method determines whether an edge needs to be split at the given position
     770              :     *  (if there is not already a split nearby) and returns the corresponding index in the split list.
     771              :     *
     772              :     * @param[in] toSplit The first edge in the split list
     773              :     * @param[in] pos The relative position on the edge where the stop is located
     774              :     * @param[out] relPos The relative position on the splitted edge
     775              :     * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
     776              :     * @return the index in the split list where the split edge needs to be added or reused
     777              :     */
     778        24234 :     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
     779        24234 :         relPos = pos;
     780        24234 :         needSplit = true;
     781              :         int splitIndex = 0;
     782              :         const auto& splitList = myAccessSplits.find(toSplit);
     783        24234 :         if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
     784        27355 :             for (const _IntermodalEdge* const split : splitList->second) {
     785        27355 :                 if (relPos < split->getLength() + POSITION_EPS) {
     786              :                     break;
     787              :                 }
     788        13569 :                 relPos -= split->getLength();
     789        13569 :                 splitIndex++;
     790              :             }
     791              :             assert(splitIndex < (int)splitList->second.size());
     792        13786 :             if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
     793           42 :                 needSplit = false;
     794              :             }
     795              :         }
     796        24234 :         return splitIndex;
     797              :     }
     798              : 
     799              :     /** @brief Splits an edge (if necessary) and connects it to a stopping edge
     800              :     *
     801              :     * This method determines whether an edge needs to be split at the given position
     802              :     *  (if there is not already a split nearby) and connects the stop edge via new access edges.
     803              :     *
     804              :     * @param[in] toSplit The first edge in the split list
     805              :     * @param[in] afterSplit The edge to add if a split is performed
     806              :     * @param[in] pos The relative position on the edge where the stop is located
     807              :     * @param[in] stopConn The stop edge to connect to
     808              :     * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
     809              :     * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
     810              :     */
     811        71556 :     void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
     812              :                    _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
     813              :                    _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
     814        71556 :         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
     815        71556 :         if (splitList.empty()) {
     816        30406 :             splitList.push_back(toSplit);
     817              :         }
     818        71556 :         if (!forward) {
     819        23661 :             splitIndex = (int)splitList.size() - 1 - splitIndex;
     820        23661 :             if (!needSplit) {
     821           42 :                 splitIndex--;
     822              :             }
     823              :         }
     824        71556 :         _IntermodalEdge* beforeSplit = splitList[splitIndex];
     825        71556 :         if (needSplit) {
     826        71430 :             addEdge(afterSplit);
     827        71430 :             beforeSplit->transferSuccessors(afterSplit);
     828        71430 :             beforeSplit->addSuccessor(afterSplit);
     829        71430 :             if (forward) {
     830        47811 :                 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     831              :                 beforeSplit->setLength(relPos);
     832              :             } else {
     833              :                 afterSplit->setLength(relPos);
     834        23619 :                 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     835              :                 // rename backward edges for easier referencing
     836              :                 const std::string newID = beforeSplit->getID();
     837        23619 :                 beforeSplit->setID(afterSplit->getID());
     838        23619 :                 afterSplit->setID(newID);
     839              :             }
     840        71430 :             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
     841              :         } else {
     842              :             // don't split, use the present split edges
     843          126 :             afterSplit = splitList[splitIndex + 1];
     844              :         }
     845              :         // add access to / from edge
     846        71556 :         if (addEntry) {
     847        52196 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     848        52196 :             addEdge(access);
     849        52196 :             beforeSplit->addSuccessor(access);
     850        52196 :             access->addSuccessor(stopConn);
     851              :         }
     852        71556 :         if (addExit) {
     853              :             // pedestrian case only, exit from public to pedestrian
     854        47322 :             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
     855        47322 :             addEdge(exit);
     856        47322 :             stopConn->addSuccessor(exit);
     857        47322 :             exit->addSuccessor(afterSplit);
     858              :         }
     859        71556 :     }
     860              : 
     861              : 
     862              : private:
     863              :     /// @brief the edge dictionary
     864              :     std::vector<_IntermodalEdge*> myEdges;
     865              : 
     866              :     /// @brief retrieve the forward and backward edge for the given input edge E
     867              :     std::map<const E*, EdgePair> myBidiLookup;
     868              : 
     869              :     /// @brief retrieve the depart edges for the given input edge E
     870              :     std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
     871              : 
     872              :     /// @brief retrieve the arrival edges for the given input edge E
     873              :     std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
     874              : 
     875              :     /// @brief the walking connector edge (fake walking area)
     876              :     std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
     877              : 
     878              :     /// @brief retrieve the car edge for the given input edge E
     879              :     std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
     880              : 
     881              :     /// @brief retrieve the public transport edges for the given line
     882              :     std::map<std::string, std::vector<_PTEdge*> > myPTLines;
     883              : 
     884              :     /// @brief retrieve the representing edge for the given stopping place
     885              :     std::map<std::string, _IntermodalEdge*> myStopConnections;
     886              : 
     887              :     /// @brief retrieve the splitted edges for the given "original"
     888              :     std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
     889              : 
     890              :     /// @brief looped lines need extra checking when building itineraries
     891              :     std::set<std::string > myLoopedLines;
     892              : 
     893              :     int myNumericalID;
     894              :     const int myCarWalkTransfer;
     895              : 
     896              : private:
     897              :     /// @brief Invalidated assignment operator
     898              :     IntermodalNetwork& operator=(const IntermodalNetwork& s);
     899              : 
     900              : };
        

Generated by: LCOV version 2.0-1