LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms_Ramps.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 224 250 89.6 %
Date: 2024-05-06 15:32:35 Functions: 11 13 84.6 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2012-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    NBAlgorithms_Ramps.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Jakob Erdmann
      17             : /// @author  Michael Behrisch
      18             : /// @date    29. March 2012
      19             : ///
      20             : // Algorithms for highway on-/off-ramps computation
      21             : /****************************************************************************/
      22             : #include <config.h>
      23             : 
      24             : #include <cassert>
      25             : #include <utils/options/OptionsCont.h>
      26             : #include <utils/common/MsgHandler.h>
      27             : #include <utils/common/ToString.h>
      28             : #include "NBNetBuilder.h"
      29             : #include "NBNodeCont.h"
      30             : #include "NBNode.h"
      31             : #include "NBEdge.h"
      32             : #include "NBAlgorithms_Ramps.h"
      33             : 
      34             : #define OFFRAMP_LOOKBACK 500
      35             : 
      36             : //#define DEBUG_RAMPS
      37             : #define DEBUGNODEID  ""
      38             : #define DEBUGCOND(obj) ((obj != 0 && (obj)->getID() == DEBUGNODEID))
      39             : 
      40             : // ===========================================================================
      41             : // static members
      42             : // ===========================================================================
      43             : const std::string NBRampsComputer::ADDED_ON_RAMP_EDGE("-AddedOnRampEdge");
      44             : 
      45             : // ===========================================================================
      46             : // method definitions
      47             : // ===========================================================================
      48             : // ---------------------------------------------------------------------------
      49             : // NBRampsComputer
      50             : // ---------------------------------------------------------------------------
      51             : void
      52        1746 : NBRampsComputer::computeRamps(NBNetBuilder& nb, OptionsCont& oc, bool mayAddOrRemove) {
      53        1746 :     const bool guessAndAdd = oc.getBool("ramps.guess") && mayAddOrRemove;
      54        1746 :     const double minHighwaySpeed = oc.getFloat("ramps.min-highway-speed");
      55        1746 :     const double maxRampSpeed = oc.getFloat("ramps.max-ramp-speed");
      56        1746 :     const double rampLength = oc.getFloat("ramps.ramp-length");
      57        1746 :     const double minWeaveLength = oc.getFloat("ramps.min-weave-length");
      58        3492 :     const bool dontSplit = oc.getBool("ramps.no-split");
      59             :     NBNodeCont& nc = nb.getNodeCont();
      60             :     NBEdgeCont& ec = nb.getEdgeCont();
      61             :     NBDistrictCont& dc = nb.getDistrictCont();
      62             :     std::set<NBEdge*> incremented;
      63             :     // collect join exclusions
      64             :     std::set<std::string> noramps;
      65        3492 :     if (oc.isSet("ramps.unset")) {
      66           2 :         std::vector<std::string> edges = oc.getStringVector("ramps.unset");
      67             :         noramps.insert(edges.begin(), edges.end());
      68           1 :     }
      69             :     // exclude roundabouts
      70        1835 :     for (const EdgeSet& round : ec.getRoundabouts()) {
      71         547 :         for (NBEdge* const edge : round) {
      72         458 :             noramps.insert(edge->getID());
      73             :         }
      74             :     }
      75             :     // exclude public transport edges
      76        1746 :     nb.getPTStopCont().addEdges2Keep(oc, noramps);
      77        1746 :     nb.getParkingCont().addEdges2Keep(oc, noramps);
      78             : 
      79             :     // check whether on-off ramps shall be guessed
      80        5126 :     if (guessAndAdd || oc.getBool("ramps.guess-acceleration-lanes")) {
      81      113950 :         for (const auto& it : ec) {
      82      112204 :             it.second->markOffRamp(false);
      83             :         }
      84             : 
      85             :         // if an edge is part of two ramps, ordering is important
      86             :         std::set<NBNode*, ComparatorIdLess> potOnRamps;
      87             :         std::set<NBNode*, ComparatorIdLess> potOffRamps;
      88       67880 :         for (const auto& i : nc) {
      89       66134 :             NBNode* cur = i.second;
      90             : #ifdef DEBUG_RAMPS
      91             :             if (DEBUGCOND(cur)) {
      92             :                 std::cout << "check ramps cur=" << cur->getID() << "\n";
      93             :             }
      94             : #endif
      95       66134 :             if (mayNeedOnRamp(cur, minHighwaySpeed, maxRampSpeed, noramps, minWeaveLength)) {
      96             :                 potOnRamps.insert(cur);
      97             :             }
      98       66134 :             if (mayNeedOffRamp(cur, minHighwaySpeed, maxRampSpeed, noramps)) {
      99             :                 potOffRamps.insert(cur);
     100             :             }
     101             :         }
     102        1825 :         for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOnRamps.begin(); i != potOnRamps.end(); ++i) {
     103          79 :             buildOnRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd);
     104             :         }
     105        1860 :         for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOffRamps.begin(); i != potOffRamps.end(); ++i) {
     106         114 :             buildOffRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd, potOnRamps);
     107             :         }
     108             :     }
     109             :     // check whether on-off ramps are specified
     110        3492 :     if (oc.isSet("ramps.set") && mayAddOrRemove) {
     111          16 :         std::vector<std::string> edges = oc.getStringVector("ramps.set");
     112             :         std::set<NBNode*, ComparatorIdLess> potOnRamps;
     113          18 :         for (const std::string& i : edges) {
     114          10 :             NBEdge* e = ec.retrieve(i);
     115           0 :             if (noramps.count(i) != 0) {
     116           0 :                 WRITE_WARNINGF(TL("Can not build ramp on edge '%' - the edge is unsuitable."), i);
     117           0 :                 continue;
     118             :             }
     119          10 :             if (e == nullptr) {
     120           0 :                 WRITE_WARNINGF(TL("Can not build on ramp on edge '%' - the edge is not known."), i);
     121           0 :                 continue;
     122             :             }
     123          10 :             NBNode* from = e->getFromNode();
     124          10 :             if (from->getIncomingEdges().size() == 2 && from->getOutgoingEdges().size() == 1) {
     125           6 :                 buildOnRamp(from, nc, ec, dc, rampLength, dontSplit, true);
     126             :                 potOnRamps.insert(from);
     127             :             }
     128             :             // load edge again to check offramps
     129          10 :             e = ec.retrieve(i);
     130          10 :             if (e == nullptr) {
     131           0 :                 WRITE_WARNINGF(TL("Can not build off ramp on edge '%' - the edge is not known."), i);
     132           0 :                 continue;
     133             :             }
     134             :             NBNode* to = e->getToNode();
     135          10 :             if (to->getIncomingEdges().size() == 1 && to->getOutgoingEdges().size() == 2) {
     136           3 :                 buildOffRamp(to, nc, ec, dc, rampLength, dontSplit, true, potOnRamps);
     137             :             }
     138             :         }
     139           8 :     }
     140        1746 : }
     141             : 
     142             : 
     143             : bool
     144       66134 : NBRampsComputer::mayNeedOnRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps, double minWeaveLength) {
     145       66134 :     if (cur->getOutgoingEdges().size() != 1 || cur->getIncomingEdges().size() != 2) {
     146             :         return false;
     147             :     }
     148             :     NBEdge* potHighway, *potRamp, *cont;
     149        4610 :     getOnRampEdges(cur, &potHighway, &potRamp, &cont);
     150             :     // may be an on-ramp
     151             : #ifdef DEBUG_RAMPS
     152             :     if (DEBUGCOND(cur)) {
     153             :         std::cout << "check on ramp hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " cont=" << cont->getID() << std::endl;
     154             :     }
     155             : #endif
     156        4610 :     if (fulfillsRampConstraints(potHighway, potRamp, cont, minHighwaySpeed, maxRampSpeed, noramps)) {
     157             :         // prevent short weaving section
     158          88 :         double seen = cont->getLength();
     159         102 :         while (seen < minWeaveLength) {
     160          29 :             if (cont->getToNode()->getOutgoingEdges().size() > 1) {
     161             :                 return false;
     162          20 :             } else if (cont->getToNode()->getOutgoingEdges().size() == 0) {
     163             :                 return true;
     164             :             }
     165          14 :             cont = cont->getToNode()->getOutgoingEdges().front();
     166          14 :             seen += cont->getLength();
     167             :         }
     168             :         return true;
     169             :     } else {
     170             :         return false;
     171             :     }
     172             : }
     173             : 
     174             : 
     175             : bool
     176       66134 : NBRampsComputer::mayNeedOffRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps) {
     177       66134 :     if (cur->getIncomingEdges().size() != 1 || cur->getOutgoingEdges().size() != 2) {
     178             :         return false;
     179             :     }
     180             :     // may be an off-ramp
     181             :     NBEdge* potHighway, *potRamp, *prev;
     182        6111 :     getOffRampEdges(cur, &potHighway, &potRamp, &prev);
     183             : #ifdef DEBUG_RAMPS
     184             :     if (DEBUGCOND(cur)) {
     185             :         std::cout << "check off ramp hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " prev=" << prev->getID() << std::endl;
     186             :     }
     187             : #endif
     188        6111 :     return fulfillsRampConstraints(potHighway, potRamp, prev, minHighwaySpeed, maxRampSpeed, noramps);
     189             : }
     190             : 
     191             : 
     192             : void
     193          85 : NBRampsComputer::buildOnRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes) {
     194             :     NBEdge* potHighway, *potRamp, *cont;
     195          85 :     getOnRampEdges(cur, &potHighway, &potRamp, &cont);
     196             : #ifdef DEBUG_RAMPS
     197             :     if (DEBUGCOND(cur)) {
     198             :         std::cout << "buildOnRamp cur=" << cur->getID() << " hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " cont=" << cont->getID() << "\n";
     199             :     }
     200             : #endif
     201             :     // compute the number of lanes to append
     202          85 :     const int firstLaneNumber = cont->getNumLanes();
     203          85 :     int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
     204             :     NBEdge* first = cont;
     205             :     NBEdge* last = cont;
     206          85 :     NBEdge* curr = cont;
     207             :     std::set<NBEdge*> incremented;
     208          85 :     if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), cont) == incremented.end()) {
     209             :         double currLength = 0;
     210          47 :         while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
     211          17 :             if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
     212          17 :                 curr->incLaneNo(toAdd);
     213          17 :                 if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     214          17 :                     curr->invalidateConnections(true);
     215             :                 }
     216             :                 incremented.insert(curr);
     217          17 :                 moveRampRight(curr, toAdd);
     218          17 :                 currLength += curr->getGeometry().length(); // !!! loaded length?
     219          17 :                 last = curr;
     220             :                 // mark acceleration lanes
     221          40 :                 for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
     222          23 :                     curr->setAcceleration(i, true);
     223             :                 }
     224             :             }
     225          17 :             NBNode* nextN = curr->getToNode();
     226          17 :             if (nextN->getOutgoingEdges().size() == 1 && nextN->getIncomingEdges().size() == 1) {
     227           8 :                 curr = nextN->getOutgoingEdges()[0];
     228           8 :                 if (curr->getNumLanes() != firstLaneNumber) {
     229             :                     // the number of lanes changes along the computation; we'll stop...
     230           1 :                     curr = nullptr;
     231           7 :                 } else if (curr->isTurningDirectionAt(last)) {
     232             :                     // turnarounds certainly should not be included in a ramp
     233           0 :                     curr = nullptr;
     234           7 :                 } else if (curr == potHighway || curr == potRamp) {
     235             :                     // circular connectivity. do not split!
     236           0 :                     curr = nullptr;
     237             :                 }
     238             :             } else {
     239             :                 // ambiguous; and, in fact, what should it be? ...stop
     240           9 :                 curr = nullptr;
     241             :             }
     242             :         }
     243             :         // check whether a further split is necessary
     244          50 :         if (curr != nullptr && !dontSplit && currLength - POSITION_EPS < rampLength && curr->getNumLanes() == firstLaneNumber && std::find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
     245             :             // there is enough place to build a ramp; do it
     246             :             bool wasFirst = first == curr;
     247          20 :             std::string newNodeID = getUnusedID(curr->getID() + "-AddedOnRampNode", nc);
     248          20 :             std::string newEdgeID = getUnusedID(curr->getID() + ADDED_ON_RAMP_EDGE, ec);
     249          20 :             NBNode* rn = new NBNode(newNodeID, curr->getGeometry().positionAtOffset(rampLength - currLength));
     250          20 :             nc.insert(rn);
     251          20 :             std::string name = curr->getID();
     252          20 :             ec.splitAt(dc, curr, rn, newEdgeID, curr->getID(), curr->getNumLanes() + toAdd, curr->getNumLanes());
     253             :             //ec.retrieve(name)->invalidateConnections();
     254          20 :             curr = ec.retrieve(newEdgeID);
     255             :             incremented.insert(curr);
     256          20 :             last = curr;
     257          20 :             moveRampRight(curr, toAdd);
     258          20 :             if (wasFirst) {
     259          19 :                 first = curr;
     260             :             }
     261             :             // mark acceleration lanes
     262          47 :             for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
     263          27 :                 curr->setAcceleration(i, true);
     264             :             }
     265             :         }
     266          30 :         if (curr == cont && dontSplit && addLanes) {
     267           0 :             WRITE_WARNING("Could not build on-ramp for edge '"  + curr->getID() + "' due to option '--ramps.no-split'");
     268             :             return;
     269             :         }
     270             :     } else {
     271             :         // mark acceleration lanes
     272          77 :         for (int i = 0; i < firstLaneNumber - potHighway->getNumLanes(); ++i) {
     273          22 :             cont->setAcceleration(i, true);
     274             :         }
     275             :     }
     276             :     // set connections from ramp/highway to added ramp
     277          85 :     if (addLanes) {
     278          34 :         if (potHighway->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     279          34 :             if (!potHighway->addLane2LaneConnections(0, first, potRamp->getNumLanes(), MIN2(first->getNumLanes() - potRamp->getNumLanes(), potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
     280           0 :                 throw ProcessError(TL("Could not set connection!"));
     281             :             }
     282             :         }
     283          34 :         if (potRamp->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     284          34 :             if (!potRamp->addLane2LaneConnections(0, first, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
     285           0 :                 throw ProcessError(TL("Could not set connection!"));
     286             :             }
     287             :         }
     288          34 :         patchRampGeometry(potRamp, first, potHighway, false);
     289             :     }
     290             : }
     291             : 
     292             : 
     293             : void
     294         117 : NBRampsComputer::buildOffRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes,
     295             :                               const std::set<NBNode*, ComparatorIdLess>& potOnRamps) {
     296             :     NBEdge* potHighway, *potRamp, *prev;
     297         117 :     getOffRampEdges(cur, &potHighway, &potRamp, &prev);
     298             : #ifdef DEBUG_RAMPS
     299             :     if (DEBUGCOND(cur)) {
     300             :         std::cout << "buildOffRamp cur=" << cur->getID() << " hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " prev=" << prev->getID() << "\n";
     301             :     }
     302             : #endif
     303             :     // compute the number of lanes to append
     304         117 :     const int firstLaneNumber = prev->getNumLanes();
     305         117 :     int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
     306             :     NBEdge* first = prev;
     307             :     NBEdge* last = prev;
     308         117 :     NBEdge* curr = prev;
     309             :     std::set<NBEdge*> incremented;
     310         117 :     if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), prev) == incremented.end()) {
     311             :         double currLength = 0;
     312          53 :         while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
     313          18 :             if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
     314          18 :                 curr->incLaneNo(toAdd);
     315          18 :                 if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     316          17 :                     curr->invalidateConnections(true);
     317             :                 }
     318             :                 incremented.insert(curr);
     319          18 :                 moveRampRight(curr, toAdd);
     320          18 :                 currLength += curr->getGeometry().length(); // !!! loaded length?
     321          18 :                 last = curr;
     322             :             }
     323          18 :             NBNode* prevN = curr->getFromNode();
     324          18 :             if (prevN->getIncomingEdges().size() == 1 && prevN->getOutgoingEdges().size() == 1) {
     325          12 :                 curr = prevN->getIncomingEdges()[0];
     326          12 :                 if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER && toAdd != 0) {
     327             :                     // curr might be an onRamp. In this case connections need to be rebuilt
     328          12 :                     curr->invalidateConnections();
     329             :                 }
     330          12 :                 if (curr->getNumLanes() != firstLaneNumber) {
     331             :                     // the number of lanes changes along the computation; we'll stop...
     332           3 :                     curr = nullptr;
     333           9 :                 } else if (last->isTurningDirectionAt(curr)) {
     334             :                     // turnarounds certainly should not be included in a ramp
     335           0 :                     curr = nullptr;
     336           9 :                 } else if (curr == potHighway || curr == potRamp) {
     337             :                     // circular connectivity. do not split!
     338           0 :                     curr = nullptr;
     339             :                 }
     340             :             } else {
     341             :                 // ambiguous; and, in fact, what should it be? ...stop
     342           6 :                 curr = nullptr;
     343             :             }
     344             :         }
     345             :         // check whether a further split is necessary
     346          60 :         if (curr != nullptr && !dontSplit && currLength - POSITION_EPS < rampLength && curr->getNumLanes() == firstLaneNumber && std::find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
     347             :             // there is enough place to build a ramp; do it
     348             :             bool wasFirst = first == curr;
     349          25 :             Position pos = curr->getGeometry().positionAtOffset(curr->getGeometry().length() - (rampLength  - currLength));
     350          25 :             std::string newNodeID = getUnusedID(curr->getID() + "-AddedOffRampNode", nc);
     351          25 :             std::string newEdgeID = getUnusedID(curr->getID() + "-AddedOffRampEdge", ec);
     352          25 :             NBNode* rn = new NBNode(newNodeID, pos);
     353          25 :             nc.insert(rn);
     354          25 :             std::string name = curr->getID();
     355          25 :             ec.splitAt(dc, curr, rn, curr->getID(), newEdgeID, curr->getNumLanes(), curr->getNumLanes() + toAdd);
     356          25 :             curr = ec.retrieve(newEdgeID);
     357             :             incremented.insert(curr);
     358          25 :             last = curr;
     359          25 :             moveRampRight(curr, toAdd);
     360          25 :             if (wasFirst) {
     361          20 :                 first = curr;
     362             :             }
     363             :         }
     364          35 :         if (curr == prev && dontSplit && addLanes) {
     365           2 :             WRITE_WARNING("Could not build off-ramp for edge '"  + curr->getID() + "' due to option '--ramps.no-split'");
     366             :             return;
     367             :         }
     368             :     }
     369         116 :     NBEdge* toMark = first;
     370             :     toMark->markOffRamp(true);
     371             :     double markedLength = toMark->getLoadedLength();
     372         189 :     while (markedLength < OFFRAMP_LOOKBACK) {
     373         138 :         if (toMark != first && toMark->getToNode()->getOutgoingEdges().size() != 1) {
     374             :             break;
     375             :         }
     376         128 :         NBNode* from = toMark->getFromNode();
     377         128 :         if (from->getIncomingEdges().size() == 1) {
     378          65 :             toMark = from->getIncomingEdges()[0];
     379             :         } else if (potOnRamps.count(from) == 1) {
     380             :             NBEdge* potOnRamp, *cont;
     381           8 :             getOnRampEdges(from, &toMark, &potOnRamp, &cont);
     382             :         } else {
     383             :             break;
     384             :         }
     385          73 :         toMark->markOffRamp(true);
     386          73 :         markedLength += toMark->getLoadedLength();
     387             :     }
     388             :     // set connections from added ramp to ramp/highway
     389         116 :     if (addLanes) {
     390          42 :         if (first->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
     391          41 :             if (!first->addLane2LaneConnections(potRamp->getNumLanes(), potHighway, 0, MIN2(first->getNumLanes() - 1, potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
     392           0 :                 throw ProcessError(TL("Could not set connection!"));
     393             :             }
     394          41 :             if (!first->addLane2LaneConnections(0, potRamp, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, false)) {
     395           0 :                 throw ProcessError(TL("Could not set connection!"));
     396             :             }
     397             :         }
     398          42 :         patchRampGeometry(potRamp, first, potHighway, true);
     399             :     }
     400             : }
     401             : 
     402             : 
     403             : void
     404          80 : NBRampsComputer::moveRampRight(NBEdge* ramp, int addedLanes) {
     405          80 :     if (ramp->getLaneSpreadFunction() != LaneSpreadFunction::CENTER) {
     406             :         return;
     407             :     }
     408             :     try {
     409             :         PositionVector g = ramp->getGeometry();
     410          53 :         const double offset = (0.5 * addedLanes *
     411          53 :                                (ramp->getLaneWidth() == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : ramp->getLaneWidth()));
     412          53 :         g.move2side(offset);
     413          53 :         ramp->setGeometry(g);
     414          53 :     } catch (InvalidArgument&) {
     415           0 :         WRITE_WARNINGF(TL("For edge '%': could not compute shape."), ramp->getID());
     416           0 :     }
     417             : }
     418             : 
     419             : 
     420             : bool
     421           0 : NBRampsComputer::determinedBySpeed(NBEdge** potHighway, NBEdge** potRamp) {
     422           0 :     if (fabs((*potHighway)->getSpeed() - (*potRamp)->getSpeed()) < .1) {
     423             :         return false;
     424             :     }
     425           0 :     if ((*potHighway)->getSpeed() < (*potRamp)->getSpeed()) {
     426             :         std::swap(*potHighway, *potRamp);
     427             :     }
     428             :     return true;
     429             : }
     430             : 
     431             : 
     432             : bool
     433           0 : NBRampsComputer::determinedByLaneNumber(NBEdge** potHighway, NBEdge** potRamp) {
     434           0 :     if ((*potHighway)->getNumLanes() == (*potRamp)->getNumLanes()) {
     435             :         return false;
     436             :     }
     437           0 :     if ((*potHighway)->getNumLanes() < (*potRamp)->getNumLanes()) {
     438             :         std::swap(*potHighway, *potRamp);
     439             :     }
     440             :     return true;
     441             : }
     442             : 
     443             : 
     444             : void
     445        4703 : NBRampsComputer::getOnRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
     446        4703 :     *other = n->getOutgoingEdges()[0];
     447             :     const std::vector<NBEdge*>& edges = n->getIncomingEdges();
     448             :     assert(edges.size() == 2);
     449        4703 :     *potHighway = edges[0];
     450        4703 :     *potRamp = edges[1];
     451             :     /*
     452             :     // heuristic: highway is faster than ramp
     453             :     if(determinedBySpeed(potHighway, potRamp)) {
     454             :         return;
     455             :     }
     456             :     // heuristic: highway has more lanes than ramp
     457             :     if(determinedByLaneNumber(potHighway, potRamp)) {
     458             :         return;
     459             :     }
     460             :     */
     461             :     // heuristic: ramp comes from right
     462        4703 :     if (NBContHelper::relative_incoming_edge_sorter(*other)(*potRamp, *potHighway)) {
     463             :         std::swap(*potHighway, *potRamp);
     464             :     }
     465        4703 : }
     466             : 
     467             : 
     468             : void
     469        6228 : NBRampsComputer::getOffRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
     470        6228 :     *other = n->getIncomingEdges()[0];
     471             :     const std::vector<NBEdge*>& edges = n->getOutgoingEdges();
     472        6228 :     *potHighway = edges[0];
     473        6228 :     *potRamp = edges[1];
     474             :     assert(edges.size() == 2);
     475             :     /*
     476             :     // heuristic: highway is faster than ramp
     477             :     if(determinedBySpeed(potHighway, potRamp)) {
     478             :         return;
     479             :     }
     480             :     // heuristic: highway has more lanes than ramp
     481             :     if(determinedByLaneNumber(potHighway, potRamp)) {
     482             :         return;
     483             :     }
     484             :     */
     485             :     // heuristic: ramp goes to right
     486             :     const std::vector<NBEdge*>& edges2 = n->getEdges();
     487             : #ifdef DEBUG_RAMPS
     488             :     if (DEBUGCOND(n)) {
     489             :         std::cout << "  edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
     490             :     }
     491             : #endif
     492        6228 :     std::vector<NBEdge*>::const_iterator i = std::find(edges2.begin(), edges2.end(), *other);
     493        6228 :     NBContHelper::nextCW(edges2, i);
     494        6228 :     if ((*i) == *potRamp) {
     495             :         std::swap(*potHighway, *potRamp);
     496             :     }
     497             :     // the following would be better but runs afoul of misleading angles when both edges
     498             :     // have the same geometry start point but different references lanes are
     499             :     // chosen for NBEdge::computeAngle()
     500             :     //if (NBContHelper::relative_outgoing_edge_sorter(*other)(*potHighway, *potRamp)) {
     501             :     //    std::swap(*potHighway, *potRamp);
     502             :     //}
     503        6228 : }
     504             : 
     505             : 
     506             : bool
     507       10721 : NBRampsComputer::fulfillsRampConstraints(
     508             :     NBEdge* potHighway, NBEdge* potRamp, NBEdge* other, double minHighwaySpeed, double maxRampSpeed,
     509             :     const std::set<std::string>& noramps) {
     510             :     // check modes that are not appropriate for rampsdo not build ramps on rail edges
     511       10721 :     if (hasWrongMode(potHighway) || hasWrongMode(potRamp) || hasWrongMode(other)) {
     512        7793 :         return false;
     513             :     }
     514             :     // do not build ramps at traffic lights
     515        2928 :     if (NBNode::isTrafficLight(potRamp->getToNode()->getType())) {
     516             :         return false;
     517             :     }
     518             :     // do not build ramps on connectors
     519        2483 :     if (potHighway->isMacroscopicConnector() || potRamp->isMacroscopicConnector() || other->isMacroscopicConnector()) {
     520             :         return false;
     521             :     }
     522             :     // check whether a lane is missing
     523        2483 :     if (potHighway->getNumLanes() + potRamp->getNumLanes() < other->getNumLanes()) {
     524             :         return false;
     525             :     }
     526             :     // is it really a highway?
     527        2437 :     double maxSpeed = MAX3(potHighway->getSpeed(), other->getSpeed(), potRamp->getSpeed());
     528        2437 :     if (maxSpeed < minHighwaySpeed) {
     529             :         return false;
     530             :     }
     531             :     // is any of the connections a turnaround?
     532         298 :     if (other->getToNode() == potHighway->getFromNode()) {
     533             :         // off ramp
     534         342 :         if (other->isTurningDirectionAt(potHighway) ||
     535         169 :                 other->isTurningDirectionAt(potRamp)) {
     536           4 :             return false;
     537             :         }
     538             :     } else {
     539             :         // on ramp
     540         250 :         if (other->isTurningDirectionAt(potHighway) ||
     541         125 :                 other->isTurningDirectionAt(potRamp)) {
     542           2 :             return false;
     543             :         }
     544             :     }
     545             :     // are the angles between highway and other / ramp and other more or less straight?
     546         125 :     const NBNode* node = ((potHighway->getToNode() == potRamp->getToNode() && potHighway->getToNode() == other->getFromNode())
     547         292 :                           ? potHighway->getToNode() : potHighway->getFromNode());
     548         292 :     double angle = fabs(NBHelpers::relAngle(potHighway->getAngleAtNode(node), other->getAngleAtNode(node)));
     549         292 :     if (angle >= 60) {
     550             :         return false;
     551             :     }
     552         242 :     angle = fabs(NBHelpers::relAngle(potRamp->getAngleAtNode(node), other->getAngleAtNode(node)));
     553         242 :     if (angle >= 60) {
     554             :         return false;
     555             :     }
     556             :     /*
     557             :     if (potHighway->getSpeed() < minHighwaySpeed || other->getSpeed() < minHighwaySpeed) {
     558             :         return false;
     559             :     }
     560             :     */
     561             :     // is it really a ramp?
     562         217 :     if (maxRampSpeed > 0 && maxRampSpeed < potRamp->getSpeed()) {
     563             :         return false;
     564             :     }
     565         217 :     if (noramps.find(other->getID()) != noramps.end()) {
     566          15 :         return false;
     567             :     }
     568             :     return true;
     569             : }
     570             : 
     571             : 
     572             : bool
     573       17314 : NBRampsComputer::hasWrongMode(NBEdge* edge) {
     574             :     // must allow passenger vehicles
     575       17314 :     if ((edge->getPermissions() & SVC_PASSENGER) == 0) {
     576             :         return true;
     577             :     }
     578             :     // must not have a green verge or a lane that is only for soft modes
     579       25830 :     for (int i = 0; i < (int)edge->getNumLanes(); ++i) {
     580       16309 :         if ((edge->getPermissions(i) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
     581             :             return true;
     582             :         }
     583             :     }
     584             :     return false;
     585             : }
     586             : 
     587             : void
     588          76 : NBRampsComputer::patchRampGeometry(NBEdge* potRamp, NBEdge* first, NBEdge* potHighway, bool onRamp) {
     589             :     // geometry of first and highway should allign on the left side
     590          76 :     if (first->getLaneSpreadFunction() == LaneSpreadFunction::CENTER && first->hasDefaultGeometryEndpoints()) {
     591           5 :         const NBNode* n = onRamp ? potHighway->getToNode() : potHighway->getFromNode();
     592           5 :         if (potHighway->hasDefaultGeometryEndpointAtNode(n)) {
     593             :             PositionVector p2 = first->getGeometry();
     594             :             try {
     595           5 :                 p2.move2side((first->getNumLanes() - potHighway->getNumLanes()) * first->getLaneWidth(0) * 0.5);
     596           5 :                 first->setGeometry(p2);
     597           0 :             } catch (InvalidArgument&) {}
     598           5 :         }
     599             :     }
     600             : 
     601             :     // ramp should merge smoothly with first
     602             :     PositionVector p = potRamp->getGeometry();
     603             :     double offset = 0;
     604          76 :     int firstIndex = MAX2(0, MIN2(potRamp->getNumLanes(), first->getNumLanes()) - 1);
     605          76 :     if (potRamp->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
     606          28 :         offset = -first->getLaneWidth(firstIndex) / 2;
     607             :     } else {
     608          48 :         if (firstIndex % 2 == 1) {
     609             :             // even number of lanes
     610          10 :             offset = -first->getLaneWidth(firstIndex / 2) / 2;
     611             :         }
     612          48 :         firstIndex /= 2; // integer division
     613             :     }
     614             :     // reset lane shape (might be affected by earlier junctions.join step. see #947)
     615          76 :     first->resetLaneShapes();
     616          76 :     PositionVector l = first->getLaneShape(firstIndex);
     617             :     try {
     618          76 :         l.move2side(offset);
     619           0 :     } catch (InvalidArgument&) {}
     620             :     //std::cout << " ramp=" << potRamp->getID() << " firstIndex=" << firstIndex << " offset=" << offset << " l=" << l << "\n";
     621             : 
     622          76 :     if (onRamp) {
     623          42 :         p[0] = l[-1];
     624             :     } else {
     625             :         p.pop_back();
     626          34 :         p.push_back(l[0]);
     627             :     }
     628          76 :     potRamp->setGeometry(p);
     629             : 
     630          76 : }
     631             : 
     632             : 
     633             : /****************************************************************************/

Generated by: LCOV version 1.14