LCOV - code coverage report
Current view: top level - src/utils/router - IntermodalNetwork.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.7 % 393 376
Test Date: 2026-03-02 16:00:03 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-2026 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    IntermodalNetwork.h
      15              : /// @author  Jakob Erdmann
      16              : /// @author  Michael Behrisch
      17              : /// @author  Robert Hilbrich
      18              : /// @date    Mon, 03 March 2014
      19              : ///
      20              : // The Edge definition for the Intermodal Router
      21              : /****************************************************************************/
      22              : #pragma once
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <vector>
      27              : #include <algorithm>
      28              : #include <assert.h>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/common/Named.h>
      31              : #include <utils/common/SUMOTime.h>
      32              : #include <utils/common/ToString.h>
      33              : #include <utils/geom/Position.h>
      34              : #include <utils/vehicle/SUMOVehicleParameter.h>
      35              : #include "AccessEdge.h"
      36              : #include "CarEdge.h"
      37              : #include "IntermodalEdge.h"
      38              : #include "PedestrianEdge.h"
      39              : #include "PublicTransportEdge.h"
      40              : #include "StopEdge.h"
      41              : 
      42              : //#define IntermodalRouter_DEBUG_NETWORK
      43              : //#define IntermodalRouter_DEBUG_ACCESS
      44              : 
      45              : 
      46              : // ===========================================================================
      47              : // class definitions
      48              : // ===========================================================================
      49              : /** @brief where mode changes are possible
      50              : */
      51              : enum ModeChangeOptions {
      52              :     /// @brief parking areas
      53              :     PARKING_AREAS = 1 << 0,
      54              :     /// @brief public transport stops and access
      55              :     PT_STOPS = 1 << 1,
      56              :     /// @brief junctions with edges allowing the additional mode
      57              :     ALL_JUNCTIONS = 1 << 2,
      58              :     /// @brief taxi customer may exit at parking areas
      59              :     TAXI_DROPOFF_PARKING_AREAS = 1 << 3,
      60              :     /// @brief taxi customer may exit at public transport stops
      61              :     TAXI_DROPOFF_PT = 1 << 4,
      62              :     /// @brief taxi customer may exit anywhere
      63              :     TAXI_DROPOFF_ANYWHERE = 1 << 5,
      64              :     /// @brief taxi customer may be picked up at parking areas
      65              :     TAXI_PICKUP_PARKING_AREAS = 1 << 6,
      66              :     /// @brief taxi customer may be picked up at public transport stops
      67              :     TAXI_PICKUP_PT = 1 << 7,
      68              :     /// @brief taxi customer may be picked up anywhere
      69              :     TAXI_PICKUP_ANYWHERE = 1 << 8
      70              : };
      71              : 
      72              : 
      73              : 
      74              : /// @brief the intermodal network storing edges, connections and the mappings to the "real" edges
      75              : template<class E, class L, class N, class V>
      76              : class IntermodalNetwork {
      77              : private:
      78              :     typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
      79              :     typedef AccessEdge<E, L, N, V> _AccessEdge;
      80              :     typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
      81              :     typedef PublicTransportEdge<E, L, N, V> _PTEdge;
      82              :     typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
      83              : 
      84              : public:
      85              :     /* @brief build the pedestrian part of the intermodal network (once)
      86              :      * @param edges The list of MSEdge or ROEdge to build from
      87              :      * @param numericalID the start number for the creation of new edges
      88              :      */
      89        11082 :     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
      90        11082 :         : myNumericalID(0), myCarWalkTransfer(carWalkTransfer), myHavePTSchedules(false) {
      91              : #ifdef IntermodalRouter_DEBUG_NETWORK
      92              :         std::cout << "initIntermodalNetwork\n";
      93              : #endif
      94              :         // build the pedestrian edges and the depart / arrival connectors with lookup tables
      95              :         bool haveSeenWalkingArea = false;
      96       862624 :         for (const E* const edge : edges) {
      97       851542 :             if (edge->isTazConnector()) {
      98              :                 // only a single edge
      99        41318 :                 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
     100        41318 :                 addEdge(access);
     101        41318 :                 myDepartLookup[edge].push_back(access);
     102        41318 :                 myArrivalLookup[edge].push_back(access);
     103              :             } else {
     104       432355 :                 const L* lane = getSidewalk<E, L>(edge);
     105       810224 :                 if (lane != nullptr) {
     106       553454 :                     if (edge->isWalkingArea()) {
     107              :                         // only a single edge
     108       108156 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     109       108156 :                         myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
     110       108156 :                         myDepartLookup[edge].push_back(myEdges.back());
     111       108156 :                         myArrivalLookup[edge].push_back(myEdges.back());
     112              :                         haveSeenWalkingArea = true;
     113              :                     } else { // regular edge or crossing
     114              :                         // forward and backward edges
     115       445298 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     116       445298 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
     117       445298 :                         myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
     118              :                     }
     119              :                 }
     120       810224 :                 if (!edge->isWalkingArea()) {
     121              :                     // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
     122      1404136 :                     _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
     123      1404136 :                     _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
     124       702068 :                     addConnectors(departConn, arrivalConn, 0);
     125              :                 }
     126              :             }
     127              :         }
     128              : 
     129              :         // build the walking connectors if there are no walking areas
     130       862624 :         for (const E* const edge : edges) {
     131       851542 :             if (edge->isTazConnector() || edge->isInternal()) {
     132       464366 :                 continue;
     133              :             }
     134       387176 :             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       326714 :                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
     137         9210 :                     const N* const node = edge->getToJunction();
     138              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     139        14156 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     140         7078 :                         myWalkingConnectorLookup[node] = myEdges.back();
     141              :                     }
     142              :                 }
     143              :             } else {
     144       282972 :                 for (const N* const node : {
     145              :                             edge->getFromJunction(), edge->getToJunction()
     146              :                         }) {
     147              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     148       111608 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     149        55804 :                         myWalkingConnectorLookup[node] = myEdges.back();
     150              :                     }
     151              :                 }
     152              :             }
     153              :         }
     154              :         // build the connections
     155       862624 :         for (const E* const edge : edges) {
     156       851542 :             if (edge->isTazConnector()) {
     157              :                 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
     158        41318 :                 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
     159              :                 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
     160              :                 const E* other = edge->getOtherTazConnector();
     161        41318 :                 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
     162              :                 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
     163        84504 :                 for (const E* out : edge->getSuccessors()) {
     164        43186 :                     if (out->isNormal()) {
     165        70298 :                         tazDepart->addSuccessor(getDepartConnector(out));
     166        35149 :                         getArrivalConnector(out)->addSuccessor(otherTazArrive);
     167              :                     }
     168              :                 }
     169        84523 :                 for (const E* in : edge->getPredecessors()) {
     170        43205 :                     if (in->isNormal()) {
     171        35168 :                         getArrivalConnector(in)->addSuccessor(tazArrive);
     172        70336 :                         otherTazDepart->addSuccessor(getDepartConnector(in));
     173              :                     }
     174              :                 }
     175        41318 :                 continue;
     176        41318 :             }
     177       432355 :             const L* const sidewalk = getSidewalk<E, L>(edge);
     178       810224 :             if (sidewalk == nullptr) {
     179       256770 :                 continue;
     180              :             }
     181              :             // find all incoming and outgoing lanes for the sidewalk and
     182              :             // connect the corresponding IntermodalEdges
     183       553454 :             const EdgePair& pair = getBothDirections(edge);
     184              : #ifdef IntermodalRouter_DEBUG_NETWORK
     185              :             std::cout << "  building connections from " << sidewalk->getID() << "\n";
     186              : #endif
     187       553454 :             if (haveSeenWalkingArea) {
     188       312450 :                 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       549939 :                 for (const auto& target : outgoing) {
     194       387658 :                     if (target.first->getEdge().isWalkingArea()) {
     195              :                         hasWalkingArea = true;
     196              :                         break;
     197              :                     }
     198              :                 }
     199       700108 :                 for (const auto& target : outgoing) {
     200       387658 :                     const E* const targetEdge = &(target.first->getEdge());
     201       164770 :                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
     202       387658 :                                        && (!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       340107 :                         const EdgePair& targetPair = getBothDirections(targetEdge);
     209       340107 :                         pair.first->addSuccessor(targetPair.first);
     210       680214 :                         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       312450 :             }
     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       553454 :             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
     222       553454 :             if (toNodeConn != nullptr) {
     223              :                 // Check for the outgoing vias and use the shortest one as an approximation
     224       246339 :                 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       581567 :                 for (const auto& target : outgoing) {
     228       335228 :                     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       246339 :                 if (minVia != nullptr) {
     235              :                     const auto it = myBidiLookup.find(minVia);
     236        66859 :                     if (it != myBidiLookup.end()) {
     237              :                         interVia = it->second;
     238              :                     }
     239              :                 }
     240       246339 :                 if (!haveSeenWalkingArea) {
     241              :                     // if we have walking areas we should use them and not the connector
     242       482008 :                     pair.first->addSuccessor(toNodeConn, interVia.first);
     243              :                 }
     244       246339 :                 toNodeConn->addSuccessor(pair.second, interVia.second);
     245       246339 :             }
     246       553454 :             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
     247       553454 :             if (fromNodeConn != nullptr) {
     248       246424 :                 if (!haveSeenWalkingArea) {
     249       482008 :                     pair.second->addSuccessor(fromNodeConn);
     250              :                 }
     251       492848 :                 fromNodeConn->addSuccessor(pair.first);
     252              :             }
     253       553454 :             if (!edge->isWalkingArea()) {
     254              :                 // build connections from depart connector
     255       445298 :                 _IntermodalEdge* startConnector = getDepartConnector(edge);
     256       445298 :                 startConnector->addSuccessor(pair.first);
     257       890596 :                 startConnector->addSuccessor(pair.second);
     258              :                 // build connections to arrival connector
     259              :                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
     260       890596 :                 pair.first->addSuccessor(endConnector);
     261       890596 :                 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        11082 :     }
     271              : 
     272        11079 :     ~IntermodalNetwork() {
     273      3664586 :         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
     274      3653507 :             delete *it;
     275              :         }
     276        11079 :     }
     277              : 
     278      3653799 :     void addEdge(_IntermodalEdge* edge) {
     279      7307598 :         while ((int)myEdges.size() <= edge->getNumericalID()) {
     280      3653799 :             myEdges.push_back(0);
     281              :         }
     282      3653799 :         myEdges[edge->getNumericalID()] = edge;
     283      3653799 :     }
     284              : 
     285       729976 :     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
     286       729976 :         addEdge(depConn);
     287       729976 :         addEdge(arrConn);
     288       729976 :         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
     289       729976 :         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
     290       729976 :     }
     291              : 
     292              :     const std::vector<_IntermodalEdge*>& getAllEdges() {
     293        13904 :         return myEdges;
     294              :     }
     295              : 
     296              :     /// @brief Returns the pair of forward and backward edge
     297      2833346 :     const EdgePair& getBothDirections(const E* e) const {
     298              :         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
     299      2833346 :         if (it == myBidiLookup.end()) {
     300              :             assert(false);
     301            0 :             throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
     302              :         }
     303      2833346 :         return (*it).second;
     304              :     }
     305              : 
     306              :     /// @brief Returns the departing intermodal edge
     307       192620 :     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       192620 :         if (it == myDepartLookup.end()) {
     310            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     311              :         }
     312       192620 :         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          130 :             for (const _IntermodalEdge* const split : it->second) {
     317           71 :                 if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
     318           63 :                     const double dist = split->getEndPos() - split->getStartPos();
     319           63 :                     if (dist < bestDist) {
     320              :                         bestDist = dist;
     321              :                         best = split;
     322              :                     }
     323              :                 }
     324              :             }
     325              :             assert(best != nullptr);
     326           59 :             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       193833 :             while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     333              :                 totalLength += (*splitIt)->getLength();
     334              :                 ++splitIt;
     335              :             }
     336       192561 :             return *splitIt;
     337              :         }
     338              :     }
     339              : 
     340              :     /// @brief Returns the departing intermodal connector at the given split offset
     341      1589488 :     _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      1589488 :         if (it == myDepartLookup.end()) {
     344            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     345              :         }
     346      1589488 :         if (splitIndex >= (int)it->second.size()) {
     347            0 :             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
     348              :         }
     349      1589488 :         return it->second[splitIndex];
     350              :     }
     351              : 
     352              :     /// @brief Returns the arriving intermodal edge
     353       181602 :     _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       181602 :         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       524817 :         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     362              :             totalLength += (*splitIt)->getLength();
     363              :             ++splitIt;
     364              :         }
     365       181602 :         if (splitIt != splitList.end()) {
     366       181602 :             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      1490356 :         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        39458 :     _IntermodalEdge* getWalkingConnector(const E* e) const {
     379              :         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
     380        39458 :         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        39458 :         return it->second;
     393              :     }
     394              : 
     395         5454 :     void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
     396       408417 :         for (const E* const edge : edges) {
     397       402963 :             if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
     398       326157 :                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
     399       326157 :                 addEdge(myCarLookup[edge]);
     400              :             }
     401              :         }
     402       331611 :         for (const auto& edgePair : myCarLookup) {
     403       326157 :             _IntermodalEdge* const carEdge = edgePair.second;
     404              :             // connectivity within the car network
     405       793227 :             for (const auto& suc : edgePair.first->getViaSuccessors()) {
     406       934140 :                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
     407       934140 :                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
     408       467070 :                 if (sucCarEdge != nullptr) {
     409       400394 :                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
     410              :                 }
     411              :             }
     412              :             // connectivity to the pedestrian network (only for normal edges)
     413       326157 :             if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
     414       215722 :                 continue;
     415              :             }
     416       110435 :             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       110435 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     435              :                 // add access edges that allow exiting a taxi
     436        39163 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     437        39163 :                 if (walkCon != 0) {
     438        17314 :                     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       285095 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     442       319950 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     443       104632 :                             addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
     444              :                         }
     445              :                     }
     446       288126 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     447       326081 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     448       107803 :                             addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
     449              :                         }
     450              :                     }
     451              :                 }
     452              :             }
     453              :             // use intermediate access edge that prevents taxi departure
     454       110435 :             _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
     455       110435 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
     456       110435 :             addEdge(access);
     457       110435 :             departConn->addSuccessor(access);
     458       110435 :             access->addSuccessor(carEdge);
     459       110435 :             if ((myCarWalkTransfer & TAXI_PICKUP_ANYWHERE) != 0) {
     460              :                 // taxi may depart anywhere but there is a time penalty
     461        38487 :                 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     462        38487 :                 addEdge(taxiAccess);
     463        38487 :                 departConn->addSuccessor(taxiAccess);
     464        38487 :                 taxiAccess->addSuccessor(carEdge);
     465              :             }
     466       110435 :             if ((myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     467              :                 // taxi (as all other cars) may arrive anywhere
     468        78422 :                 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
     469              :             } else {
     470              :                 // use intermediate access edge that prevents taxi arrival
     471       142448 :                 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
     472              :             }
     473              :         }
     474         5454 :     }
     475              : 
     476              :     /// @brief Returns the associated car edge
     477              :     _IntermodalEdge* getCarEdge(const E* e) const {
     478       934140 :         if (e == nullptr) {
     479              :             return nullptr;
     480              :         }
     481              :         auto it = myCarLookup.find(e);
     482       655804 :         if (it == myCarLookup.end()) {
     483              :             return nullptr;
     484              :         }
     485       589128 :         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        50346 :         if (it == myStopConnections.end()) {
     492              :             return nullptr;
     493              :         }
     494        50346 :         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        31154 :     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        31154 :         const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
     517        16470 :                                       (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
     518        31154 :         const bool transferTaxiWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_DROPOFF_PARKING_AREAS) != 0) ||
     519        16470 :                                        (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0));
     520        31154 :         const bool transferWalkTaxi = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_PICKUP_PARKING_AREAS) != 0) ||
     521        16470 :                                        (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0));
     522        31154 :         const double pos = (startPos + endPos) / 2.;
     523              : #ifdef IntermodalRouter_DEBUG_ACCESS
     524              :         std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
     525              :                   << " access=" << isAccess << " tWait=" << taxiWait << "\n";
     526              : #endif
     527              :         if (myStopConnections.count(stopId) == 0) {
     528        55840 :             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
     529        27920 :             addEdge(myStopConnections[stopId]);
     530              :         }
     531        31154 :         _IntermodalEdge* const stopConn = myStopConnections[stopId];
     532        31154 :         const L* lane = getSidewalk<E, L>(stopEdge);
     533        31154 :         if (lane != nullptr) {
     534        28046 :             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
     535              :             double relPos;
     536              :             bool needSplit;
     537        28046 :             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
     538        28046 :             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
     539        28046 :             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
     540        28046 :             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
     541        28046 :             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
     542              :             _IntermodalEdge* carSplit = nullptr;
     543              :             if (myCarLookup.count(stopEdge) > 0) {
     544        28046 :                 if (needSplit) {
     545        27908 :                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     546              :                 }
     547        28046 :                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
     548              :             }
     549        28046 :             if (needSplit) {
     550        27908 :                 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
     551              :                     // adding access from car to walk
     552         5837 :                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     553        17511 :                     for (_IntermodalEdge* conn : {
     554              :                                 fwdSplit, backSplit
     555              :                             }) {
     556        11674 :                         if (transferCarWalk) {
     557        11242 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
     558        11242 :                             addEdge(access);
     559        11242 :                             beforeSplit->addSuccessor(access);
     560        11242 :                             access->addSuccessor(conn);
     561          432 :                         } else if (transferTaxiWalk) {
     562          432 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     563              :                         }
     564              :                     }
     565              :                 }
     566        27908 :                 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
     567          178 :                     _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     568          178 :                     addEdge(access);
     569          178 :                     stopConn->addSuccessor(access);
     570          178 :                     access->addSuccessor(carSplit);
     571              :                 }
     572              : 
     573              :                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
     574        27908 :                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
     575        27908 :                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
     576        27908 :                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
     577        55816 :                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     578        27908 :                 depConn->addSuccessor(fwdSplit);
     579        27908 :                 depConn->addSuccessor(backBeforeSplit);
     580              :                 depConn->setLength(fwdSplit->getLength());
     581        27908 :                 prevDep->removeSuccessor(backBeforeSplit);
     582        27908 :                 prevDep->addSuccessor(backSplit);
     583              :                 prevDep->setLength(backSplit->getLength());
     584        27908 :                 if (carSplit != nullptr) {
     585        27908 :                     depConn->addSuccessor(carSplit);
     586              :                 }
     587              : 
     588              :                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
     589        27908 :                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
     590        27908 :                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
     591        55816 :                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     592        27908 :                 fwdSplit->addSuccessor(arrConn);
     593        27908 :                 backBeforeSplit->addSuccessor(arrConn);
     594              :                 arrConn->setLength(fwdSplit->getLength());
     595        27908 :                 fwdSplit->removeSuccessor(prevArr);
     596        27908 :                 fwdBeforeSplit->addSuccessor(prevArr);
     597              :                 prevArr->setLength(backSplit->getLength());
     598        27908 :                 if (carSplit != nullptr) {
     599        27908 :                     if (carSplit->removeSuccessor(prevArr)) {
     600         2998 :                         carSplit->addSuccessor(arrConn);
     601         5996 :                         myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
     602              :                     } else {
     603              :                         // check for restricted access
     604        52830 :                         for (_IntermodalEdge* out : carSplit->getSuccessors()) {
     605        52830 :                             _AccessEdge* aOut = dynamic_cast<_AccessEdge*>(out);
     606        52830 :                             if (aOut != nullptr && aOut->removeSuccessor(prevArr)) {
     607        24910 :                                 aOut->addSuccessor(arrConn);
     608        24910 :                                 addRestrictedCarExit(
     609        24910 :                                     myAccessSplits[myCarLookup[stopEdge]][splitIndex],
     610              :                                     prevArr,
     611              :                                     aOut->getVehicleRetriction());
     612              :                                 break;
     613              :                             }
     614              :                         }
     615              :                     }
     616              :                 }
     617        27908 :                 addConnectors(depConn, arrConn, splitIndex + 1);
     618              :             }
     619              :         } else {
     620              :             // pedestrians cannot walk here:
     621              :             // add stop edge as depart connector so that pedestrians may start at the stop
     622         3108 :             std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
     623              :             assert(splitList.size() > 0);
     624              :             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
     625         3744 :             while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
     626              :                 ++splitIt;
     627              :             }
     628         3108 :             splitList.insert(splitIt, stopConn);
     629              : 
     630         3108 :             if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
     631          500 :                 _IntermodalEdge* carEdge =  myCarLookup[stopEdge];
     632              :                 double relPos;
     633              :                 bool needSplit;
     634          500 :                 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
     635          500 :                 if (needSplit) {
     636          500 :                     _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     637          500 :                     splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
     638              : 
     639          500 :                     if (transferCarWalk || transferTaxiWalk) {
     640              :                         // adding access from car to walk
     641          500 :                         _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     642          500 :                         if (transferCarWalk) {
     643          403 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     644          403 :                             addEdge(access);
     645          403 :                             beforeSplit->addSuccessor(access);
     646          403 :                             access->addSuccessor(stopConn);
     647           97 :                         } else if (transferTaxiWalk) {
     648           97 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     649              :                         }
     650              :                     }
     651          500 :                     if (transferWalkTaxi) {
     652           97 :                         _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     653           97 :                         addEdge(access);
     654           97 :                         stopConn->addSuccessor(access);
     655           97 :                         access->addSuccessor(carSplit);
     656              :                     }
     657              :                 }
     658              :             }
     659              :         }
     660        31154 :     }
     661              : 
     662              :     bool hasPTSchedules() const {
     663          223 :         return myHavePTSchedules;
     664              :     }
     665              : 
     666         1558 :     void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
     667              :         SUMOTime lastUntil = 0;
     668              :         StopParVector validStops;
     669         1558 :         if (addStops != nullptr) {
     670              :             // stops are part of a stand-alone route. until times are offsets from vehicle departure
     671         4866 :             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
     672         3391 :                 if (myStopConnections.count(stop.busstop) > 0) {
     673              :                     // compute stop times for the first vehicle
     674         3389 :                     const SUMOTime newUntil = stop.until + pars.depart;
     675         3389 :                     if (newUntil >= lastUntil) {
     676         3374 :                         validStops.push_back(stop);
     677         3374 :                         validStops.back().until = newUntil;
     678              :                         lastUntil = newUntil;
     679              :                     } else {
     680           60 :                         WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     681              :                     }
     682              :                 }
     683              :             }
     684              :         }
     685         3316 :         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
     686              :             // stops are part of the vehicle until times are absolute times for the first vehicle
     687         1758 :             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
     688         1603 :                 validStops.push_back(stop);
     689         1603 :                 lastUntil = stop.until;
     690              :             } else {
     691          155 :                 if (stop.busstop != "" && stop.until >= 0) {
     692            0 :                     WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     693              :                 }
     694              :             }
     695              :         }
     696         1558 :         if (validStops.size() < 2 && pars.line != "taxi") {
     697          204 :             WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
     698           68 :             return;
     699              :         }
     700              : 
     701         1490 :         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
     702         1490 :         if (lineEdges.empty()) {
     703              :             _IntermodalEdge* lastStop = nullptr;
     704              :             Position lastPos;
     705              :             SUMOTime lastTime = 0;
     706         6215 :             for (const SUMOVehicleParameter::Stop& s : validStops) {
     707         4780 :                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
     708         4780 :                 Position stopPos = E::getStopPosition(s);
     709         4780 :                 if (lastStop != nullptr) {
     710         6774 :                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
     711         3387 :                     addEdge(newEdge);
     712         6774 :                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
     713         3387 :                     myHavePTSchedules = true;
     714         3387 :                     lastStop->addSuccessor(newEdge);
     715         3387 :                     newEdge->addSuccessor(currStop);
     716         3387 :                     lineEdges.push_back(newEdge);
     717              :                 }
     718         4780 :                 lastTime = s.until;
     719              :                 lastStop = currStop;
     720         4780 :                 lastPos = stopPos;
     721              :             }
     722         1435 :             if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
     723              :                 myLoopedLines.insert(pars.line);
     724              :             }
     725              :         } else {
     726           55 :             if (validStops.size() != lineEdges.size() + 1) {
     727            0 :                 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
     728            0 :                 return;
     729              :             }
     730           55 :             if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
     731           36 :                 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     732           12 :                 return;
     733              :             }
     734              :             typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
     735              :             typename StopParVector::const_iterator s = validStops.begin() + 1;
     736          143 :             for (; s != validStops.end(); ++s, ++lineEdge) {
     737          100 :                 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
     738            0 :                     WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     739            0 :                     return;
     740              :                 }
     741              :             }
     742           43 :             SUMOTime lastTime = validStops.front().until;
     743           86 :             if (lineEdges.front()->hasSchedule(lastTime)) {
     744           24 :                 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
     745              :             }
     746          143 :             for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
     747          200 :                 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
     748          100 :                 myHavePTSchedules = true;
     749          100 :                 lastTime = s->until;
     750              :             }
     751              :         }
     752         1558 :     }
     753              : 
     754              :     /** @brief Adds access edges for transfering from walking to vehicle use
     755              :     * @param[in] edge The edge on which the transfer takes place
     756              :     * @param[in] svc The permitted vehicle class for transfering
     757              :     */
     758        44125 :     void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
     759              :         assert(edge != nullptr);
     760              :         assert(myCarLookup.count(edge) != 0);
     761              :         assert(myBidiLookup.count(edge) != 0);
     762        44125 :         EdgePair pedestrianEdges = myBidiLookup[edge];
     763        44125 :         _IntermodalEdge* carEdge = myCarLookup[edge];
     764        44125 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
     765        44125 :         addEdge(access);
     766        44125 :         pedestrianEdges.first->addSuccessor(access);
     767        44125 :         pedestrianEdges.second->addSuccessor(access);
     768        44125 :         access->addSuccessor(carEdge);
     769        44125 :     }
     770              : 
     771              :     /** @brief Adds access edges for transfering from driving to walking that are only usable by a particular vehicle class
     772              :     * @param[in] from The origin edge of the transfer
     773              :     * @param[in] to The destination edge of the transfer
     774              :     * @param[in] svc The permitted vehicle class for transfering
     775              :     */
     776       326412 :     void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
     777       326412 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
     778       326412 :         addEdge(access);
     779       326412 :         from->addSuccessor(access);
     780       326412 :         access->addSuccessor(to);
     781       326412 :     }
     782              : 
     783              :     bool isLooped(const std::string lineID) const {
     784              :         return myLoopedLines.count(lineID) != 0;
     785              :     }
     786              : 
     787              : private:
     788              :     /** @brief Returns where to insert or use the split edge
     789              :     *
     790              :     * This method determines whether an edge needs to be split at the given position
     791              :     *  (if there is not already a split nearby) and returns the corresponding index in the split list.
     792              :     *
     793              :     * @param[in] toSplit The first edge in the split list
     794              :     * @param[in] pos The relative position on the edge where the stop is located
     795              :     * @param[out] relPos The relative position on the splitted edge
     796              :     * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
     797              :     * @return the index in the split list where the split edge needs to be added or reused
     798              :     */
     799        28546 :     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
     800        28546 :         relPos = pos;
     801        28546 :         needSplit = true;
     802              :         int splitIndex = 0;
     803              :         const auto& splitList = myAccessSplits.find(toSplit);
     804        28546 :         if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
     805        31628 :             for (const _IntermodalEdge* const split : splitList->second) {
     806        31628 :                 if (relPos < split->getLength() + POSITION_EPS) {
     807              :                     break;
     808              :                 }
     809        15641 :                 relPos -= split->getLength();
     810        15641 :                 splitIndex++;
     811              :             }
     812              :             assert(splitIndex < (int)splitList->second.size());
     813        15987 :             if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
     814          138 :                 needSplit = false;
     815              :             }
     816              :         }
     817        28546 :         return splitIndex;
     818              :     }
     819              : 
     820              :     /** @brief Splits an edge (if necessary) and connects it to a stopping edge
     821              :     *
     822              :     * This method determines whether an edge needs to be split at the given position
     823              :     *  (if there is not already a split nearby) and connects the stop edge via new access edges.
     824              :     *
     825              :     * @param[in] toSplit The first edge in the split list
     826              :     * @param[in] afterSplit The edge to add if a split is performed
     827              :     * @param[in] pos The relative position on the edge where the stop is located
     828              :     * @param[in] stopConn The stop edge to connect to
     829              :     * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
     830              :     * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
     831              :     */
     832        84638 :     void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
     833              :                    _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
     834              :                    _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
     835        84638 :         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
     836        84638 :         if (splitList.empty()) {
     837        36877 :             splitList.push_back(toSplit);
     838              :         }
     839        84638 :         if (!forward) {
     840        28046 :             splitIndex = (int)splitList.size() - 1 - splitIndex;
     841        28046 :             if (!needSplit) {
     842          138 :                 splitIndex--;
     843              :             }
     844              :         }
     845        84638 :         _IntermodalEdge* beforeSplit = splitList[splitIndex];
     846        84638 :         if (needSplit) {
     847        84224 :             addEdge(afterSplit);
     848        84224 :             beforeSplit->transferSuccessors(afterSplit);
     849        84224 :             beforeSplit->addSuccessor(afterSplit);
     850        84224 :             if (forward) {
     851        56316 :                 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     852              :                 beforeSplit->setLength(relPos);
     853              :             } else {
     854              :                 afterSplit->setLength(relPos);
     855        27908 :                 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     856              :                 // rename backward edges for easier referencing
     857              :                 const std::string newID = beforeSplit->getID();
     858        27908 :                 beforeSplit->setID(afterSplit->getID());
     859        27908 :                 afterSplit->setID(newID);
     860              :             }
     861        84224 :             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
     862              :         } else {
     863              :             // don't split, use the present split edges
     864          414 :             afterSplit = splitList[splitIndex + 1];
     865              :         }
     866              :         // add access to / from edge
     867        84638 :         if (addEntry) {
     868        61736 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     869        61736 :             addEdge(access);
     870        61736 :             beforeSplit->addSuccessor(access);
     871        61736 :             access->addSuccessor(stopConn);
     872              :         }
     873        84638 :         if (addExit) {
     874              :             // pedestrian case only, exit from public to pedestrian
     875        56092 :             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
     876        56092 :             addEdge(exit);
     877        56092 :             stopConn->addSuccessor(exit);
     878        56092 :             exit->addSuccessor(afterSplit);
     879              :         }
     880        84638 :     }
     881              : 
     882              : 
     883              : private:
     884              :     /// @brief the edge dictionary
     885              :     std::vector<_IntermodalEdge*> myEdges;
     886              : 
     887              :     /// @brief retrieve the forward and backward edge for the given input edge E
     888              :     std::map<const E*, EdgePair> myBidiLookup;
     889              : 
     890              :     /// @brief retrieve the depart edges for the given input edge E
     891              :     std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
     892              : 
     893              :     /// @brief retrieve the arrival edges for the given input edge E
     894              :     std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
     895              : 
     896              :     /// @brief the walking connector edge (fake walking area)
     897              :     std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
     898              : 
     899              :     /// @brief retrieve the car edge for the given input edge E
     900              :     std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
     901              : 
     902              :     /// @brief retrieve the public transport edges for the given line
     903              :     std::map<std::string, std::vector<_PTEdge*> > myPTLines;
     904              : 
     905              :     /// @brief retrieve the representing edge for the given stopping place
     906              :     std::map<std::string, _IntermodalEdge*> myStopConnections;
     907              : 
     908              :     /// @brief retrieve the splitted edges for the given "original"
     909              :     std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
     910              : 
     911              :     /// @brief looped lines need extra checking when building itineraries
     912              :     std::set<std::string > myLoopedLines;
     913              : 
     914              :     int myNumericalID;
     915              :     const int myCarWalkTransfer;
     916              :     bool myHavePTSchedules;
     917              : 
     918              : private:
     919              :     /// @brief Invalidated assignment operator
     920              :     IntermodalNetwork& operator=(const IntermodalNetwork& s);
     921              : 
     922              : };
        

Generated by: LCOV version 2.0-1