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