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