LCOV - code coverage report
Current view: top level - src/utils/router - IntermodalNetwork.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.6 % 390 373
Test Date: 2025-11-13 15:38:19 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 << 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        10595 :     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
      90        10595 :         : 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       895780 :         for (const E* const edge : edges) {
      97       885185 :             if (edge->isTazConnector()) {
      98              :                 // only a single edge
      99        94986 :                 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
     100        94986 :                 addEdge(access);
     101        94986 :                 myDepartLookup[edge].push_back(access);
     102        94986 :                 myArrivalLookup[edge].push_back(access);
     103              :             } else {
     104       414290 :                 const L* lane = getSidewalk<E, L>(edge);
     105       790199 :                 if (lane != nullptr) {
     106       539019 :                     if (edge->isWalkingArea()) {
     107              :                         // only a single edge
     108       104007 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     109       104007 :                         myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
     110       104007 :                         myDepartLookup[edge].push_back(myEdges.back());
     111       104007 :                         myArrivalLookup[edge].push_back(myEdges.back());
     112              :                         haveSeenWalkingArea = true;
     113              :                     } else { // regular edge or crossing
     114              :                         // forward and backward edges
     115       435012 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     116       435012 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
     117       435012 :                         myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
     118              :                     }
     119              :                 }
     120       790199 :                 if (!edge->isWalkingArea()) {
     121              :                     // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
     122      1372384 :                     _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
     123      1372384 :                     _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
     124       686192 :                     addConnectors(departConn, arrivalConn, 0);
     125              :                 }
     126              :             }
     127              :         }
     128              : 
     129              :         // build the walking connectors if there are no walking areas
     130       895780 :         for (const E* const edge : edges) {
     131       885185 :             if (edge->isTazConnector() || edge->isInternal()) {
     132       508628 :                 continue;
     133              :             }
     134       376557 :             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       318858 :                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
     137         9298 :                     const N* const node = edge->getToJunction();
     138              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     139        13812 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     140         6906 :                         myWalkingConnectorLookup[node] = myEdges.back();
     141              :                     }
     142              :                 }
     143              :             } else {
     144       274803 :                 for (const N* const node : {
     145              :                             edge->getFromJunction(), edge->getToJunction()
     146              :                         }) {
     147              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     148       108782 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     149        54391 :                         myWalkingConnectorLookup[node] = myEdges.back();
     150              :                     }
     151              :                 }
     152              :             }
     153              :         }
     154              :         // build the connections
     155       895780 :         for (const E* const edge : edges) {
     156       885185 :             if (edge->isTazConnector()) {
     157              :                 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
     158        94986 :                 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
     159              :                 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
     160              :                 const E* other = edge->getOtherTazConnector();
     161        94986 :                 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
     162              :                 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
     163       207257 :                 for (const E* out : edge->getSuccessors()) {
     164       112271 :                     if (out->isNormal()) {
     165       140764 :                         tazDepart->addSuccessor(getDepartConnector(out));
     166        70382 :                         getArrivalConnector(out)->addSuccessor(otherTazArrive);
     167              :                     }
     168              :                 }
     169       207276 :                 for (const E* in : edge->getPredecessors()) {
     170       112290 :                     if (in->isNormal()) {
     171        70401 :                         getArrivalConnector(in)->addSuccessor(tazArrive);
     172       140802 :                         otherTazDepart->addSuccessor(getDepartConnector(in));
     173              :                     }
     174              :                 }
     175        94986 :                 continue;
     176        94986 :             }
     177       414290 :             const L* const sidewalk = getSidewalk<E, L>(edge);
     178       790199 :             if (sidewalk == nullptr) {
     179       251180 :                 continue;
     180              :             }
     181              :             // find all incoming and outgoing lanes for the sidewalk and
     182              :             // connect the corresponding IntermodalEdges
     183       539019 :             const EdgePair& pair = getBothDirections(edge);
     184              : #ifdef IntermodalRouter_DEBUG_NETWORK
     185              :             std::cout << "  building connections from " << sidewalk->getID() << "\n";
     186              : #endif
     187       539019 :             if (haveSeenWalkingArea) {
     188       304080 :                 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       537214 :                 for (const auto& target : outgoing) {
     194       378840 :                     if (target.first->getEdge().isWalkingArea()) {
     195              :                         hasWalkingArea = true;
     196              :                         break;
     197              :                     }
     198              :                 }
     199       682920 :                 for (const auto& target : outgoing) {
     200       378840 :                     const E* const targetEdge = &(target.first->getEdge());
     201       155520 :                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
     202       378840 :                                        && (!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       331331 :                         const EdgePair& targetPair = getBothDirections(targetEdge);
     209       331331 :                         pair.first->addSuccessor(targetPair.first);
     210       662662 :                         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       304080 :             }
     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       539019 :             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
     222       539019 :             if (toNodeConn != nullptr) {
     223              :                 // Check for the outgoing vias and use the shortest one as an approximation
     224       240385 :                 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       567975 :                 for (const auto& target : outgoing) {
     228       327590 :                     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       240385 :                 if (minVia != nullptr) {
     235              :                     const auto it = myBidiLookup.find(minVia);
     236        64873 :                     if (it != myBidiLookup.end()) {
     237              :                         interVia = it->second;
     238              :                     }
     239              :                 }
     240       240385 :                 if (!haveSeenWalkingArea) {
     241              :                     // if we have walking areas we should use them and not the connector
     242       469878 :                     pair.first->addSuccessor(toNodeConn, interVia.first);
     243              :                 }
     244       240385 :                 toNodeConn->addSuccessor(pair.second, interVia.second);
     245       240385 :             }
     246       539019 :             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
     247       539019 :             if (fromNodeConn != nullptr) {
     248       240467 :                 if (!haveSeenWalkingArea) {
     249       469878 :                     pair.second->addSuccessor(fromNodeConn);
     250              :                 }
     251       480934 :                 fromNodeConn->addSuccessor(pair.first);
     252              :             }
     253       539019 :             if (!edge->isWalkingArea()) {
     254              :                 // build connections from depart connector
     255       435012 :                 _IntermodalEdge* startConnector = getDepartConnector(edge);
     256       435012 :                 startConnector->addSuccessor(pair.first);
     257       870024 :                 startConnector->addSuccessor(pair.second);
     258              :                 // build connections to arrival connector
     259              :                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
     260       870024 :                 pair.first->addSuccessor(endConnector);
     261       870024 :                 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        10595 :     }
     271              : 
     272        10594 :     ~IntermodalNetwork() {
     273      3601588 :         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
     274      3590994 :             delete *it;
     275              :         }
     276        10594 :     }
     277              : 
     278      3591086 :     void addEdge(_IntermodalEdge* edge) {
     279      7182172 :         while ((int)myEdges.size() <= edge->getNumericalID()) {
     280      3591086 :             myEdges.push_back(0);
     281              :         }
     282      3591086 :         myEdges[edge->getNumericalID()] = edge;
     283      3591086 :     }
     284              : 
     285       711202 :     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
     286       711202 :         addEdge(depConn);
     287       711202 :         addEdge(arrConn);
     288       711202 :         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
     289       711202 :         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
     290       711202 :     }
     291              : 
     292              :     const std::vector<_IntermodalEdge*>& getAllEdges() {
     293        13240 :         return myEdges;
     294              :     }
     295              : 
     296              :     /// @brief Returns the pair of forward and backward edge
     297      2862152 :     const EdgePair& getBothDirections(const E* e) const {
     298              :         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
     299      2862152 :         if (it == myBidiLookup.end()) {
     300              :             assert(false);
     301            0 :             throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
     302              :         }
     303      2862152 :         return (*it).second;
     304              :     }
     305              : 
     306              :     /// @brief Returns the departing intermodal edge
     307       191994 :     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       191994 :         if (it == myDepartLookup.end()) {
     310            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     311              :         }
     312       191994 :         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       193197 :             while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     333              :                 totalLength += (*splitIt)->getLength();
     334              :                 ++splitIt;
     335              :             }
     336       191935 :             return *splitIt;
     337              :         }
     338              :     }
     339              : 
     340              :     /// @brief Returns the departing intermodal connector at the given split offset
     341      1778675 :     _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      1778675 :         if (it == myDepartLookup.end()) {
     344            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     345              :         }
     346      1778675 :         if (splitIndex >= (int)it->second.size()) {
     347            0 :             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
     348              :         }
     349      1778675 :         return it->second[splitIndex];
     350              :     }
     351              : 
     352              :     /// @brief Returns the arriving intermodal edge
     353       181082 :     _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       181082 :         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       517003 :         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     362              :             totalLength += (*splitIt)->getLength();
     363              :             ++splitIt;
     364              :         }
     365       181082 :         if (splitIt != splitList.end()) {
     366       181082 :             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      1686518 :         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        38884 :     _IntermodalEdge* getWalkingConnector(const E* e) const {
     379              :         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
     380        38884 :         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        38884 :         return it->second;
     393              :     }
     394              : 
     395         5131 :     void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
     396       419630 :         for (const E* const edge : edges) {
     397       414499 :             if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
     398       314338 :                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
     399       314338 :                 addEdge(myCarLookup[edge]);
     400              :             }
     401              :         }
     402       319469 :         for (const auto& edgePair : myCarLookup) {
     403       314338 :             _IntermodalEdge* const carEdge = edgePair.second;
     404              :             // connectivity within the car network
     405       782721 :             for (const auto& suc : edgePair.first->getViaSuccessors()) {
     406       936766 :                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
     407       936766 :                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
     408       468383 :                 if (sucCarEdge != nullptr) {
     409       387291 :                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
     410              :                 }
     411              :             }
     412              :             // connectivity to the pedestrian network (only for normal edges)
     413       314338 :             if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
     414       208554 :                 continue;
     415              :             }
     416       105784 :             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          216 :                     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       105784 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     435              :                 // add access edges that allow exiting a taxi
     436        38589 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     437        38589 :                 if (walkCon != 0) {
     438        16986 :                     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       280001 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     442       315238 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     443       103068 :                             addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
     444              :                         }
     445              :                     }
     446       283016 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     447       321337 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     448       106223 :                             addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
     449              :                         }
     450              :                     }
     451              :                 }
     452              :             }
     453              :             // use intermediate access edge that prevents taxi departure
     454       105784 :             _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
     455       105784 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
     456       105784 :             addEdge(access);
     457       105784 :             departConn->addSuccessor(access);
     458       105784 :             access->addSuccessor(carEdge);
     459       105784 :             if ((myCarWalkTransfer & TAXI_PICKUP_ANYWHERE) != 0) {
     460              :                 // taxi may depart anywhere but there is a time penalty
     461        38021 :                 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     462        38021 :                 addEdge(taxiAccess);
     463        38021 :                 departConn->addSuccessor(taxiAccess);
     464        38021 :                 taxiAccess->addSuccessor(carEdge);
     465              :             }
     466       105784 :             if ((myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     467              :                 // taxi (as all other cars) may arrive anywhere
     468        77274 :                 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
     469              :             } else {
     470              :                 // use intermediate access edge that prevents taxi arrival
     471       134294 :                 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
     472              :             }
     473              :         }
     474         5131 :     }
     475              : 
     476              :     /// @brief Returns the associated car edge
     477              :     _IntermodalEdge* getCarEdge(const E* e) const {
     478       936766 :         if (e == nullptr) {
     479              :             return nullptr;
     480              :         }
     481              :         auto it = myCarLookup.find(e);
     482       651381 :         if (it == myCarLookup.end()) {
     483              :             return nullptr;
     484              :         }
     485       570289 :         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        50230 :         if (it == myStopConnections.end()) {
     492              :             return nullptr;
     493              :         }
     494        50230 :         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        28735 :     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        28735 :         const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
     517        15537 :                                       (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
     518        28735 :         const bool transferTaxiWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_DROPOFF_PARKING_AREAS) != 0) ||
     519        15537 :                                        (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0));
     520        28735 :         const bool transferWalkTaxi = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & TAXI_PICKUP_PARKING_AREAS) != 0) ||
     521        15537 :                                        (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0));
     522        28735 :         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        50764 :             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
     529        25382 :             addEdge(myStopConnections[stopId]);
     530              :         }
     531        28735 :         _IntermodalEdge* const stopConn = myStopConnections[stopId];
     532        28735 :         const L* lane = getSidewalk<E, L>(stopEdge);
     533        28735 :         if (lane != nullptr) {
     534        25133 :             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
     535              :             double relPos;
     536              :             bool needSplit;
     537        25133 :             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
     538        25133 :             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
     539        25133 :             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
     540        25133 :             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
     541        25133 :             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
     542              :             _IntermodalEdge* carSplit = nullptr;
     543              :             if (myCarLookup.count(stopEdge) > 0) {
     544        25133 :                 if (needSplit) {
     545        25010 :                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     546              :                 }
     547        25133 :                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
     548              :             }
     549        25133 :             if (needSplit) {
     550        25010 :                 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
     551              :                     // adding access from car to walk
     552         5156 :                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     553        15468 :                     for (_IntermodalEdge* conn : {
     554              :                                 fwdSplit, backSplit
     555              :                             }) {
     556        10312 :                         if (transferCarWalk) {
     557         9880 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
     558         9880 :                             addEdge(access);
     559         9880 :                             beforeSplit->addSuccessor(access);
     560         9880 :                             access->addSuccessor(conn);
     561          432 :                         } else if (transferTaxiWalk) {
     562          432 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     563              :                         }
     564              :                     }
     565              :                 }
     566        25010 :                 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
     567          174 :                     _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     568          174 :                     addEdge(access);
     569          174 :                     stopConn->addSuccessor(access);
     570          174 :                     access->addSuccessor(carSplit);
     571              :                 }
     572              : 
     573              :                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
     574        25010 :                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
     575        25010 :                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
     576        25010 :                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
     577        50020 :                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     578        25010 :                 depConn->addSuccessor(fwdSplit);
     579        25010 :                 depConn->addSuccessor(backBeforeSplit);
     580              :                 depConn->setLength(fwdSplit->getLength());
     581        25010 :                 prevDep->removeSuccessor(backBeforeSplit);
     582        25010 :                 prevDep->addSuccessor(backSplit);
     583              :                 prevDep->setLength(backSplit->getLength());
     584        25010 :                 if (carSplit != nullptr) {
     585        25010 :                     depConn->addSuccessor(carSplit);
     586              :                 }
     587              : 
     588              :                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
     589        25010 :                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
     590        25010 :                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
     591        50020 :                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     592        25010 :                 fwdSplit->addSuccessor(arrConn);
     593        25010 :                 backBeforeSplit->addSuccessor(arrConn);
     594              :                 arrConn->setLength(fwdSplit->getLength());
     595        25010 :                 fwdSplit->removeSuccessor(prevArr);
     596        25010 :                 fwdBeforeSplit->addSuccessor(prevArr);
     597              :                 prevArr->setLength(backSplit->getLength());
     598        25010 :                 if (carSplit != nullptr) {
     599        25010 :                     if (carSplit->removeSuccessor(prevArr)) {
     600         2990 :                         carSplit->addSuccessor(arrConn);
     601         5980 :                         myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
     602              :                     } else {
     603              :                         // check for restricted access
     604        46573 :                         for (_IntermodalEdge* out : carSplit->getSuccessors()) {
     605        46573 :                             _AccessEdge* aOut = dynamic_cast<_AccessEdge*>(out);
     606        46573 :                             if (aOut != nullptr && aOut->removeSuccessor(prevArr)) {
     607        22020 :                                 aOut->addSuccessor(arrConn);
     608        22020 :                                 addRestrictedCarExit(
     609        22020 :                                         myAccessSplits[myCarLookup[stopEdge]][splitIndex],
     610              :                                         prevArr,
     611              :                                         aOut->getVehicleRetriction());
     612              :                                 break;
     613              :                             }
     614              :                         }
     615              :                     }
     616              :                 }
     617        25010 :                 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         3602 :             std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
     623              :             assert(splitList.size() > 0);
     624              :             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
     625         4238 :             while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
     626              :                 ++splitIt;
     627              :             }
     628         3602 :             splitList.insert(splitIt, stopConn);
     629              : 
     630         3602 :             if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
     631          621 :                 _IntermodalEdge* carEdge =  myCarLookup[stopEdge];
     632              :                 double relPos;
     633              :                 bool needSplit;
     634          621 :                 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
     635          621 :                 if (needSplit) {
     636          621 :                     _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     637          621 :                     splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
     638              : 
     639          621 :                     if (transferCarWalk || transferTaxiWalk) {
     640              :                         // adding access from car to walk
     641          621 :                         _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     642          621 :                         if (transferCarWalk) {
     643          524 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     644          524 :                             addEdge(access);
     645          524 :                             beforeSplit->addSuccessor(access);
     646          524 :                             access->addSuccessor(stopConn);
     647           97 :                         } else if (transferTaxiWalk) {
     648           97 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     649              :                         }
     650              :                     }
     651          621 :                     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        28735 :     }
     661              : 
     662         1557 :     void addSchedule(const SUMOVehicleParameter& pars, const StopParVector* addStops = nullptr) {
     663              :         SUMOTime lastUntil = 0;
     664              :         StopParVector validStops;
     665         1557 :         if (addStops != nullptr) {
     666              :             // stops are part of a stand-alone route. until times are offsets from vehicle departure
     667         4865 :             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
     668         3391 :                 if (myStopConnections.count(stop.busstop) > 0) {
     669              :                     // compute stop times for the first vehicle
     670         3389 :                     const SUMOTime newUntil = stop.until + pars.depart;
     671         3389 :                     if (newUntil >= lastUntil) {
     672         3374 :                         validStops.push_back(stop);
     673         3374 :                         validStops.back().until = newUntil;
     674              :                         lastUntil = newUntil;
     675              :                     } else {
     676           60 :                         WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     677              :                     }
     678              :                 }
     679              :             }
     680              :         }
     681         3315 :         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
     682              :             // stops are part of the vehicle until times are absolute times for the first vehicle
     683         1758 :             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
     684         1603 :                 validStops.push_back(stop);
     685         1603 :                 lastUntil = stop.until;
     686              :             } else {
     687          155 :                 if (stop.busstop != "" && stop.until >= 0) {
     688            0 :                     WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     689              :                 }
     690              :             }
     691              :         }
     692         1557 :         if (validStops.size() < 2 && pars.line != "taxi") {
     693          204 :             WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
     694           68 :             return;
     695              :         }
     696              : 
     697         1489 :         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
     698         1489 :         if (lineEdges.empty()) {
     699              :             _IntermodalEdge* lastStop = nullptr;
     700              :             Position lastPos;
     701              :             SUMOTime lastTime = 0;
     702         6214 :             for (const SUMOVehicleParameter::Stop& s : validStops) {
     703         4780 :                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
     704         4780 :                 Position stopPos = E::getStopPosition(s);
     705         4780 :                 if (lastStop != nullptr) {
     706         6774 :                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
     707         3387 :                     addEdge(newEdge);
     708        10161 :                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
     709         3387 :                     lastStop->addSuccessor(newEdge);
     710         3387 :                     newEdge->addSuccessor(currStop);
     711         3387 :                     lineEdges.push_back(newEdge);
     712              :                 }
     713         4780 :                 lastTime = s.until;
     714              :                 lastStop = currStop;
     715         4780 :                 lastPos = stopPos;
     716              :             }
     717         1434 :             if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
     718              :                 myLoopedLines.insert(pars.line);
     719              :             }
     720              :         } else {
     721           55 :             if (validStops.size() != lineEdges.size() + 1) {
     722            0 :                 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
     723            0 :                 return;
     724              :             }
     725           55 :             if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
     726           36 :                 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     727           12 :                 return;
     728              :             }
     729              :             typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
     730              :             typename StopParVector::const_iterator s = validStops.begin() + 1;
     731          143 :             for (; s != validStops.end(); ++s, ++lineEdge) {
     732          100 :                 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
     733            0 :                     WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     734            0 :                     return;
     735              :                 }
     736              :             }
     737           43 :             SUMOTime lastTime = validStops.front().until;
     738           86 :             if (lineEdges.front()->hasSchedule(lastTime)) {
     739           24 :                 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
     740              :             }
     741          143 :             for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
     742          200 :                 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
     743          100 :                 lastTime = s->until;
     744              :             }
     745              :         }
     746         1557 :     }
     747              : 
     748              :     /** @brief Adds access edges for transfering from walking to vehicle use
     749              :     * @param[in] edge The edge on which the transfer takes place
     750              :     * @param[in] svc The permitted vehicle class for transfering
     751              :     */
     752        43677 :     void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
     753              :         assert(edge != nullptr);
     754              :         assert(myCarLookup.count(edge) != 0);
     755              :         assert(myBidiLookup.count(edge) != 0);
     756        43677 :         EdgePair pedestrianEdges = myBidiLookup[edge];
     757        43677 :         _IntermodalEdge* carEdge = myCarLookup[edge];
     758        43677 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
     759        43677 :         addEdge(access);
     760        43677 :         pedestrianEdges.first->addSuccessor(access);
     761        43677 :         pedestrianEdges.second->addSuccessor(access);
     762        43677 :         access->addSuccessor(carEdge);
     763        43677 :     }
     764              : 
     765              :     /** @brief Adds access edges for transfering from driving to walking that are only usable by a particular vehicle class
     766              :     * @param[in] from The origin edge of the transfer
     767              :     * @param[in] to The destination edge of the transfer
     768              :     * @param[in] svc The permitted vehicle class for transfering
     769              :     */
     770       315973 :     void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
     771       315973 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
     772       315973 :         addEdge(access);
     773       315973 :         from->addSuccessor(access);
     774       315973 :         access->addSuccessor(to);
     775       315973 :     }
     776              : 
     777              :     bool isLooped(const std::string lineID) const {
     778              :         return myLoopedLines.count(lineID) != 0;
     779              :     }
     780              : 
     781              : private:
     782              :     /** @brief Returns where to insert or use the split edge
     783              :     *
     784              :     * This method determines whether an edge needs to be split at the given position
     785              :     *  (if there is not already a split nearby) and returns the corresponding index in the split list.
     786              :     *
     787              :     * @param[in] toSplit The first edge in the split list
     788              :     * @param[in] pos The relative position on the edge where the stop is located
     789              :     * @param[out] relPos The relative position on the splitted edge
     790              :     * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
     791              :     * @return the index in the split list where the split edge needs to be added or reused
     792              :     */
     793        25754 :     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
     794        25754 :         relPos = pos;
     795        25754 :         needSplit = true;
     796              :         int splitIndex = 0;
     797              :         const auto& splitList = myAccessSplits.find(toSplit);
     798        25754 :         if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
     799        27818 :             for (const _IntermodalEdge* const split : splitList->second) {
     800        27818 :                 if (relPos < split->getLength() + POSITION_EPS) {
     801              :                     break;
     802              :                 }
     803        13704 :                 relPos -= split->getLength();
     804        13704 :                 splitIndex++;
     805              :             }
     806              :             assert(splitIndex < (int)splitList->second.size());
     807        14114 :             if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
     808          123 :                 needSplit = false;
     809              :             }
     810              :         }
     811        25754 :         return splitIndex;
     812              :     }
     813              : 
     814              :     /** @brief Splits an edge (if necessary) and connects it to a stopping edge
     815              :     *
     816              :     * This method determines whether an edge needs to be split at the given position
     817              :     *  (if there is not already a split nearby) and connects the stop edge via new access edges.
     818              :     *
     819              :     * @param[in] toSplit The first edge in the split list
     820              :     * @param[in] afterSplit The edge to add if a split is performed
     821              :     * @param[in] pos The relative position on the edge where the stop is located
     822              :     * @param[in] stopConn The stop edge to connect to
     823              :     * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
     824              :     * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
     825              :     */
     826        76020 :     void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
     827              :                    _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
     828              :                    _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
     829        76020 :         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
     830        76020 :         if (splitList.empty()) {
     831        33878 :             splitList.push_back(toSplit);
     832              :         }
     833        76020 :         if (!forward) {
     834        25133 :             splitIndex = (int)splitList.size() - 1 - splitIndex;
     835        25133 :             if (!needSplit) {
     836          123 :                 splitIndex--;
     837              :             }
     838              :         }
     839        76020 :         _IntermodalEdge* beforeSplit = splitList[splitIndex];
     840        76020 :         if (needSplit) {
     841        75651 :             addEdge(afterSplit);
     842        75651 :             beforeSplit->transferSuccessors(afterSplit);
     843        75651 :             beforeSplit->addSuccessor(afterSplit);
     844        75651 :             if (forward) {
     845        50641 :                 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     846              :                 beforeSplit->setLength(relPos);
     847              :             } else {
     848              :                 afterSplit->setLength(relPos);
     849        25010 :                 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     850              :                 // rename backward edges for easier referencing
     851              :                 const std::string newID = beforeSplit->getID();
     852        25010 :                 beforeSplit->setID(afterSplit->getID());
     853        25010 :                 afterSplit->setID(newID);
     854              :             }
     855        75651 :             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
     856              :         } else {
     857              :             // don't split, use the present split edges
     858          369 :             afterSplit = splitList[splitIndex + 1];
     859              :         }
     860              :         // add access to / from edge
     861        76020 :         if (addEntry) {
     862        55214 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     863        55214 :             addEdge(access);
     864        55214 :             beforeSplit->addSuccessor(access);
     865        55214 :             access->addSuccessor(stopConn);
     866              :         }
     867        76020 :         if (addExit) {
     868              :             // pedestrian case only, exit from public to pedestrian
     869        50266 :             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
     870        50266 :             addEdge(exit);
     871        50266 :             stopConn->addSuccessor(exit);
     872        50266 :             exit->addSuccessor(afterSplit);
     873              :         }
     874        76020 :     }
     875              : 
     876              : 
     877              : private:
     878              :     /// @brief the edge dictionary
     879              :     std::vector<_IntermodalEdge*> myEdges;
     880              : 
     881              :     /// @brief retrieve the forward and backward edge for the given input edge E
     882              :     std::map<const E*, EdgePair> myBidiLookup;
     883              : 
     884              :     /// @brief retrieve the depart edges for the given input edge E
     885              :     std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
     886              : 
     887              :     /// @brief retrieve the arrival edges for the given input edge E
     888              :     std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
     889              : 
     890              :     /// @brief the walking connector edge (fake walking area)
     891              :     std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
     892              : 
     893              :     /// @brief retrieve the car edge for the given input edge E
     894              :     std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
     895              : 
     896              :     /// @brief retrieve the public transport edges for the given line
     897              :     std::map<std::string, std::vector<_PTEdge*> > myPTLines;
     898              : 
     899              :     /// @brief retrieve the representing edge for the given stopping place
     900              :     std::map<std::string, _IntermodalEdge*> myStopConnections;
     901              : 
     902              :     /// @brief retrieve the splitted edges for the given "original"
     903              :     std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
     904              : 
     905              :     /// @brief looped lines need extra checking when building itineraries
     906              :     std::set<std::string > myLoopedLines;
     907              : 
     908              :     int myNumericalID;
     909              :     const int myCarWalkTransfer;
     910              : 
     911              : private:
     912              :     /// @brief Invalidated assignment operator
     913              :     IntermodalNetwork& operator=(const IntermodalNetwork& s);
     914              : 
     915              : };
        

Generated by: LCOV version 2.0-1