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: 2024-10-24 15:46:30 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-2024 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        13415 :     IntermodalNetwork(const std::vector<E*>& edges, const bool pedestrianOnly, const int carWalkTransfer = 0)
      90        13415 :         : 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       870482 :         for (const E* const edge : edges) {
      97       857067 :             if (edge->isTazConnector()) {
      98              :                 // only a single edge
      99       104350 :                 _AccessEdge* access = new _AccessEdge(myNumericalID++, edge->getID(), edge);
     100       104350 :                 addEdge(access);
     101       104350 :                 myDepartLookup[edge].push_back(access);
     102       104350 :                 myArrivalLookup[edge].push_back(access);
     103              :             } else {
     104       386602 :                 const L* lane = getSidewalk<E, L>(edge);
     105       752717 :                 if (lane != 0) {
     106       566322 :                     if (edge->isWalkingArea()) {
     107              :                         // only a single edge
     108        77409 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     109        77409 :                         myBidiLookup[edge] = std::make_pair(myEdges.back(), myEdges.back());
     110        77409 :                         myDepartLookup[edge].push_back(myEdges.back());
     111        77409 :                         myArrivalLookup[edge].push_back(myEdges.back());
     112              :                         haveSeenWalkingArea = true;
     113              :                     } else { // regular edge or crossing
     114              :                         // forward and backward edges
     115       488913 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, true));
     116       488913 :                         addEdge(new _PedestrianEdge(myNumericalID++, edge, lane, false));
     117       488913 :                         myBidiLookup[edge] = std::make_pair(myEdges[myNumericalID - 2], myEdges.back());
     118              :                     }
     119              :                 }
     120       752717 :                 if (!edge->isWalkingArea()) {
     121              :                     // depart and arrival edges (the router can decide the initial direction to take and the direction to arrive from)
     122      1350616 :                     _IntermodalEdge* const departConn = new _IntermodalEdge(edge->getID() + "_depart_connector", myNumericalID++, edge, "!connector");
     123      1350616 :                     _IntermodalEdge* const arrivalConn = new _IntermodalEdge(edge->getID() + "_arrival_connector", myNumericalID++, edge, "!connector");
     124       675308 :                     addConnectors(departConn, arrivalConn, 0);
     125              :                 }
     126              :             }
     127              :         }
     128              : 
     129              :         // build the walking connectors if there are no walking areas
     130       870482 :         for (const E* const edge : edges) {
     131       857067 :             if (edge->isTazConnector() || edge->isInternal()) {
     132       534755 :                 continue;
     133              :             }
     134       322312 :             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       212519 :                 if (!pedestrianOnly && getSidewalk<E, L>(edge) == nullptr) {
     137         5960 :                     const N* const node = edge->getToJunction();
     138              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     139        10174 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     140         5087 :                         myWalkingConnectorLookup[node] = myEdges.back();
     141              :                     }
     142              :                 }
     143              :             } else {
     144       361431 :                 for (const N* const node : {
     145              :                             edge->getFromJunction(), edge->getToJunction()
     146              :                         }) {
     147              :                     if (myWalkingConnectorLookup.count(node) == 0) {
     148       157954 :                         addEdge(new _IntermodalEdge(node->getID() + "_walking_connector", myNumericalID++, nullptr, "!connector"));
     149        78977 :                         myWalkingConnectorLookup[node] = myEdges.back();
     150              :                     }
     151              :                 }
     152              :             }
     153              :         }
     154              :         // build the connections
     155       870482 :         for (const E* const edge : edges) {
     156       857067 :             if (edge->isTazConnector()) {
     157              :                 // since pedestrians walk in both directions, also allow departing at sinks and arriving at sources
     158       104350 :                 _IntermodalEdge* const tazDepart = getDepartConnector(edge);
     159              :                 _IntermodalEdge* const tazArrive = getArrivalConnector(edge);
     160              :                 const E* other = edge->getOtherTazConnector();
     161       104350 :                 _IntermodalEdge* const otherTazDepart = other != nullptr ? getDepartConnector(other) : tazDepart;
     162              :                 _IntermodalEdge* const otherTazArrive = other != nullptr ? getArrivalConnector(other) : tazArrive;
     163       221790 :                 for (const E* out : edge->getSuccessors()) {
     164       117440 :                     if (out->isNormal()) {
     165       152626 :                         tazDepart->addSuccessor(getDepartConnector(out));
     166        76313 :                         getArrivalConnector(out)->addSuccessor(otherTazArrive);
     167              :                     }
     168              :                 }
     169       221808 :                 for (const E* in : edge->getPredecessors()) {
     170       117458 :                     if (in->isNormal()) {
     171        76331 :                         getArrivalConnector(in)->addSuccessor(tazArrive);
     172       152662 :                         otherTazDepart->addSuccessor(getDepartConnector(in));
     173              :                     }
     174              :                 }
     175       104350 :                 continue;
     176       104350 :             }
     177       386602 :             const L* const sidewalk = getSidewalk<E, L>(edge);
     178       752717 :             if (sidewalk == nullptr) {
     179       186395 :                 continue;
     180              :             }
     181              :             // find all incoming and outgoing lanes for the sidewalk and
     182              :             // connect the corresponding IntermodalEdges
     183       566322 :             const EdgePair& pair = getBothDirections(edge);
     184              : #ifdef IntermodalRouter_DEBUG_NETWORK
     185              :             std::cout << "  building connections from " << sidewalk->getID() << "\n";
     186              : #endif
     187       566322 :             if (haveSeenWalkingArea) {
     188       213565 :                 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       366539 :                 for (const auto& target : outgoing) {
     194       255536 :                     if (target.first->getEdge().isWalkingArea()) {
     195              :                         hasWalkingArea = true;
     196              :                         break;
     197              :                     }
     198              :                 }
     199       469101 :                 for (const auto& target : outgoing) {
     200       255536 :                     const E* const targetEdge = &(target.first->getEdge());
     201       150424 :                     const bool used = (target.first == getSidewalk<E, L>(targetEdge)
     202       255536 :                                        && (!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       226545 :                         const EdgePair& targetPair = getBothDirections(targetEdge);
     209       226545 :                         pair.first->addSuccessor(targetPair.first);
     210       453090 :                         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       213565 :             }
     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       566322 :             _IntermodalEdge* const toNodeConn = myWalkingConnectorLookup[edge->getToJunction()];
     222       566322 :             if (toNodeConn != nullptr) {
     223              :                 // Check for the outgoing vias and use the shortest one as an approximation
     224       354637 :                 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       796145 :                 for (const auto& target : outgoing) {
     228       441508 :                     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       354637 :                 if (minVia != nullptr) {
     235              :                     const auto it = myBidiLookup.find(minVia);
     236       116807 :                     if (it != myBidiLookup.end()) {
     237              :                         interVia = it->second;
     238              :                     }
     239              :                 }
     240       354637 :                 if (!haveSeenWalkingArea) {
     241              :                     // if we have walking areas we should use them and not the connector
     242       705514 :                     pair.first->addSuccessor(toNodeConn, interVia.first);
     243              :                 }
     244       354637 :                 toNodeConn->addSuccessor(pair.second, interVia.second);
     245       354637 :             }
     246       566322 :             _IntermodalEdge* const fromNodeConn = myWalkingConnectorLookup[edge->getFromJunction()];
     247       566322 :             if (fromNodeConn != nullptr) {
     248       354674 :                 if (!haveSeenWalkingArea) {
     249       705514 :                     pair.second->addSuccessor(fromNodeConn);
     250              :                 }
     251       709348 :                 fromNodeConn->addSuccessor(pair.first);
     252              :             }
     253       566322 :             if (!edge->isWalkingArea()) {
     254              :                 // build connections from depart connector
     255       488913 :                 _IntermodalEdge* startConnector = getDepartConnector(edge);
     256       488913 :                 startConnector->addSuccessor(pair.first);
     257       977826 :                 startConnector->addSuccessor(pair.second);
     258              :                 // build connections to arrival connector
     259              :                 _IntermodalEdge* endConnector = getArrivalConnector(edge);
     260       977826 :                 pair.first->addSuccessor(endConnector);
     261       977826 :                 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        13415 :     }
     271              : 
     272        13414 :     ~IntermodalNetwork() {
     273      3328660 :         for (typename std::vector<_IntermodalEdge*>::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
     274      3315246 :             delete *it;
     275              :         }
     276        13414 :     }
     277              : 
     278      3315338 :     void addEdge(_IntermodalEdge* edge) {
     279      6630676 :         while ((int)myEdges.size() <= edge->getNumericalID()) {
     280      3315338 :             myEdges.push_back(0);
     281              :         }
     282      3315338 :         myEdges[edge->getNumericalID()] = edge;
     283      3315338 :     }
     284              : 
     285       698540 :     void addConnectors(_IntermodalEdge* const depConn, _IntermodalEdge* const arrConn, const int index) {
     286       698540 :         addEdge(depConn);
     287       698540 :         addEdge(arrConn);
     288       698540 :         myDepartLookup[depConn->getEdge()].insert(myDepartLookup[depConn->getEdge()].begin() + index, depConn);
     289       698540 :         myArrivalLookup[arrConn->getEdge()].insert(myArrivalLookup[arrConn->getEdge()].begin() + index, arrConn);
     290       698540 :     }
     291              : 
     292              :     const std::vector<_IntermodalEdge*>& getAllEdges() {
     293        15819 :         return myEdges;
     294              :     }
     295              : 
     296              :     /// @brief Returns the pair of forward and backward edge
     297      2475383 :     const EdgePair& getBothDirections(const E* e) const {
     298              :         typename std::map<const E*, EdgePair>::const_iterator it = myBidiLookup.find(e);
     299      2475383 :         if (it == myBidiLookup.end()) {
     300              :             assert(false);
     301            0 :             throw ProcessError(TLF("Edge '%' not found in intermodal network.'", e->getID()));
     302              :         }
     303      2475383 :         return (*it).second;
     304              :     }
     305              : 
     306              :     /// @brief Returns the departing intermodal edge
     307       165618 :     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       165618 :         if (it == myDepartLookup.end()) {
     310            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     311              :         }
     312       165618 :         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       168040 :             while (splitIt + 1 != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     333              :                 totalLength += (*splitIt)->getLength();
     334              :                 ++splitIt;
     335              :             }
     336       165603 :             return *splitIt;
     337              :         }
     338              :     }
     339              : 
     340              :     /// @brief Returns the departing intermodal connector at the given split offset
     341      1775306 :     _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      1775306 :         if (it == myDepartLookup.end()) {
     344            0 :             throw ProcessError(TLF("Depart edge '%' not found in intermodal network.", e->getID()));
     345              :         }
     346      1775306 :         if (splitIndex >= (int)it->second.size()) {
     347            0 :             throw ProcessError("Split index " + toString(splitIndex) + " invalid for depart edge '" + e->getID() + "' .");
     348              :         }
     349      1775306 :         return it->second[splitIndex];
     350              :     }
     351              : 
     352              :     /// @brief Returns the arriving intermodal edge
     353       154330 :     _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       154330 :         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       446429 :         while (splitIt != splitList.end() && totalLength + (*splitIt)->getLength() < pos) {
     362              :             totalLength += (*splitIt)->getLength();
     363              :             ++splitIt;
     364              :         }
     365       154330 :         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      1750686 :         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         5423 :     _IntermodalEdge* getWalkingConnector(const E* e) const {
     375              :         typename std::map<const N*, _IntermodalEdge*>::const_iterator it = myWalkingConnectorLookup.find(e->getToJunction());
     376         5423 :         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         5423 :         return it->second;
     389              :     }
     390              : 
     391         4718 :     void addCarEdges(const std::vector<E*>& edges, double taxiWait) {
     392       350236 :         for (const E* const edge : edges) {
     393       345518 :             if (edge->getFunction() == SumoXMLEdgeFunc::NORMAL || edge->getFunction() == SumoXMLEdgeFunc::INTERNAL) {
     394       259610 :                 myCarLookup[edge] = new CarEdge<E, L, N, V>(myNumericalID++, edge);
     395       259610 :                 addEdge(myCarLookup[edge]);
     396              :             }
     397              :         }
     398       264328 :         for (const auto& edgePair : myCarLookup) {
     399       259610 :             _IntermodalEdge* const carEdge = edgePair.second;
     400              :             // connectivity within the car network
     401       647580 :             for (const auto& suc : edgePair.first->getViaSuccessors()) {
     402       775940 :                 _IntermodalEdge* const sucCarEdge = getCarEdge(suc.first);
     403       775940 :                 _IntermodalEdge* const sucViaEdge = getCarEdge(suc.second);
     404       387970 :                 if (sucCarEdge != nullptr) {
     405       320419 :                     carEdge->addSuccessor(sucCarEdge, sucViaEdge);
     406              :                 }
     407              :             }
     408              :             // connectivity to the pedestrian network (only for normal edges)
     409       259610 :             if (edgePair.first->getFunction() != SumoXMLEdgeFunc::NORMAL) {
     410       176291 :                 continue;
     411              :             }
     412        83319 :             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        83319 :             if ((myCarWalkTransfer & ALL_JUNCTIONS) == 0 && (myCarWalkTransfer & TAXI_DROPOFF_ANYWHERE) != 0) {
     431              :                 // add access edges that allow exiting a taxi
     432         5128 :                 _IntermodalEdge* const walkCon = getWalkingConnector(edgePair.first);
     433         5128 :                 if (walkCon != 0) {
     434         2798 :                     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        44354 :                     for (const E* const out : edgePair.first->getToJunction()->getOutgoing()) {
     438        42636 :                         if (!out->isInternal() && !out->isTazConnector() && getSidewalk<E, L>(out) != 0) {
     439        14512 :                             addRestrictedCarExit(carEdge, getBothDirections(out).first, SVC_TAXI);
     440              :                         }
     441              :                     }
     442        44270 :                     for (const E* const in : edgePair.first->getToJunction()->getIncoming()) {
     443        42516 :                         if (!in->isInternal() && !in->isTazConnector() && getSidewalk<E, L>(in) != 0) {
     444        14428 :                             addRestrictedCarExit(carEdge, getBothDirections(in).second, SVC_TAXI);
     445              :                         }
     446              :                     }
     447              :                 }
     448              :             }
     449              :             // use intermediate access edge that prevents taxi departure
     450        83319 :             _IntermodalEdge* departConn = getDepartConnector(edgePair.first);
     451        83319 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, (SVCAll & ~SVC_TAXI));
     452        83319 :             addEdge(access);
     453        83319 :             departConn->addSuccessor(access);
     454        83319 :             access->addSuccessor(carEdge);
     455        83319 :             if ((myCarWalkTransfer & TAXI_PICKUP_PT) == 0) {
     456              :                 // taxi may depart anywhere but there is a time penalty
     457        81607 :                 _AccessEdge* taxiAccess = new _AccessEdge(myNumericalID++, departConn, carEdge, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     458        81607 :                 addEdge(taxiAccess);
     459        81607 :                 departConn->addSuccessor(taxiAccess);
     460        81607 :                 taxiAccess->addSuccessor(carEdge);
     461              :             }
     462        83319 :             if ((myCarWalkTransfer & TAXI_DROPOFF_PT) == 0) {
     463              :                 // taxi (as all other cars) may arrive anywhere
     464       163862 :                 carEdge->addSuccessor(getArrivalConnector(edgePair.first));
     465              :             } else {
     466              :                 // use intermediate access edge that prevents taxi arrival
     467         2776 :                 addRestrictedCarExit(carEdge, getArrivalConnector(edgePair.first), (SVCAll & ~SVC_TAXI));
     468              :             }
     469              :         }
     470         4718 :     }
     471              : 
     472              :     /// @brief Returns the associated car edge
     473              :     _IntermodalEdge* getCarEdge(const E* e) const {
     474       775940 :         if (e == nullptr) {
     475              :             return nullptr;
     476              :         }
     477              :         auto it = myCarLookup.find(e);
     478       540400 :         if (it == myCarLookup.end()) {
     479              :             return nullptr;
     480              :         }
     481       472849 :         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        49720 :         if (it == myStopConnections.end()) {
     488              :             return nullptr;
     489              :         }
     490        49720 :         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        26019 :     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        26019 :         const bool transferCarWalk = ((category == SUMO_TAG_PARKING_AREA && (myCarWalkTransfer & PARKING_AREAS) != 0) ||
     513        13364 :                                       (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & PT_STOPS) != 0));
     514        18254 :         const bool transferTaxiWalk = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_DROPOFF_PT) != 0);
     515        21129 :         const bool transferWalkTaxi = (category == SUMO_TAG_BUS_STOP && (myCarWalkTransfer & TAXI_PICKUP_PT) != 0);
     516        26019 :         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        47848 :             myStopConnections[stopId] = new StopEdge<E, L, N, V>(stopId, myNumericalID++, stopEdge, startPos, endPos);
     523        23924 :             addEdge(myStopConnections[stopId]);
     524              :         }
     525        26019 :         _IntermodalEdge* const stopConn = myStopConnections[stopId];
     526        26019 :         const L* lane = getSidewalk<E, L>(stopEdge);
     527        26019 :         if (lane != nullptr) {
     528        23408 :             const std::pair<_IntermodalEdge*, _IntermodalEdge*>& pair = getBothDirections(stopEdge);
     529              :             double relPos;
     530              :             bool needSplit;
     531        23408 :             const int splitIndex = findSplitIndex(pair.first, pos, relPos, needSplit);
     532        23408 :             _IntermodalEdge* const fwdSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, true, pos) : nullptr;
     533        23408 :             splitEdge(pair.first, splitIndex, fwdSplit, relPos, length, needSplit, stopConn);
     534        23408 :             _IntermodalEdge* const backSplit = needSplit ? new PedestrianEdge<E, L, N, V>(myNumericalID++, stopEdge, lane, false, pos) : nullptr;
     535        23408 :             splitEdge(pair.second, splitIndex, backSplit, relPos, length, needSplit, stopConn, false);
     536              :             _IntermodalEdge* carSplit = nullptr;
     537              :             if (myCarLookup.count(stopEdge) > 0) {
     538        23408 :                 if (needSplit) {
     539        23232 :                     carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     540              :                 }
     541        23408 :                 splitEdge(myCarLookup[stopEdge], splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, transferCarWalk);
     542              :             }
     543        23408 :             if (needSplit) {
     544        23232 :                 if (carSplit != nullptr && (transferCarWalk || transferTaxiWalk)) {
     545              :                     // adding access from car to walk
     546         5081 :                     _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     547        15243 :                     for (_IntermodalEdge* conn : {
     548              :                                 fwdSplit, backSplit
     549              :                             }) {
     550        10162 :                         if (transferCarWalk) {
     551         9738 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, conn, length);
     552         9738 :                             addEdge(access);
     553         9738 :                             beforeSplit->addSuccessor(access);
     554         9738 :                             access->addSuccessor(conn);
     555          424 :                         } else if (transferTaxiWalk) {
     556          424 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     557              :                         }
     558              :                     }
     559              :                 }
     560        23232 :                 if (carSplit != nullptr && transferWalkTaxi && !isAccess) {
     561          130 :                     _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     562          130 :                     addEdge(access);
     563          130 :                     stopConn->addSuccessor(access);
     564          130 :                     access->addSuccessor(carSplit);
     565              :                 }
     566              : 
     567              :                 // fixing depart connections for the forward pedestrian, the backward pedestrian and the car edge
     568        23232 :                 _IntermodalEdge* const prevDep = getDepartConnector(stopEdge, splitIndex);
     569        23232 :                 const std::vector<_IntermodalEdge*>& backSplitList = myAccessSplits[pair.second];
     570        23232 :                 _IntermodalEdge* const backBeforeSplit = backSplitList[backSplitList.size() - 2 - splitIndex];
     571        46464 :                 _IntermodalEdge* const depConn = new _IntermodalEdge(stopEdge->getID() + "_depart_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     572        23232 :                 depConn->addSuccessor(fwdSplit);
     573        23232 :                 depConn->addSuccessor(backBeforeSplit);
     574              :                 depConn->setLength(fwdSplit->getLength());
     575        23232 :                 prevDep->removeSuccessor(backBeforeSplit);
     576        23232 :                 prevDep->addSuccessor(backSplit);
     577              :                 prevDep->setLength(backSplit->getLength());
     578        23232 :                 if (carSplit != nullptr) {
     579        23232 :                     depConn->addSuccessor(carSplit);
     580              :                 }
     581              : 
     582              :                 // fixing arrival connections for the forward pedestrian, the backward pedestrian and the car edge
     583        23232 :                 _IntermodalEdge* const prevArr = getArrivalConnector(stopEdge, splitIndex);
     584        23232 :                 _IntermodalEdge* const fwdBeforeSplit = myAccessSplits[pair.first][splitIndex];
     585        46464 :                 _IntermodalEdge* const arrConn = new _IntermodalEdge(stopEdge->getID() + "_arrival_connector" + toString(pos), myNumericalID++, stopEdge, "!connector");
     586        23232 :                 fwdSplit->addSuccessor(arrConn);
     587        23232 :                 backBeforeSplit->addSuccessor(arrConn);
     588              :                 arrConn->setLength(fwdSplit->getLength());
     589        23232 :                 fwdSplit->removeSuccessor(prevArr);
     590        23232 :                 fwdBeforeSplit->addSuccessor(prevArr);
     591              :                 prevArr->setLength(backSplit->getLength());
     592        23232 :                 if (carSplit != nullptr) {
     593        23232 :                     if (carSplit->removeSuccessor(prevArr)) {
     594        23020 :                         carSplit->addSuccessor(arrConn);
     595        46040 :                         myAccessSplits[myCarLookup[stopEdge]][splitIndex]->addSuccessor(prevArr);
     596              :                     }
     597              :                 }
     598        23232 :                 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         2611 :             std::vector<_IntermodalEdge*>& splitList = myDepartLookup[stopEdge];
     604              :             assert(splitList.size() > 0);
     605              :             typename std::vector<_IntermodalEdge*>::iterator splitIt = splitList.begin();
     606         3182 :             while (splitIt != splitList.end() && startPos > (*splitIt)->getEndPos()) {
     607              :                 ++splitIt;
     608              :             }
     609         2611 :             splitList.insert(splitIt, stopConn);
     610              : 
     611         2611 :             if (!isAccess && (transferWalkTaxi || transferCarWalk || transferTaxiWalk)) {
     612          435 :                 _IntermodalEdge* carEdge =  myCarLookup[stopEdge];
     613              :                 double relPos;
     614              :                 bool needSplit;
     615          435 :                 const int splitIndex = findSplitIndex(carEdge, pos, relPos, needSplit);
     616          435 :                 if (needSplit) {
     617          435 :                     _IntermodalEdge* carSplit = new CarEdge<E, L, N, V>(myNumericalID++, stopEdge, pos);
     618          435 :                     splitEdge(carEdge, splitIndex, carSplit, relPos, length, needSplit, stopConn, true, false, false);
     619              : 
     620          435 :                     if (transferCarWalk || transferTaxiWalk) {
     621              :                         // adding access from car to walk
     622          435 :                         _IntermodalEdge* const beforeSplit = myAccessSplits[myCarLookup[stopEdge]][splitIndex];
     623          435 :                         if (transferCarWalk) {
     624          352 :                             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     625          352 :                             addEdge(access);
     626          352 :                             beforeSplit->addSuccessor(access);
     627          352 :                             access->addSuccessor(stopConn);
     628           83 :                         } else if (transferTaxiWalk) {
     629           83 :                             addRestrictedCarExit(beforeSplit, stopConn, SVC_TAXI);
     630              :                         }
     631              :                     }
     632          435 :                     if (transferWalkTaxi) {
     633           83 :                         _AccessEdge* access = new _AccessEdge(myNumericalID++, stopConn, carSplit, 0, SVC_TAXI, SVC_IGNORING, taxiWait);
     634           83 :                         addEdge(access);
     635           83 :                         stopConn->addSuccessor(access);
     636           83 :                         access->addSuccessor(carSplit);
     637              :                     }
     638              :                 }
     639              :             }
     640              :         }
     641        26019 :     }
     642              : 
     643         1200 :     void addSchedule(const SUMOVehicleParameter& pars, const std::vector<SUMOVehicleParameter::Stop>* addStops = nullptr) {
     644              :         SUMOTime lastUntil = 0;
     645              :         std::vector<SUMOVehicleParameter::Stop> validStops;
     646         1200 :         if (addStops != nullptr) {
     647              :             // stops are part of a stand-alone route. until times are offsets from vehicle departure
     648         2675 :             for (const SUMOVehicleParameter::Stop& stop : *addStops) {
     649         1558 :                 if (myStopConnections.count(stop.busstop) > 0) {
     650              :                     // compute stop times for the first vehicle
     651         1556 :                     const SUMOTime newUntil = stop.until + pars.depart;
     652         1556 :                     if (newUntil >= lastUntil) {
     653         1541 :                         validStops.push_back(stop);
     654         1541 :                         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         2876 :         for (const SUMOVehicleParameter::Stop& stop : pars.stops) {
     663              :             // stops are part of the vehicle until times are absolute times for the first vehicle
     664         1676 :             if (myStopConnections.count(stop.busstop) > 0 && stop.until >= lastUntil) {
     665         1523 :                 validStops.push_back(stop);
     666         1523 :                 lastUntil = stop.until;
     667              :             } else {
     668          153 :                 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         1200 :         if (validStops.size() < 2 && pars.line != "taxi") {
     674          135 :             WRITE_WARNINGF(TL("Not using public transport line '%' for routing persons. It has less than two usable stops."), pars.line);
     675           45 :             return;
     676              :         }
     677              : 
     678         1155 :         typename std::vector<_PTEdge*>& lineEdges = myPTLines[pars.line];
     679         1155 :         if (lineEdges.empty()) {
     680              :             _IntermodalEdge* lastStop = nullptr;
     681              :             Position lastPos;
     682              :             SUMOTime lastTime = 0;
     683         3990 :             for (const SUMOVehicleParameter::Stop& s : validStops) {
     684         2890 :                 _IntermodalEdge* currStop = myStopConnections[s.busstop];
     685         2890 :                 Position stopPos = E::getStopPosition(s);
     686         2890 :                 if (lastStop != nullptr) {
     687         3658 :                     _PTEdge* const newEdge = new _PTEdge(s.busstop, myNumericalID++, lastStop, currStop->getEdge(), pars.line, lastPos.distanceTo(stopPos));
     688         1829 :                     addEdge(newEdge);
     689         5487 :                     newEdge->addSchedule(pars.id, lastTime, pars.repetitionNumber, pars.repetitionOffset, s.until - lastTime);
     690         1829 :                     lastStop->addSuccessor(newEdge);
     691         1829 :                     newEdge->addSuccessor(currStop);
     692         1829 :                     lineEdges.push_back(newEdge);
     693              :                 }
     694         2890 :                 lastTime = s.until;
     695              :                 lastStop = currStop;
     696         2890 :                 lastPos = stopPos;
     697              :             }
     698         1100 :             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         1200 :     }
     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        11744 :     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        11744 :         EdgePair pedestrianEdges = myBidiLookup[edge];
     738        11744 :         _IntermodalEdge* carEdge = myCarLookup[edge];
     739        11744 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, pedestrianEdges.first, carEdge, 0, svc, SVC_IGNORING, traveltime);
     740        11744 :         addEdge(access);
     741        11744 :         pedestrianEdges.first->addSuccessor(access);
     742        11744 :         pedestrianEdges.second->addSuccessor(access);
     743        11744 :         access->addSuccessor(carEdge);
     744        11744 :     }
     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        33633 :     void addRestrictedCarExit(_IntermodalEdge* from, _IntermodalEdge* to, SVCPermissions vehicleRestriction) {
     752        33633 :         _AccessEdge* access = new _AccessEdge(myNumericalID++, from, to, 0, SVC_IGNORING, vehicleRestriction);
     753        33633 :         addEdge(access);
     754        33633 :         from->addSuccessor(access);
     755        33633 :         access->addSuccessor(to);
     756        33633 :     }
     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        23843 :     int findSplitIndex(_IntermodalEdge* const toSplit, const double pos, double& relPos, bool& needSplit) const {
     775        23843 :         relPos = pos;
     776        23843 :         needSplit = true;
     777              :         int splitIndex = 0;
     778              :         const auto& splitList = myAccessSplits.find(toSplit);
     779        23843 :         if (splitList != myAccessSplits.end() && !splitList->second.empty()) {
     780        27332 :             for (const _IntermodalEdge* const split : splitList->second) {
     781        27332 :                 if (relPos < split->getLength() + POSITION_EPS) {
     782              :                     break;
     783              :                 }
     784        13540 :                 relPos -= split->getLength();
     785        13540 :                 splitIndex++;
     786              :             }
     787              :             assert(splitIndex < (int)splitList->second.size());
     788        13792 :             if (splitIndex + 1 < (int)splitList->second.size() && fabs(relPos - splitList->second[splitIndex]->getLength()) < POSITION_EPS) {
     789          176 :                 needSplit = false;
     790              :             }
     791              :         }
     792        23843 :         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        70659 :     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        70659 :         std::vector<_IntermodalEdge*>& splitList = myAccessSplits[toSplit];
     811        70659 :         if (splitList.empty()) {
     812        29483 :             splitList.push_back(toSplit);
     813              :         }
     814        70659 :         if (!forward) {
     815        23408 :             splitIndex = (int)splitList.size() - 1 - splitIndex;
     816        23408 :             if (!needSplit) {
     817          176 :                 splitIndex--;
     818              :             }
     819              :         }
     820        70659 :         _IntermodalEdge* beforeSplit = splitList[splitIndex];
     821        70659 :         if (needSplit) {
     822        70131 :             addEdge(afterSplit);
     823        70131 :             beforeSplit->transferSuccessors(afterSplit);
     824        70131 :             beforeSplit->addSuccessor(afterSplit);
     825        70131 :             if (forward) {
     826        46899 :                 afterSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     827              :                 beforeSplit->setLength(relPos);
     828              :             } else {
     829              :                 afterSplit->setLength(relPos);
     830        23232 :                 beforeSplit->setLength(MAX2(0.0, beforeSplit->getLength() - relPos));
     831              :                 // rename backward edges for easier referencing
     832              :                 const std::string newID = beforeSplit->getID();
     833        23232 :                 beforeSplit->setID(afterSplit->getID());
     834        23232 :                 afterSplit->setID(newID);
     835              :             }
     836        70131 :             splitList.insert(splitList.begin() + splitIndex + 1, afterSplit);
     837              :         } else {
     838              :             // don't split, use the present split edges
     839          528 :             afterSplit = splitList[splitIndex + 1];
     840              :         }
     841              :         // add access to / from edge
     842        70659 :         if (addEntry) {
     843        51693 :             _AccessEdge* access = new _AccessEdge(myNumericalID++, beforeSplit, stopConn, length);
     844        51693 :             addEdge(access);
     845        51693 :             beforeSplit->addSuccessor(access);
     846        51693 :             access->addSuccessor(stopConn);
     847              :         }
     848        70659 :         if (addExit) {
     849              :             // pedestrian case only, exit from public to pedestrian
     850        46816 :             _AccessEdge* exit = new _AccessEdge(myNumericalID++, stopConn, afterSplit, length);
     851        46816 :             addEdge(exit);
     852        46816 :             stopConn->addSuccessor(exit);
     853        46816 :             exit->addSuccessor(afterSplit);
     854              :         }
     855        70659 :     }
     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