Eclipse SUMO - Simulation of Urban MObility
NBAlgorithms_Ramps.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3// Copyright (C) 2012-2023 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/****************************************************************************/
20// Algorithms for highway on-/off-ramps computation
21/****************************************************************************/
22#include <config.h>
23
24#include <cassert>
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// ===========================================================================
43const std::string NBRampsComputer::ADDED_ON_RAMP_EDGE("-AddedOnRampEdge");
44
45// ===========================================================================
46// method definitions
47// ===========================================================================
48// ---------------------------------------------------------------------------
49// NBRampsComputer
50// ---------------------------------------------------------------------------
51void
53 const bool guessAndAdd = oc.getBool("ramps.guess") && mayAddOrRemove;
54 const double minHighwaySpeed = oc.getFloat("ramps.min-highway-speed");
55 const double maxRampSpeed = oc.getFloat("ramps.max-ramp-speed");
56 const double rampLength = oc.getFloat("ramps.ramp-length");
57 const double minWeaveLength = oc.getFloat("ramps.min-weave-length");
58 const bool dontSplit = oc.getBool("ramps.no-split");
59 NBNodeCont& nc = nb.getNodeCont();
60 NBEdgeCont& ec = nb.getEdgeCont();
62 std::set<NBEdge*> incremented;
63 // collect join exclusions
64 std::set<std::string> noramps;
65 if (oc.isSet("ramps.unset")) {
66 std::vector<std::string> edges = oc.getStringVector("ramps.unset");
67 noramps.insert(edges.begin(), edges.end());
68 }
69 // exclude roundabouts
70 for (const EdgeSet& round : ec.getRoundabouts()) {
71 for (NBEdge* const edge : round) {
72 noramps.insert(edge->getID());
73 }
74 }
75 // exclude public transport edges
76 nb.getPTStopCont().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 for (const auto& it : ec) {
82 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 for (const auto& i : nc) {
89 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 if (mayNeedOnRamp(cur, minHighwaySpeed, maxRampSpeed, noramps, minWeaveLength)) {
96 potOnRamps.insert(cur);
97 }
98 if (mayNeedOffRamp(cur, minHighwaySpeed, maxRampSpeed, noramps)) {
99 potOffRamps.insert(cur);
100 }
101 }
102 for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOnRamps.begin(); i != potOnRamps.end(); ++i) {
103 buildOnRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd);
104 }
105 for (std::set<NBNode*, ComparatorIdLess>::const_iterator i = potOffRamps.begin(); i != potOffRamps.end(); ++i) {
106 buildOffRamp(*i, nc, ec, dc, rampLength, dontSplit || !guessAndAdd, guessAndAdd, potOnRamps);
107 }
108 }
109 // check whether on-off ramps are specified
110 if (oc.isSet("ramps.set") && mayAddOrRemove) {
111 std::vector<std::string> edges = oc.getStringVector("ramps.set");
112 std::set<NBNode*, ComparatorIdLess> potOnRamps;
113 for (const std::string& i : edges) {
114 NBEdge* e = ec.retrieve(i);
115 if (noramps.count(i) != 0) {
116 WRITE_WARNINGF(TL("Can not build ramp on edge '%' - the edge is unsuitable."), i);
117 continue;
118 }
119 if (e == nullptr) {
120 WRITE_WARNINGF(TL("Can not build on ramp on edge '%' - the edge is not known."), i);
121 continue;
122 }
123 NBNode* from = e->getFromNode();
124 if (from->getIncomingEdges().size() == 2 && from->getOutgoingEdges().size() == 1) {
125 buildOnRamp(from, nc, ec, dc, rampLength, dontSplit, true);
126 potOnRamps.insert(from);
127 }
128 // load edge again to check offramps
129 e = ec.retrieve(i);
130 if (e == nullptr) {
131 WRITE_WARNINGF(TL("Can not build off ramp on edge '%' - the edge is not known."), i);
132 continue;
133 }
134 NBNode* to = e->getToNode();
135 if (to->getIncomingEdges().size() == 1 && to->getOutgoingEdges().size() == 2) {
136 buildOffRamp(to, nc, ec, dc, rampLength, dontSplit, true, potOnRamps);
137 }
138 }
139 }
140}
141
142
143bool
144NBRampsComputer::mayNeedOnRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps, double minWeaveLength) {
145 if (cur->getOutgoingEdges().size() != 1 || cur->getIncomingEdges().size() != 2) {
146 return false;
147 }
148 NBEdge* potHighway, *potRamp, *cont;
149 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 if (fulfillsRampConstraints(potHighway, potRamp, cont, minHighwaySpeed, maxRampSpeed, noramps)) {
157 // prevent short weaving section
158 double seen = cont->getLength();
159 while (seen < minWeaveLength) {
160 if (cont->getToNode()->getOutgoingEdges().size() > 1) {
161 return false;
162 } else if (cont->getToNode()->getOutgoingEdges().size() == 0) {
163 return true;
164 }
165 cont = cont->getToNode()->getOutgoingEdges().front();
166 seen += cont->getLength();
167 }
168 return true;
169 } else {
170 return false;
171 }
172}
173
174
175bool
176NBRampsComputer::mayNeedOffRamp(NBNode* cur, double minHighwaySpeed, double maxRampSpeed, const std::set<std::string>& noramps) {
177 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 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 return fulfillsRampConstraints(potHighway, potRamp, prev, minHighwaySpeed, maxRampSpeed, noramps);
189}
190
191
192void
193NBRampsComputer::buildOnRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes) {
194 NBEdge* potHighway, *potRamp, *cont;
195 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 const int firstLaneNumber = cont->getNumLanes();
203 int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
204 NBEdge* first = cont;
205 NBEdge* last = cont;
206 NBEdge* curr = cont;
207 std::set<NBEdge*> incremented;
208 if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), cont) == incremented.end()) {
209 double currLength = 0;
210 while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
211 if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
212 curr->incLaneNo(toAdd);
214 curr->invalidateConnections(true);
215 }
216 incremented.insert(curr);
217 moveRampRight(curr, toAdd);
218 currLength += curr->getGeometry().length(); // !!! loaded length?
219 last = curr;
220 // mark acceleration lanes
221 for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
222 curr->setAcceleration(i, true);
223 }
224 }
225 NBNode* nextN = curr->getToNode();
226 if (nextN->getOutgoingEdges().size() == 1 && nextN->getIncomingEdges().size() == 1) {
227 curr = nextN->getOutgoingEdges()[0];
228 if (curr->getNumLanes() != firstLaneNumber) {
229 // the number of lanes changes along the computation; we'll stop...
230 curr = nullptr;
231 } else if (curr->isTurningDirectionAt(last)) {
232 // turnarounds certainly should not be included in a ramp
233 curr = nullptr;
234 } else if (curr == potHighway || curr == potRamp) {
235 // circular connectivity. do not split!
236 curr = nullptr;
237 }
238 } else {
239 // ambiguous; and, in fact, what should it be? ...stop
240 curr = nullptr;
241 }
242 }
243 // check whether a further split is necessary
244 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 NBNode* rn = new NBNode(curr->getID() + "-AddedOnRampNode", curr->getGeometry().positionAtOffset(rampLength - currLength));
248 if (!nc.insert(rn)) {
249 throw ProcessError(TLF("Ups - could not build on-ramp for edge '%' (node could not be build)!", curr->getID()));
250 }
251 std::string name = curr->getID();
252 bool ok = ec.splitAt(dc, curr, rn, curr->getID() + ADDED_ON_RAMP_EDGE, curr->getID(), curr->getNumLanes() + toAdd, curr->getNumLanes());
253 if (!ok) {
254 WRITE_ERRORF(TL("Ups - could not build on-ramp for edge '%'!"), curr->getID());
255 return;
256 }
257 //ec.retrieve(name)->invalidateConnections();
258 curr = ec.retrieve(name + ADDED_ON_RAMP_EDGE);
259 incremented.insert(curr);
260 last = curr;
261 moveRampRight(curr, toAdd);
262 if (wasFirst) {
263 first = curr;
264 }
265 // mark acceleration lanes
266 for (int i = 0; i < curr->getNumLanes() - potHighway->getNumLanes(); ++i) {
267 curr->setAcceleration(i, true);
268 }
269 }
270 if (curr == cont && dontSplit && addLanes) {
271 WRITE_WARNING("Could not build on-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
272 return;
273 }
274 } else {
275 // mark acceleration lanes
276 for (int i = 0; i < firstLaneNumber - potHighway->getNumLanes(); ++i) {
277 cont->setAcceleration(i, true);
278 }
279 }
280 // set connections from ramp/highway to added ramp
281 if (addLanes) {
283 if (!potHighway->addLane2LaneConnections(0, first, potRamp->getNumLanes(), MIN2(first->getNumLanes() - potRamp->getNumLanes(), potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
284 throw ProcessError(TL("Could not set connection!"));
285 }
286 }
288 if (!potRamp->addLane2LaneConnections(0, first, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
289 throw ProcessError(TL("Could not set connection!"));
290 }
291 }
292 patchRampGeometry(potRamp, first, potHighway, false);
293 }
294}
295
296
297void
298NBRampsComputer::buildOffRamp(NBNode* cur, NBNodeCont& nc, NBEdgeCont& ec, NBDistrictCont& dc, double rampLength, bool dontSplit, bool addLanes,
299 const std::set<NBNode*, ComparatorIdLess>& potOnRamps) {
300 NBEdge* potHighway, *potRamp, *prev;
301 getOffRampEdges(cur, &potHighway, &potRamp, &prev);
302#ifdef DEBUG_RAMPS
303 if (DEBUGCOND(cur)) {
304 std::cout << "buildOffRamp cur=" << cur->getID() << " hw=" << potHighway->getID() << " ramp=" << potRamp->getID() << " prev=" << prev->getID() << "\n";
305 }
306#endif
307 // compute the number of lanes to append
308 const int firstLaneNumber = prev->getNumLanes();
309 int toAdd = (potRamp->getNumLanes() + potHighway->getNumLanes()) - firstLaneNumber;
310 NBEdge* first = prev;
311 NBEdge* last = prev;
312 NBEdge* curr = prev;
313 std::set<NBEdge*> incremented;
314 if (addLanes && toAdd > 0 && std::find(incremented.begin(), incremented.end(), prev) == incremented.end()) {
315 double currLength = 0;
316 while (curr != nullptr && currLength + curr->getGeometry().length() - POSITION_EPS < rampLength) {
317 if (find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
318 curr->incLaneNo(toAdd);
320 curr->invalidateConnections(true);
321 }
322 incremented.insert(curr);
323 moveRampRight(curr, toAdd);
324 currLength += curr->getGeometry().length(); // !!! loaded length?
325 last = curr;
326 }
327 NBNode* prevN = curr->getFromNode();
328 if (prevN->getIncomingEdges().size() == 1 && prevN->getOutgoingEdges().size() == 1) {
329 curr = prevN->getIncomingEdges()[0];
330 if (curr->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_USER && toAdd != 0) {
331 // curr might be an onRamp. In this case connections need to be rebuilt
332 curr->invalidateConnections();
333 }
334 if (curr->getNumLanes() != firstLaneNumber) {
335 // the number of lanes changes along the computation; we'll stop...
336 curr = nullptr;
337 } else if (last->isTurningDirectionAt(curr)) {
338 // turnarounds certainly should not be included in a ramp
339 curr = nullptr;
340 } else if (curr == potHighway || curr == potRamp) {
341 // circular connectivity. do not split!
342 curr = nullptr;
343 }
344 } else {
345 // ambiguous; and, in fact, what should it be? ...stop
346 curr = nullptr;
347 }
348 }
349 // check whether a further split is necessary
350 if (curr != nullptr && !dontSplit && currLength - POSITION_EPS < rampLength && curr->getNumLanes() == firstLaneNumber && std::find(incremented.begin(), incremented.end(), curr) == incremented.end()) {
351 // there is enough place to build a ramp; do it
352 bool wasFirst = first == curr;
353 Position pos = curr->getGeometry().positionAtOffset(curr->getGeometry().length() - (rampLength - currLength));
354 NBNode* rn = new NBNode(curr->getID() + "-AddedOffRampNode", pos);
355 if (!nc.insert(rn)) {
356 throw ProcessError(TLF("Ups - could not build off-ramp for edge '%' (node could not be build)!", curr->getID()));
357 }
358 std::string name = curr->getID();
359 bool ok = ec.splitAt(dc, curr, rn, curr->getID(), curr->getID() + "-AddedOffRampEdge", curr->getNumLanes(), curr->getNumLanes() + toAdd);
360 if (!ok) {
361 WRITE_ERRORF(TL("Ups - could not build off-ramp for edge '%'!"), curr->getID());
362 return;
363 }
364 curr = ec.retrieve(name + "-AddedOffRampEdge");
365 incremented.insert(curr);
366 last = curr;
367 moveRampRight(curr, toAdd);
368 if (wasFirst) {
369 first = curr;
370 }
371 }
372 if (curr == prev && dontSplit && addLanes) {
373 WRITE_WARNING("Could not build off-ramp for edge '" + curr->getID() + "' due to option '--ramps.no-split'");
374 return;
375 }
376 }
377 NBEdge* toMark = first;
378 toMark->markOffRamp(true);
379 double markedLength = toMark->getLoadedLength();
380 while (markedLength < OFFRAMP_LOOKBACK) {
381 if (toMark != first && toMark->getToNode()->getOutgoingEdges().size() != 1) {
382 break;
383 }
384 NBNode* from = toMark->getFromNode();
385 if (from->getIncomingEdges().size() == 1) {
386 toMark = from->getIncomingEdges()[0];
387 } else if (potOnRamps.count(from) == 1) {
388 NBEdge* potOnRamp, *cont;
389 getOnRampEdges(from, &toMark, &potOnRamp, &cont);
390 } else {
391 break;
392 }
393 toMark->markOffRamp(true);
394 markedLength += toMark->getLoadedLength();
395 }
396 // set connections from added ramp to ramp/highway
397 if (addLanes) {
399 if (!first->addLane2LaneConnections(potRamp->getNumLanes(), potHighway, 0, MIN2(first->getNumLanes() - 1, potHighway->getNumLanes()), NBEdge::Lane2LaneInfoType::VALIDATED, true)) {
400 throw ProcessError(TL("Could not set connection!"));
401 }
402 if (!first->addLane2LaneConnections(0, potRamp, 0, potRamp->getNumLanes(), NBEdge::Lane2LaneInfoType::VALIDATED, false)) {
403 throw ProcessError(TL("Could not set connection!"));
404 }
405 }
406 patchRampGeometry(potRamp, first, potHighway, true);
407 }
408}
409
410
411void
414 return;
415 }
416 try {
417 PositionVector g = ramp->getGeometry();
418 const double offset = (0.5 * addedLanes *
420 g.move2side(offset);
421 ramp->setGeometry(g);
422 } catch (InvalidArgument&) {
423 WRITE_WARNINGF(TL("For edge '%': could not compute shape."), ramp->getID());
424 }
425}
426
427
428bool
430 if (fabs((*potHighway)->getSpeed() - (*potRamp)->getSpeed()) < .1) {
431 return false;
432 }
433 if ((*potHighway)->getSpeed() < (*potRamp)->getSpeed()) {
434 std::swap(*potHighway, *potRamp);
435 }
436 return true;
437}
438
439
440bool
442 if ((*potHighway)->getNumLanes() == (*potRamp)->getNumLanes()) {
443 return false;
444 }
445 if ((*potHighway)->getNumLanes() < (*potRamp)->getNumLanes()) {
446 std::swap(*potHighway, *potRamp);
447 }
448 return true;
449}
450
451
452void
453NBRampsComputer::getOnRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
454 *other = n->getOutgoingEdges()[0];
455 const std::vector<NBEdge*>& edges = n->getIncomingEdges();
456 assert(edges.size() == 2);
457 *potHighway = edges[0];
458 *potRamp = edges[1];
459 /*
460 // heuristic: highway is faster than ramp
461 if(determinedBySpeed(potHighway, potRamp)) {
462 return;
463 }
464 // heuristic: highway has more lanes than ramp
465 if(determinedByLaneNumber(potHighway, potRamp)) {
466 return;
467 }
468 */
469 // heuristic: ramp comes from right
470 if (NBContHelper::relative_incoming_edge_sorter(*other)(*potRamp, *potHighway)) {
471 std::swap(*potHighway, *potRamp);
472 }
473}
474
475
476void
477NBRampsComputer::getOffRampEdges(NBNode* n, NBEdge** potHighway, NBEdge** potRamp, NBEdge** other) {
478 *other = n->getIncomingEdges()[0];
479 const std::vector<NBEdge*>& edges = n->getOutgoingEdges();
480 *potHighway = edges[0];
481 *potRamp = edges[1];
482 assert(edges.size() == 2);
483 /*
484 // heuristic: highway is faster than ramp
485 if(determinedBySpeed(potHighway, potRamp)) {
486 return;
487 }
488 // heuristic: highway has more lanes than ramp
489 if(determinedByLaneNumber(potHighway, potRamp)) {
490 return;
491 }
492 */
493 // heuristic: ramp goes to right
494 const std::vector<NBEdge*>& edges2 = n->getEdges();
495#ifdef DEBUG_RAMPS
496 if (DEBUGCOND(n)) {
497 std::cout << " edges=" << toString(edges) << " edges2=" << toString(edges2) << "\n";
498 }
499#endif
500 std::vector<NBEdge*>::const_iterator i = std::find(edges2.begin(), edges2.end(), *other);
501 NBContHelper::nextCW(edges2, i);
502 if ((*i) == *potRamp) {
503 std::swap(*potHighway, *potRamp);
504 }
505 // the following would be better but runs afoul of misleading angles when both edges
506 // have the same geometry start point but different references lanes are
507 // chosen for NBEdge::computeAngle()
508 //if (NBContHelper::relative_outgoing_edge_sorter(*other)(*potHighway, *potRamp)) {
509 // std::swap(*potHighway, *potRamp);
510 //}
511}
512
513
514bool
516 NBEdge* potHighway, NBEdge* potRamp, NBEdge* other, double minHighwaySpeed, double maxRampSpeed,
517 const std::set<std::string>& noramps) {
518 // check modes that are not appropriate for rampsdo not build ramps on rail edges
519 if (hasWrongMode(potHighway) || hasWrongMode(potRamp) || hasWrongMode(other)) {
520 return false;
521 }
522 // do not build ramps at traffic lights
523 if (NBNode::isTrafficLight(potRamp->getToNode()->getType())) {
524 return false;
525 }
526 // do not build ramps on connectors
527 if (potHighway->isMacroscopicConnector() || potRamp->isMacroscopicConnector() || other->isMacroscopicConnector()) {
528 return false;
529 }
530 // check whether a lane is missing
531 if (potHighway->getNumLanes() + potRamp->getNumLanes() < other->getNumLanes()) {
532 return false;
533 }
534 // is it really a highway?
535 double maxSpeed = MAX3(potHighway->getSpeed(), other->getSpeed(), potRamp->getSpeed());
536 if (maxSpeed < minHighwaySpeed) {
537 return false;
538 }
539 // is any of the connections a turnaround?
540 if (other->getToNode() == potHighway->getFromNode()) {
541 // off ramp
542 if (other->isTurningDirectionAt(potHighway) ||
543 other->isTurningDirectionAt(potRamp)) {
544 return false;
545 }
546 } else {
547 // on ramp
548 if (other->isTurningDirectionAt(potHighway) ||
549 other->isTurningDirectionAt(potRamp)) {
550 return false;
551 }
552 }
553 // are the angles between highway and other / ramp and other more or less straight?
554 const NBNode* node = ((potHighway->getToNode() == potRamp->getToNode() && potHighway->getToNode() == other->getFromNode())
555 ? potHighway->getToNode() : potHighway->getFromNode());
556 double angle = fabs(NBHelpers::relAngle(potHighway->getAngleAtNode(node), other->getAngleAtNode(node)));
557 if (angle >= 60) {
558 return false;
559 }
560 angle = fabs(NBHelpers::relAngle(potRamp->getAngleAtNode(node), other->getAngleAtNode(node)));
561 if (angle >= 60) {
562 return false;
563 }
564 /*
565 if (potHighway->getSpeed() < minHighwaySpeed || other->getSpeed() < minHighwaySpeed) {
566 return false;
567 }
568 */
569 // is it really a ramp?
570 if (maxRampSpeed > 0 && maxRampSpeed < potRamp->getSpeed()) {
571 return false;
572 }
573 if (noramps.find(other->getID()) != noramps.end()) {
574 return false;
575 }
576 return true;
577}
578
579
580bool
582 // must allow passenger vehicles
583 if ((edge->getPermissions() & SVC_PASSENGER) == 0) {
584 return true;
585 }
586 // must not have a green verge or a lane that is only for soft modes
587 for (int i = 0; i < (int)edge->getNumLanes(); ++i) {
588 if ((edge->getPermissions(i) & ~(SVC_PEDESTRIAN | SVC_BICYCLE)) == 0) {
589 return true;
590 }
591 }
592 return false;
593}
594
595void
596NBRampsComputer::patchRampGeometry(NBEdge* potRamp, NBEdge* first, NBEdge* potHighway, bool onRamp) {
597 // geometry of first and highway should allign on the left side
599 const NBNode* n = onRamp ? potHighway->getToNode() : potHighway->getFromNode();
600 if (potHighway->hasDefaultGeometryEndpointAtNode(n)) {
601 PositionVector p2 = first->getGeometry();
602 try {
603 p2.move2side((first->getNumLanes() - potHighway->getNumLanes()) * first->getLaneWidth(0) * 0.5);
604 first->setGeometry(p2);
605 } catch (InvalidArgument&) {}
606 }
607 }
608
609 // ramp should merge smoothly with first
610 PositionVector p = potRamp->getGeometry();
611 double offset = 0;
612 int firstIndex = MAX2(0, MIN2(potRamp->getNumLanes(), first->getNumLanes()) - 1);
614 offset = -first->getLaneWidth(firstIndex) / 2;
615 } else {
616 if (firstIndex % 2 == 1) {
617 // even number of lanes
618 offset = -first->getLaneWidth(firstIndex / 2) / 2;
619 }
620 firstIndex /= 2; // integer division
621 }
622 // reset lane shape (might be affected by earlier junctions.join step. see #947)
623 first->resetLaneShapes();
624 PositionVector l = first->getLaneShape(firstIndex);
625 try {
626 l.move2side(offset);
627 } catch (InvalidArgument&) {}
628 //std::cout << " ramp=" << potRamp->getID() << " firstIndex=" << firstIndex << " offset=" << offset << " l=" << l << "\n";
629
630 if (onRamp) {
631 p[0] = l[-1];
632 } else {
633 p.pop_back();
634 p.push_back(l[0]);
635 }
636 potRamp->setGeometry(p);
637
638}
639
640
641/****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:268
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:277
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:267
#define TL(string)
Definition: MsgHandler.h:284
#define TLF(string,...)
Definition: MsgHandler.h:285
#define DEBUGCOND(obj)
#define OFFRAMP_LOOKBACK
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_PEDESTRIAN
pedestrian
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
T MAX3(T a, T b, T c)
Definition: StdDefs.h:96
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static void nextCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
A container for districts.
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:59
const std::set< EdgeSet > getRoundabouts() const
Returns the determined roundabouts.
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:279
bool splitAt(NBDistrictCont &dc, NBEdge *edge, NBNode *node)
Splits the edge at the position nearest to the given node.
Definition: NBEdgeCont.cpp:602
The representation of a single edge during network building.
Definition: NBEdge.h:92
double getLength() const
Returns the computed length of the edge.
Definition: NBEdge.h:583
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4215
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition: NBEdge.h:592
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:632
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:536
void incLaneNo(int by)
increment lane
Definition: NBEdge.cpp:3911
void markOffRamp(bool isOffRamp)
marks this edge has being an offRamp or leading to one (used for connection computation)
Definition: NBEdge.h:1382
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:771
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:946
bool hasDefaultGeometryEndpoints() const
Returns whether the geometry is terminated by the node positions This default may be violated by init...
Definition: NBEdge.cpp:594
EdgeBuildingStep getStep() const
The building step of this edge.
Definition: NBEdge.h:625
@ LANES2LANES_USER
Lanes to lanes - relationships are loaded; no recheck is necessary/wished.
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:609
const std::string & getID() const
Definition: NBEdge.h:1515
void resetLaneShapes()
reset lane shapes to what they would be before cutting with the junction shapes
Definition: NBEdge.cpp:2138
void setAcceleration(int lane, bool accelRamp)
marks one lane as acceleration lane
Definition: NBEdge.cpp:4162
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3507
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:510
void invalidateConnections(bool reallowSetting=false)
invalidate current connections of edge
Definition: NBEdge.cpp:1461
@ VALIDATED
The connection was computed and validated.
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:529
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2074
bool hasDefaultGeometryEndpointAtNode(const NBNode *node) const
Returns whether the geometry is terminated by the node positions This default may be violated by init...
Definition: NBEdge.cpp:601
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:341
bool addLane2LaneConnections(int fromLane, NBEdge *dest, int toLane, int no, Lane2LaneInfoType type, bool invalidatePrevious=false, bool mayDefinitelyPass=false)
Builds no connections starting at the given lanes.
Definition: NBEdge.cpp:1101
bool isMacroscopicConnector() const
Returns whether this edge was marked as a macroscopic connector.
Definition: NBEdge.h:1122
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:934
void setGeometry(const PositionVector &g, bool inner=false)
(Re)sets the edge's geometry
Definition: NBEdge.cpp:617
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition: NBHelpers.cpp:45
Instance responsible for building networks.
Definition: NBNetBuilder.h:107
NBParkingCont & getParkingCont()
Definition: NBNetBuilder.h:173
NBPTStopCont & getPTStopCont()
Returns a reference to the pt stop container.
Definition: NBNetBuilder.h:164
NBNodeCont & getNodeCont()
Returns a reference to the node container.
Definition: NBNetBuilder.h:144
NBEdgeCont & getEdgeCont()
Definition: NBNetBuilder.h:139
NBDistrictCont & getDistrictCont()
Returns a reference the districts container.
Definition: NBNetBuilder.h:159
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
Definition: NBNodeCont.cpp:87
Represents a single node (junction) during network building.
Definition: NBNode.h:66
SumoXMLNodeType getType() const
Returns the type of this node.
Definition: NBNode.h:283
static bool isTrafficLight(SumoXMLNodeType type)
return whether the given type is a traffic light
Definition: NBNode.cpp:3816
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:266
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:271
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:276
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
Definition: NBParking.cpp:78
static void computeRamps(NBNetBuilder &nb, OptionsCont &oc, bool mayAddOrRemove)
Computes highway on-/off-ramps (if wished)
static void getOffRampEdges(NBNode *n, NBEdge **potHighway, NBEdge **potRamp, NBEdge **other)
static bool mayNeedOffRamp(NBNode *cur, double minHighwaySpeed, double maxRampSpeed, const std::set< std::string > &noramps)
Determines whether the given node may be an off-ramp end.
static bool mayNeedOnRamp(NBNode *cur, double minHighwaySpeed, double maxRampSpeed, const std::set< std::string > &noramps, double minWeaveLength)
Determines whether the given node may be an on-ramp begin.
static bool determinedBySpeed(NBEdge **potHighway, NBEdge **potRamp)
static void moveRampRight(NBEdge *ramp, int addedLanes)
Moves the ramp to the right, as new lanes were added.
static void getOnRampEdges(NBNode *n, NBEdge **potHighway, NBEdge **potRamp, NBEdge **other)
static const std::string ADDED_ON_RAMP_EDGE
suffix for newly generated on-ramp edges
static void patchRampGeometry(NBEdge *potRamp, NBEdge *first, NBEdge *potHighway, bool onRamp)
shift ramp geometry to merge smoothly with the motorway
static void buildOffRamp(NBNode *cur, NBNodeCont &nc, NBEdgeCont &ec, NBDistrictCont &dc, double rampLength, bool dontSplit, bool addLanes, const std::set< NBNode *, ComparatorIdLess > &potOnRamps)
Builds an off-ramp ending at the given node.
static bool determinedByLaneNumber(NBEdge **potHighway, NBEdge **potRamp)
static bool hasWrongMode(NBEdge *edge)
whether the edge has a mode that does not indicate a ramp edge
static void buildOnRamp(NBNode *cur, NBNodeCont &nc, NBEdgeCont &ec, NBDistrictCont &dc, double rampLength, bool dontSplit, bool addLanes)
Builds an on-ramp starting at the given node.
static bool fulfillsRampConstraints(NBEdge *potHighway, NBEdge *potRamp, NBEdge *other, double minHighwaySpeed, double maxRampSpeed, const std::set< std::string > &noramps)
Checks whether an on-/off-ramp can be bult here.
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
A list of positions.
double length() const
Returns the length.
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain ammount
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884