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