LCOV - code coverage report
Current view: top level - src/utils/router - IntermodalNetwork.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 95.8 % 380 364
Test Date: 2025-05-20 15:36:59 Functions: 100.0 % 32 32

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    IntermodalNetwork.h
      15              : /// @author  Jakob Erdmann
      16              : /// @author  Michael Behrisch
      17              : /// @author  Robert Hilbrich
      18              : /// @date    Mon, 03 March 2014
      19              : ///
      20              : // The Edge definition for the Intermodal Router
      21              : /****************************************************************************/
      22              : #pragma once
      23              : #include <config.h>
      24              : 
      25              : #include <string>
      26              : #include <vector>
      27              : #include <algorithm>
      28              : #include <assert.h>
      29              : #include <utils/common/MsgHandler.h>
      30              : #include <utils/common/Named.h>
      31              : #include <utils/common/SUMOTime.h>
      32              : #include <utils/common/ToString.h>
      33              : #include <utils/geom/Position.h>
      34              : #include <utils/vehicle/SUMOVehicleParameter.h>
      35              : #include "AccessEdge.h"
      36              : #include "CarEdge.h"
      37              : #include "IntermodalEdge.h"
      38              : #include "PedestrianEdge.h"
      39              : #include "PublicTransportEdge.h"
      40              : #include "StopEdge.h"
      41              : 
      42              : //#define IntermodalRouter_DEBUG_NETWORK
      43              : //#define IntermodalRouter_DEBUG_ACCESS
      44              : 
      45              : 
      46              : // ===========================================================================
      47              : // class definitions
      48              : // ===========================================================================
      49              : /** @brief where mode changes are possible
      50              : */
      51              : enum ModeChangeOptions {
      52              :     /// @brief parking areas
      53              :     PARKING_AREAS = 1,
      54              :     /// @brief public transport stops and access
      55              :     PT_STOPS = 2,
      56              :     /// @brief junctions with edges allowing the additional mode
      57              :     ALL_JUNCTIONS = 2 << 2,
      58              :     /// @brief taxi customer may exit at parking areas
      59              :     TAXI_DROPOFF_PARKING_AREAS = 2 << 3,
      60              :     /// @brief taxi customer may exit at public transport stops
      61              :     TAXI_DROPOFF_PT = 2 << 4,
      62              :     /// @brief taxi customer may exit anywhere
      63              :     TAXI_DROPOFF_ANYWHERE = 2 << 5,
      64              :     /// @brief taxi customer may be picked up at parking areas
      65              :     TAXI_PICKUP_PARKING_AREAS = 2 << 6,
      66              :     /// @brief taxi customer may be picked up at public transport stops
      67              :     TAXI_PICKUP_PT = 2 << 7,
      68              :     /// @brief taxi customer may be picked up anywhere
      69              :     TAXI_PICKUP_ANYWHERE = 2 << 8
      70              : };
      71              : 
      72              : 
      73              : 
      74              : /// @brief the intermodal network storing edges, connections and the mappings to the "real" edges
      75              : template<class E, class L, class N, class V>
      76              : class IntermodalNetwork {
      77              : private:
      78              :     typedef IntermodalEdge<E, L, N, V> _IntermodalEdge;
      79              :     typedef AccessEdge<E, L, N, V> _AccessEdge;
      80              :     typedef PedestrianEdge<E, L, N, V> _PedestrianEdge;
      81              :     typedef PublicTransportEdge<E, L, N, V> _PTEdge;
      82              :     typedef std::pair<_IntermodalEdge*, _IntermodalEdge*> EdgePair;
      83              : 
      84              : public:
      85              :     /* @brief build the pedestrian part of the intermodal network (once)
      86              :      * @param edges The list of MSEdge or ROEdge to build from
      87              :      * @param numericalID the start number for the creation of new edges
      88              :      */
      89        13808 :     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
      90        13808 :         : 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       958648 :         for (const E* const edge : edges) {
      97       944840 :             if (edge->isTazConnector()) {
      98              :                 // only a single edge
      99       108174 :                 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
     100       108174 :                 addEdge(access);
     101       108174 :                 myDepartLookup[edge].push_back(access);
     102       108174 :                 myArrivalLookup[edge].push_back(access);
     103              :             } else {
     104       404278 :                 const L* lane = getSidewalk<E, L>(edge);
     105       836666 :                 if (lane != 0) {
     106       622330 :                     if (edge->isWalkingArea()) {
     107              :                         // only a single edge
     108        88445 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     109        88445 :                         myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
     110        88445 :                         myDepartLookup[edge].push_back(myEdges.back());
     111        88445 :                         myArrivalLookup[edge].push_back(myEdges.back());
     112              :                         haveSeenWalkingArea = true;
     113              :                     } else { // regular edge or crossing
     114              :                         // forward and backward edges
     115       533885 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     116       533885 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
     117       533885 :                         myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
     118              :                     }
     119              :                 }
     120       836666 :                 if (!edge->isWalkingArea()) {
     121              :                     // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
     122      1496442 :                     _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
     123      1496442 :                     _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
     124       748221 :                     addConnectors(departConn, arrivalConn, 0);
     125              :                 }
     126              :             }
     127              :         }
     128              : 
     129              :         // build the walking connectors if there are no walking areas
     130       958648 :         for (const E* const edge : edges) {
     131       944840 :             if (edge->isTazConnector() || edge->isInternal()) {
     132       578958 :                 continue;
     133              :             }
     134       365882 :             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       247228 :                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
     137         6556 :                     const N* const node = edge->getToJunction();
     138              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     139        10872 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     140         5436 :                         myWalkingConnectorLookup[node] = myEdges.back();
     141              :                     }
     142              :                 }
     143              :             } else {
     144       388071 :                 for (const N* const node : {
     145              :                             edge->getFromJunction(), edge->getToJunction()
     146              :                         }) {
     147              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     148       167502 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     149        83751 :                         myWalkingConnectorLookup[node] = myEdges.back();
     150              :                     }
     151              :                 }
     152              :             }
     153              :         }
     154              :         // build the connections
     155       958648 :         for (const E* const edge : edges) {
     156       944840 :             if (edge->isTazConnector()) {
     157              :                 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
     158       108174 :                 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
     159              :                 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
     160              :                 const E* other = edge->getOtherTazConnector();
     161       108174 :                 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
     162              :                 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
     163       230022 :                 for (const E* out : edge->getSuccessors()) {
     164       121848 :                     if (out->isNormal()) {
     165       160446 :                         tazDepart->addSuccessor(getDepartConnector(out));
     166        80223 :                         getArrivalConnector(out)->addSuccessor(otherTazArrive);
     167              :                     }
     168              :                 }
     169       230041 :                 for (const E* in : edge->getPredecessors()) {
     170       121867 :                     if (in->isNormal()) {
     171        80242 :                         getArrivalConnector(in)->addSuccessor(tazArrive);
     172       160484 :                         otherTazDepart->addSuccessor(getDepartConnector(in));
     173              :                     }
     174              :                 }
     175       108174 :                 continue;
     176       108174 :             }
     177       404278 :             const L* const sidewalk = getSidewalk<E, L>(edge);
     178       836666 :             if (sidewalk == nullptr) {
     179       214336 :                 continue;
     180              :             }
     181              :             // find all incoming and outgoing lanes for the sidewalk and
     182              :             // connect the corresponding IntermodalEdges
     183       622330 :             const EdgePair& pair = getBothDirections(edge);
     184              : #ifdef IntermodalRouter_DEBUG_NETWORK
     185              :             std::cout << "  building connections from " << sidewalk->getID() << "\n";
     186              : #endif
     187       622330 :             if (haveSeenWalkingArea) {
     188       250919 :                 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       436612 :                 for (const auto& target : outgoing) {
     194       306046 :                     if (target.first->getEdge().isWalkingArea()) {
     195              :                         hasWalkingArea = true;
     196              :                         break;
     197              :                     }
     198              :                 }
     199       556965 :                 for (const auto& target : outgoing) {
     200       306046 :                     const E* const targetEdge = &(target.first->getEdge());
     201       154086 :                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
     202       306046 :                                        && (!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       269501 :                         const EdgePair& targetPair = getBothDirections(targetEdge);
     209       269501 :                         pair.first->addSuccessor(targetPair.first);
     210       539002 :                         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       250919 :             }
     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       622330 :             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
     222       622330 :             if (toNodeConn != nullptr) {
     223              :                 // Check for the outgoing vias and use the shortest one as an approximation
     224       373457 :                 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       841876 :                 for (const auto& target : outgoing) {
     228       468419 :                     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       373457 :                 if (minVia != nullptr) {
     235              :                     const auto it = myBidiLookup.find(minVia);
     236       120764 :                     if (it != myBidiLookup.end()) {
     237              :                         interVia = it->second;
     238              :                     }
     239              :                 }
     240       373457 :                 if (!haveSeenWalkingArea) {
     241              :                     // if we have walking areas we should use them and not the connector
     242       742822 :                     pair.first->addSuccessor(toNodeConn, interVia.first);
     243              :                 }
     244       373457 :                 toNodeConn->addSuccessor(pair.second, interVia.second);
     245       373457 :             }
     246       622330 :             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
     247       622330 :             if (fromNodeConn != nullptr) {
     248       373494 :                 if (!haveSeenWalkingArea) {
     249       742822 :                     pair.second->addSuccessor(fromNodeConn);
     250              :                 }
     251       746988 :                 fromNodeConn->addSuccessor(pair.first);
     252              :             }
     253       622330 :             if (!edge->isWalkingArea()) {
     254              :                 // build connections from depart connector
     255       533885 :                 _IntermodalEdge* startConnector = getDepartConnector(edge);
     256       533885 :                 startConnector->addSuccessor(pair.first);
     257      1067770 :                 startConnector->addSuccessor(pair.second);
     258              :                 // build connections to arrival connector
     259              :                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
     260      1067770 :                 pair.first->addSuccessor(endConnector);
     261      1067770 :                 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        13808 :     }
     271              : 
     272        13807 :     ~IntermodalNetwork() {
     273      3620039 :         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
     274      3606232 :             delete *it;
     275              :         }
     276        13807 :     }
     277              : 
     278      3606324 :     void addEdge(_IntermodalEdge* edge) {
     279      7212648 :         while ((int)myEdges.size() <= edge->getNumericalID()) {
     280      3606324 :             myEdges.push_back(0);
     281              :         }
     282      3606324 :         myEdges[edge->getNumericalID()] = edge;
     283      3606324 :     }
     284              : 
     285       771762 :     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
     286       771762 :         addEdge(depConn);
     287       771762 :         addEdge(arrConn);
     288       771762 :         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
     289       771762 :         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
     290       771762 :     }
     291              : 
     292              :     const std::vector<_IntermodalEdge*>& getAllEdges() {
     293        16327 :         return myEdges;
     294              :     }
     295              : 
     296              :     /// @brief Returns the pair of forward and backward edge
     297      2756804 :     const EdgePair& getBothDirections(const E* e) const {
     298              :         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
     299      2756804 :         if (it == myBidiLookup.end()) {
     300              :             assert(false);
     301            0 :             throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
     302              :         }
     303      2756804 :         return (*it).second;
     304              :     }
     305              : 
     306              :     /// @brief Returns the departing intermodal edge
     307       194638 :     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       194638 :         if (it == myDepartLookup.end()) {
     310            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     311              :         }
     312       194638 :         if ((e->getPermissions() & SVC_PEDESTRIAN) == 0) {
     313              :             // use most specific split (best trainStop, quay etc)
     314              :             double bestDist = std::numeric_limits<double>::max();
     315              :             const _IntermodalEdge* best = nullptr;
     316           42 :             for (const _IntermodalEdge* const split : it->second) {
     317           27 :                 if (pos >= split->getStartPos() - POSITION_EPS && pos <= split->getEndPos() + POSITION_EPS) {
     318           19 :                     const double dist = split->getEndPos() - split->getStartPos();
     319           19 :                     if (dist < bestDist) {
     320              :                         bestDist = dist;
     321              :                         best = split;
     322              :                     }
     323              :                 }
     324              :             }
     325              :             assert(best != nullptr);
     326           15 :             return best;
     327              :         } else {
     328              :             // use next downstream edge
     329              :             const std::vector<_IntermodalEdge*>& splitList = it->second;
     330              :             typename std::vector<_IntermodalEdge*>::const_iterator splitIt = splitList.begin();
     331              :             double totalLength = 0.;
     332       197073 :             while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     333              :                 totalLength += (*splitIt)->getLength();
     334              :                 ++splitIt;
     335              :             }
     336       194623 :             return *splitIt;
     337              :         }
     338              :     }
     339              : 
     340              :     /// @brief Returns the departing intermodal connector at the given split offset
     341      1931297 :     _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      1931297 :         if (it == myDepartLookup.end()) {
     344            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     345              :         }
     346      1931297 :         if (splitIndex >= (int)it->second.size()) {
     347            0 :             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
     348              :         }
     349      1931297 :         return it->second[splitIndex];
     350              :     }
     351              : 
     352              :     /// @brief Returns the arriving intermodal edge
     353       183332 :     _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       183332 :         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       529389 :         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     362              :             totalLength += (*splitIt)->getLength();
     363              :             ++splitIt;
     364              :         }
     365       183332 :         return *splitIt;
     366              :     }
     367              : 
     368              :     /// @brief Returns the arriving intermodal connector at the given split offset
     369              :     _IntermodalEdge* getArrivalConnector(const E* e, const int splitIndex = 0) const {
     370      1906312 :         return myArrivalLookup.find(e)->second[splitIndex];
     371              :     }
     372              : 
     373              :     /// @brief Returns the outgoing pedestrian edge, which is either a walking area or a walking connector
     374         6349 :     _IntermodalEdge* getWalkingConnector(const E* e) const {
     375              :         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
     376         6349 :         if (it == myWalkingConnectorLookup.end()) {
     377            0 :             const L* const sidewalk = getSidewalk<E, L>(e);
     378            0 :             if (e->isInternal() || sidewalk == 0) {
     379              :                 return 0;
     380              :             }
     381            0 :             for (const auto& target : sidewalk->getOutgoingViaLanes()) {
     382            0 :                 if (target.first->getEdge().isWalkingArea()) {
     383            0 :                     return getBothDirections(&target.first->getEdge()).first;
     384              :                 }
     385              :             }
     386            0 :             return 0;
     387              :         }
     388         6349 :         return it->second;
     389              :     }
     390              : 
     391         4901 :     void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
     392       367264 :         for (const E* const edge : edges) {
     393       362363 :             if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
     394       273653 :                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
     395       273653 :                 addEdge(myCarLookup[edge]);
     396              :             }
     397              :         }
     398       278554 :         for (const auto& edgePair : myCarLookup) {
     399       273653 :             _IntermodalEdge* const carEdge = edgePair.second;
     400              :             // connectivity within the car network
     401       683482 :             for (const auto& suc : edgePair.first->getViaSuccessors()) {
     402       819658 :                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
     403       819658 :                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
     404       409829 :                 if (sucCarEdge != nullptr) {
     405       339633 :                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
     406              :                 }
     407              :             }
     408              :             // connectivity to the pedestrian network (only for normal edges)
     409       273653 :             if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
     410       184473 :                 continue;
     411              :             }
     412        89180 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) != 0) {
     413          295 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     414          295 :                 if (walkCon != 0) {
     415          253 :                     carEdge->addSuccessor(walkCon);
     416              :                 } else {
     417              :                     // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
     418          162 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     419          120 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     420          156 :                             carEdge->addSuccessor(getBothDirections(out).first);
     421              :                         }
     422              :                     }
     423          174 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     424          132 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     425          180 :                             carEdge->addSuccessor(getBothDirections(in).second);
     426              :                         }
     427              :                     }
     428              :                 }
     429              :             }
     430        89180 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     431              :                 // add access edges that allow exiting a taxi
     432         6054 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     433         6054 :                 if (walkCon != 0) {
     434         3464 :                     addRestrictedCarExit(carEdge, walkCon, SVC_TAXI);
     435              :                 } else {
     436              :                     // we are on an edge where pedestrians are forbidden and want to continue on an arbitrary pedestrian edge
     437        49182 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     438        47204 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     439        16088 :                             addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
     440              :                         }
     441              :                     }
     442        49086 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     443        47072 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     444        15992 :                             addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
     445              :                         }
     446              :                     }
     447              :                 }
     448              :             }
     449              :             // use intermediate access edge that prevents taxi departure
     450        89180 :             _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
     451        89180 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
     452        89180 :             addEdge(access);
     453        89180 :             departConn->addSuccessor(access);
     454        89180 :             access->addSuccessor(carEdge);
     455        89180 :             if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
     456              :                 // taxi may depart anywhere but there is a time penalty
     457        87034 :                 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     458        87034 :                 addEdge(taxiAccess);
     459        87034 :                 departConn->addSuccessor(taxiAccess);
     460        87034 :                 taxiAccess->addSuccessor(carEdge);
     461              :             }
     462        89180 :             if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
     463              :                 // taxi (as all other cars) may arrive anywhere
     464       175472 :                 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
     465              :             } else {
     466              :                 // use intermediate access edge that prevents taxi arrival
     467         2888 :                 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
     468              :             }
     469              :         }
     470         4901 :     }
     471              : 
     472              :     /// @brief Returns the associated car edge
     473              :     _IntermodalEdge* getCarEdge(const E* e) const {
     474       819658 :         if (e == nullptr) {
     475              :             return nullptr;
     476              :         }
     477              :         auto it = myCarLookup.find(e);
     478       568422 :         if (it == myCarLookup.end()) {
     479              :             return nullptr;
     480              :         }
     481       498226 :         return it->second;
     482              :     }
     483              : 
     484              :     /// @brief Returns the associated stop edge
     485              :     _IntermodalEdge* getStopEdge(const std::string& stopId) const {
     486              :         auto it = myStopConnections.find(stopId);
     487        49760 :         if (it == myStopConnections.end()) {
     488              :             return nullptr;
     489              :         }
     490        49760 :         return it->second;
     491              :     }
     492              : 
     493              :     /** @brief Adds access edges for stopping places to the intermodal network
     494              :     *
     495              :     * This method creates an intermodal stop edge to represent the stopping place
     496              :     *  (if not present yet) and determines the edges which need to be splitted (usually the forward
     497              :     *  and the backward pedestrian edges and the car edge) and calls splitEdge for the
     498              :     *  actual split and the connection of the stop edge with access edges. After that it adds and adapts
     499              :     *  the depart and arrival connectors to the new edge(s).
     500              :     *
     501              :     * @param[in] stopId The id of the stop to add
     502              :     * @param[in] stopEdge The edge on which the stop is located
     503              :     * @param[in] startPos The relative position on the edge where the stop starts
     504              :     * @param[in] endPos The relative position on the edge where the stop ends
     505              :     * @param[in] length The length of the access edge to build
     506              :     * @param[in] category The type of stop
     507              :     * @param[in] isAccess Whether an <access> element is being connected
     508              :     * @param[in] taxiWait Expected time to wait for a taxi
     509              :     */
     510        26681 :     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) {
     511              :         assert(stopEdge != nullptr);
     512        26681 :         const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
     513        13742 :                                       (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
     514        18709 :         const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
     515        21714 :         const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
     516        26681 :         const double pos = (startPos + endPos) / 2.;
     517              : #ifdef IntermodalRouter_DEBUG_ACCESS
     518              :         std::cout << "addAccess stopId=" << stopId << " stopEdge=" << stopEdge->getID() << " pos=" << pos << " length=" << length << " tag=" << toString(category)
     519              :                   << " access=" << isAccess << " tWait=" << taxiWait << "\n";
     520              : #endif
     521              :         if (myStopConnections.count(stopId) == 0) {
     522        49156 :             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
     523        24578 :             addEdge(myStopConnections[stopId]);
     524              :         }
     525        26681 :         _IntermodalEdge* const stopConn = myStopConnections[stopId];
     526        26681 :         const L* lane = getSidewalk<E, L>(stopEdge);
     527        26681 :         if (lane != nullptr) {
     528        23583 :             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
     529              :             double relPos;
     530              :             bool needSplit;
     531        23583 :             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
     532        23583 :             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
     533        23583 :             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
     534        23583 :             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
     535        23583 :             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
     536              :             _IntermodalEdge* carSplit = nullptr;
     537              :             if (myCarLookup.count(stopEdge) > 0) {
     538        23583 :                 if (needSplit) {
     539        23541 :                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     540              :                 }
     541        23583 :                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
     542              :             }
     543        23583 :             if (needSplit) {
     544        23541 :                 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
     545              :                     // adding access from car to walk
     546         5042 :                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     547        15126 :                     for (_IntermodalEdge* conn : {
     548              :                                 fwdSplit, backSplit
     549              :                             }) {
     550        10084 :                         if (transferCarWalk) {
     551         9660 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
     552         9660 :                             addEdge(access);
     553         9660 :                             beforeSplit->addSuccessor(access);
     554         9660 :                             access->addSuccessor(conn);
     555          424 :                         } else if (transferTaxiWalk) {
     556          424 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     557              :                         }
     558              :                     }
     559              :                 }
     560        23541 :                 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
     561          144 :                     _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     562          144 :                     addEdge(access);
     563          144 :                     stopConn->addSuccessor(access);
     564          144 :                     access->addSuccessor(carSplit);
     565              :                 }
     566              : 
     567              :                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
     568        23541 :                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
     569        23541 :                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
     570        23541 :                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
     571        47082 :                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     572        23541 :                 depConn->addSuccessor(fwdSplit);
     573        23541 :                 depConn->addSuccessor(backBeforeSplit);
     574              :                 depConn->setLength(fwdSplit->getLength());
     575        23541 :                 prevDep->removeSuccessor(backBeforeSplit);
     576        23541 :                 prevDep->addSuccessor(backSplit);
     577              :                 prevDep->setLength(backSplit->getLength());
     578        23541 :                 if (carSplit != nullptr) {
     579        23541 :                     depConn->addSuccessor(carSplit);
     580              :                 }
     581              : 
     582              :                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
     583        23541 :                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
     584        23541 :                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
     585        47082 :                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     586        23541 :                 fwdSplit->addSuccessor(arrConn);
     587        23541 :                 backBeforeSplit->addSuccessor(arrConn);
     588              :                 arrConn->setLength(fwdSplit->getLength());
     589        23541 :                 fwdSplit->removeSuccessor(prevArr);
     590        23541 :                 fwdBeforeSplit->addSuccessor(prevArr);
     591              :                 prevArr->setLength(backSplit->getLength());
     592        23541 :                 if (carSplit != nullptr) {
     593        23541 :                     if (carSplit->removeSuccessor(prevArr)) {
     594        23329 :                         carSplit->addSuccessor(arrConn);
     595        46658 :                         myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
     596              :                     }
     597              :                 }
     598        23541 :                 addConnectors(depConn, arrConn, splitIndex + 1);
     599              :             }
     600              :         } else {
     601              :             // pedestrians cannot walk here:
     602              :             // add stop edge as depart connector so that pedestrians may start at the stop
     603         3098 :             std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
     604              :             assert(splitList.size() > 0);
     605              :             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
     606         3677 :             while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
     607              :                 ++splitIt;
     608              :             }
     609         3098 :             splitList.insert(splitIt, stopConn);
     610              : 
     611         3098 :             if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
     612          565 :                 _IntermodalEdge* carEdge =  myCarLookup[stopEdge];
     613              :                 double relPos;
     614              :                 bool needSplit;
     615          565 :                 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
     616          565 :                 if (needSplit) {
     617          565 :                     _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     618          565 :                     splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
     619              : 
     620          565 :                     if (transferCarWalk || transferTaxiWalk) {
     621              :                         // adding access from car to walk
     622          565 :                         _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     623          565 :                         if (transferCarWalk) {
     624          468 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     625          468 :                             addEdge(access);
     626          468 :                             beforeSplit->addSuccessor(access);
     627          468 :                             access->addSuccessor(stopConn);
     628           97 :                         } else if (transferTaxiWalk) {
     629           97 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     630              :                         }
     631              :                     }
     632          565 :                     if (transferWalkTaxi) {
     633           97 :                         _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     634           97 :                         addEdge(access);
     635           97 :                         stopConn->addSuccessor(access);
     636           97 :                         access->addSuccessor(carSplit);
     637              :                     }
     638              :                 }
     639              :             }
     640              :         }
     641        26681 :     }
     642              : 
     643         1256 :     void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
     644              :         SUMOTime lastUntil = 0;
     645              :         std::vector<SUMOVehicleParameter::Stop> validStops;
     646         1256 :         if (addStops != nullptr) {
     647              :             // stops are part of a stand-alone route. until times are offsets from vehicle departure
     648         2781 :             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
     649         1608 :                 if (myStopConnections.count(stop.busstop) > 0) {
     650              :                     // compute stop times for the first vehicle
     651         1606 :                     const SUMOTime newUntil = stop.until + pars.depart;
     652         1606 :                     if (newUntil >= lastUntil) {
     653         1591 :                         validStops.push_back(stop);
     654         1591 :                         validStops.back().until = newUntil;
     655              :                         lastUntil = newUntil;
     656              :                     } else {
     657           60 :                         WRITE_WARNINGF(TL("Ignoring unordered stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     658              :                     }
     659              :                 }
     660              :             }
     661              :         }
     662         3014 :         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
     663              :             // stops are part of the vehicle until times are absolute times for the first vehicle
     664         1758 :             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
     665         1603 :                 validStops.push_back(stop);
     666         1603 :                 lastUntil = stop.until;
     667              :             } else {
     668          155 :                 if (stop.busstop != "" && stop.until >= 0) {
     669            0 :                     WRITE_WARNINGF(TL("Ignoring stop at '%' until % for vehicle '%'."), stop.busstop, time2string(stop.until), pars.id);
     670              :                 }
     671              :             }
     672              :         }
     673         1256 :         if (validStops.size() < 2 && pars.line != "taxi") {
     674          141 :             WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
     675           47 :             return;
     676              :         }
     677              : 
     678         1209 :         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
     679         1209 :         if (lineEdges.empty()) {
     680              :             _IntermodalEdge* lastStop = nullptr;
     681              :             Position lastPos;
     682              :             SUMOTime lastTime = 0;
     683         4172 :             for (const SUMOVehicleParameter::Stop& s : validStops) {
     684         3018 :                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
     685         3018 :                 Position stopPos = E::getStopPosition(s);
     686         3018 :                 if (lastStop != nullptr) {
     687         3810 :                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
     688         1905 :                     addEdge(newEdge);
     689         5715 :                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
     690         1905 :                     lastStop->addSuccessor(newEdge);
     691         1905 :                     newEdge->addSuccessor(currStop);
     692         1905 :                     lineEdges.push_back(newEdge);
     693              :                 }
     694         3018 :                 lastTime = s.until;
     695              :                 lastStop = currStop;
     696         3018 :                 lastPos = stopPos;
     697              :             }
     698         1154 :             if (pars.line != "taxi" && validStops.front().busstop == validStops.back().busstop) {
     699              :                 myLoopedLines.insert(pars.line);
     700              :             }
     701              :         } else {
     702           55 :             if (validStops.size() != lineEdges.size() + 1) {
     703            0 :                 WRITE_WARNINGF("Number of stops for public transport line '%' does not match earlier definitions, ignoring schedule.", pars.line);
     704            0 :                 return;
     705              :             }
     706           55 :             if (lineEdges.front()->getEntryStop() != myStopConnections[validStops.front().busstop]) {
     707           36 :                 WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     708           12 :                 return;
     709              :             }
     710              :             typename std::vector<_PTEdge*>::const_iterator lineEdge = lineEdges.begin();
     711              :             typename std::vector<SUMOVehicleParameter::Stop>::const_iterator s = validStops.begin() + 1;
     712          143 :             for (; s != validStops.end(); ++s, ++lineEdge) {
     713          100 :                 if ((*lineEdge)->getSuccessors(SVC_IGNORING)[0] != myStopConnections[s->busstop]) {
     714            0 :                     WRITE_WARNINGF("Different stop for '%' compared to earlier definitions, ignoring schedule.", pars.line);
     715            0 :                     return;
     716              :                 }
     717              :             }
     718           43 :             SUMOTime lastTime = validStops.front().until;
     719           86 :             if (lineEdges.front()->hasSchedule(lastTime)) {
     720           24 :                 WRITE_WARNINGF("Duplicate schedule for '%' at time=%.", pars.line, time2string(lastTime));
     721              :             }
     722          143 :             for (lineEdge = lineEdges.begin(), s = validStops.begin() + 1; lineEdge != lineEdges.end(); ++lineEdge, ++s) {
     723          200 :                 (*lineEdge)->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s->until - lastTime);
     724          100 :                 lastTime = s->until;
     725              :             }
     726              :         }
     727         1256 :     }
     728              : 
     729              :     /** @brief Adds access edges for transfering from walking to vehicle use
     730              :     * @param[in] edge The edge on which the transfer takes place
     731              :     * @param[in] svc The permitted vehicle class for transfering
     732              :     */
     733        14638 :     void addCarAccess(const E* edge, SUMOVehicleClass svc, double traveltime) {
     734              :         assert(edge != nullptr);
     735              :         assert(myCarLookup.count(edge) != 0);
     736              :         assert(myBidiLookup.count(edge) != 0);
     737        14638 :         EdgePair pedestrianEdges = myBidiLookup[edge];
     738        14638 :         _IntermodalEdge* carEdge = myCarLookup[edge];
     739        14638 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
     740        14638 :         addEdge(access);
     741        14638 :         pedestrianEdges.first->addSuccessor(access);
     742        14638 :         pedestrianEdges.second->addSuccessor(access);
     743        14638 :         access->addSuccessor(carEdge);
     744        14638 :     }
     745              : 
     746              :     /** @brief Adds access edges for transfering from driving to walking that are only usable by a particular vehicle class
     747              :     * @param[in] from The origin edge of the transfer
     748              :     * @param[in] to The destination edge of the transfer
     749              :     * @param[in] svc The permitted vehicle class for transfering
     750              :     */
     751        37509 :     void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
     752        37509 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
     753        37509 :         addEdge(access);
     754        37509 :         from->addSuccessor(access);
     755        37509 :         access->addSuccessor(to);
     756        37509 :     }
     757              : 
     758              :     bool isLooped(const std::string lineID) const {
     759              :         return myLoopedLines.count(lineID) != 0;
     760              :     }
     761              : 
     762              : private:
     763              :     /** @brief Returns where to insert or use the split edge
     764              :     *
     765              :     * This method determines whether an edge needs to be split at the given position
     766              :     *  (if there is not already a split nearby) and returns the corresponding index in the split list.
     767              :     *
     768              :     * @param[in] toSplit The first edge in the split list
     769              :     * @param[in] pos The relative position on the edge where the stop is located
     770              :     * @param[out] relPos The relative position on the splitted edge
     771              :     * @param[out] needSplit whether a new split is needed or we reuse an exisiting one
     772              :     * @return the index in the split list where the split edge needs to be added or reused
     773              :     */
     774        24148 :     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
     775        24148 :         relPos = pos;
     776        24148 :         needSplit = true;
     777              :         int splitIndex = 0;
     778              :         const auto& splitList = myAccessSplits.find(toSplit);
     779        24148 :         if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
     780        27319 :             for (const _IntermodalEdge* const split : splitList->second) {
     781        27319 :                 if (relPos < split->getLength() + POSITION_EPS) {
     782              :                     break;
     783              :                 }
     784        13552 :                 relPos -= split->getLength();
     785        13552 :                 splitIndex++;
     786              :             }
     787              :             assert(splitIndex < (int)splitList->second.size());
     788        13767 :             if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
     789           42 :                 needSplit = false;
     790              :             }
     791              :         }
     792        24148 :         return splitIndex;
     793              :     }
     794              : 
     795              :     /** @brief Splits an edge (if necessary) and connects it to a stopping edge
     796              :     *
     797              :     * This method determines whether an edge needs to be split at the given position
     798              :     *  (if there is not already a split nearby) and connects the stop edge via new access edges.
     799              :     *
     800              :     * @param[in] toSplit The first edge in the split list
     801              :     * @param[in] afterSplit The edge to add if a split is performed
     802              :     * @param[in] pos The relative position on the edge where the stop is located
     803              :     * @param[in] stopConn The stop edge to connect to
     804              :     * @param[in] forward whether we are aplitting a forward edge (backward edges get different names)
     805              :     * @param[in] addExit whether we can just enter the stop or exit as well (cars should not exit yet)
     806              :     */
     807        71314 :     void splitEdge(_IntermodalEdge* const toSplit, int splitIndex,
     808              :                    _IntermodalEdge* afterSplit, const double relPos, const double length, const bool needSplit,
     809              :                    _IntermodalEdge* const stopConn, const bool forward = true, const bool addExit = true, const bool addEntry = true) {
     810        71314 :         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
     811        71314 :         if (splitList.empty()) {
     812        30213 :             splitList.push_back(toSplit);
     813              :         }
     814        71314 :         if (!forward) {
     815        23583 :             splitIndex = (int)splitList.size() - 1 - splitIndex;
     816        23583 :             if (!needSplit) {
     817           42 :                 splitIndex--;
     818              :             }
     819              :         }
     820        71314 :         _IntermodalEdge* beforeSplit = splitList[splitIndex];
     821        71314 :         if (needSplit) {
     822        71188 :             addEdge(afterSplit);
     823        71188 :             beforeSplit->transferSuccessors(afterSplit);
     824        71188 :             beforeSplit->addSuccessor(afterSplit);
     825        71188 :             if (forward) {
     826        47647 :                 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     827              :                 beforeSplit->setLength(relPos);
     828              :             } else {
     829              :                 afterSplit->setLength(relPos);
     830        23541 :                 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     831              :                 // rename backward edges for easier referencing
     832              :                 const std::string newID = beforeSplit->getID();
     833        23541 :                 beforeSplit->setID(afterSplit->getID());
     834        23541 :                 afterSplit->setID(newID);
     835              :             }
     836        71188 :             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
     837              :         } else {
     838              :             // don't split, use the present split edges
     839          126 :             afterSplit = splitList[splitIndex + 1];
     840              :         }
     841              :         // add access to / from edge
     842        71314 :         if (addEntry) {
     843        52004 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     844        52004 :             addEdge(access);
     845        52004 :             beforeSplit->addSuccessor(access);
     846        52004 :             access->addSuccessor(stopConn);
     847              :         }
     848        71314 :         if (addExit) {
     849              :             // pedestrian case only, exit from public to pedestrian
     850        47166 :             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
     851        47166 :             addEdge(exit);
     852        47166 :             stopConn->addSuccessor(exit);
     853        47166 :             exit->addSuccessor(afterSplit);
     854              :         }
     855        71314 :     }
     856              : 
     857              : 
     858              : private:
     859              :     /// @brief the edge dictionary
     860              :     std::vector<_IntermodalEdge*> myEdges;
     861              : 
     862              :     /// @brief retrieve the forward and backward edge for the given input edge E
     863              :     std::map<const E*, EdgePair> myBidiLookup;
     864              : 
     865              :     /// @brief retrieve the depart edges for the given input edge E
     866              :     std::map<const E*, std::vector<_IntermodalEdge*> > myDepartLookup;
     867              : 
     868              :     /// @brief retrieve the arrival edges for the given input edge E
     869              :     std::map<const E*, std::vector<_IntermodalEdge*> > myArrivalLookup;
     870              : 
     871              :     /// @brief the walking connector edge (fake walking area)
     872              :     std::map<const N*, _IntermodalEdge*> myWalkingConnectorLookup;
     873              : 
     874              :     /// @brief retrieve the car edge for the given input edge E
     875              :     std::map<const E*, _IntermodalEdge*, ComparatorNumericalIdLess> myCarLookup;
     876              : 
     877              :     /// @brief retrieve the public transport edges for the given line
     878              :     std::map<std::string, std::vector<_PTEdge*> > myPTLines;
     879              : 
     880              :     /// @brief retrieve the representing edge for the given stopping place
     881              :     std::map<std::string, _IntermodalEdge*> myStopConnections;
     882              : 
     883              :     /// @brief retrieve the splitted edges for the given "original"
     884              :     std::map<_IntermodalEdge*, std::vector<_IntermodalEdge*> > myAccessSplits;
     885              : 
     886              :     /// @brief looped lines need extra checking when building itineraries
     887              :     std::set<std::string > myLoopedLines;
     888              : 
     889              :     int myNumericalID;
     890              :     const int myCarWalkTransfer;
     891              : 
     892              : private:
     893              :     /// @brief Invalidated assignment operator
     894              :     IntermodalNetwork& operator=(const IntermodalNetwork& s);
     895              : 
     896              : };
        

Generated by: LCOV version 2.0-1