Eclipse SUMO - Simulation of Urban MObility
NBNodeShapeComputer.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2022 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 // This class computes shapes of junctions
21 /****************************************************************************/
22 #include <config.h>
23 
24 #include <algorithm>
25 #include <iterator>
28 #include <utils/geom/GeomHelper.h>
29 #include <utils/common/StdDefs.h>
32 #include <utils/common/ToString.h>
34 #include "NBNode.h"
35 #include "NBNodeShapeComputer.h"
36 
37 //#define DEBUG_NODE_SHAPE
38 //#define DEBUG_SMOOTH_CORNERS
39 //#define DEBUG_RADIUS
40 #define DEBUGCOND (myNode.getID() == "C")
41 
42 
43 #define EXT 100.0
44 #define EXT2 10.0
45 
46 // foot and bicycle paths as well as pure service roads should not get large junctions
47 // railways also do have have junctions with sharp turns so can be excluded
50 
51 // ===========================================================================
52 // method definitions
53 // ===========================================================================
55  myNode(node),
56  myRadius(node.getRadius()) {
57 }
58 
59 
61 
62 
63 const PositionVector
65 #ifdef DEBUG_NODE_SHAPE
66  if (DEBUGCOND) {
67  // annotate edges edges to make their ordering visible
68  int i = 0;
69  for (NBEdge* e : myNode.getEdges()) {
70  e->setStreetName(toString(i));
71  i++;
72  }
73  }
74 #endif
75  // check whether the node is a dead end node or a node where only turning is possible
76  // in this case, we will use "computeNodeShapeSmall"
77  if (myNode.getEdges().size() == 1) {
78  return computeNodeShapeSmall();
79  }
80  if (myNode.getEdges().size() == 2 && myNode.getIncomingEdges().size() == 1) {
81  if (myNode.getIncomingEdges()[0]->isTurningDirectionAt(myNode.getOutgoingEdges()[0])) {
82  return computeNodeShapeSmall();
83  }
84  }
85  const bool geometryLike = myNode.isSimpleContinuation(true, true);
86  const PositionVector& ret = computeNodeShapeDefault(geometryLike);
87  // fail fall-back: use "computeNodeShapeSmall"
88  if (ret.size() < 3) {
89  return computeNodeShapeSmall();
90  }
91  return ret;
92 }
93 
94 
95 void
97  assert(l1[0].distanceTo2D(l1[1]) >= EXT);
98  assert(l2[0].distanceTo2D(l2[1]) >= EXT);
99  PositionVector tmp;
100  tmp.push_back(PositionVector::positionAtOffset2D(l1[0], l1[1], EXT));
101  tmp.push_back(l1[1]);
102  tmp[1].sub(tmp[0]);
103  tmp[1].set(-tmp[1].y(), tmp[1].x());
104  tmp[1].add(tmp[0]);
105  tmp.extrapolate2D(EXT);
106  if (l2.intersects(tmp[0], tmp[1])) {
107  const double offset = l2.intersectsAtLengths2D(tmp)[0];
108  if (l2.length2D() - offset > POSITION_EPS) {
109  PositionVector tl2 = l2.getSubpart2D(offset, l2.length2D());
110  tl2.extrapolate2D(EXT);
111  l2.erase(l2.begin(), l2.begin() + (l2.size() - tl2.size()));
112  l2[0] = tl2[0];
113  }
114  }
115 }
116 
117 
118 const PositionVector
120  // if we have less than two edges, we can not compute the node's shape this way
121  if (myNode.getEdges().size() < 2) {
122  return PositionVector();
123  }
124  // magic values
125  const OptionsCont& oc = OptionsCont::getOptions();
126  const double defaultRadius = getDefaultRadius(oc);
127  const bool useDefaultRadius = myNode.getRadius() == NBNode::UNSPECIFIED_RADIUS || myNode.getRadius() == defaultRadius;
128  myRadius = (useDefaultRadius ? defaultRadius : myNode.getRadius());
129  const double smallRadius = oc.getFloat("junctions.small-radius");
130  const int cornerDetail = oc.getInt("junctions.corner-detail");
131  const double sCurveStretch = oc.getFloat("junctions.scurve-stretch");
132  const bool rectangularCut = oc.getBool("rectangular-lane-cut");
133  const bool openDriveOutput = oc.isSet("opendrive-output");
134 
135  // Extend geometries to move the stop line forward.
136  // In OpenDrive the junction starts whenever the geometry changes. Stop
137  // line information is not given or ambiguous (sign positions at most)
138  // In SUMO, stop lines are where the junction starts. This is computed
139  // heuristically from intersecting the junctions roads geometries.
140  const double advanceStopLine = oc.exists("opendrive-files") && oc.isSet("opendrive-files") ? oc.getFloat("opendrive.advance-stopline") : 0;
141 
142 
143 #ifdef DEBUG_NODE_SHAPE
144  if (DEBUGCOND) {
145  std::cout << "\ncomputeNodeShapeDefault node " << myNode.getID() << " simple=" << simpleContinuation << " useDefaultRadius=" << useDefaultRadius << " radius=" << myRadius << "\n";
146  }
147 #endif
148 
149  // initialise
150  EdgeVector::const_iterator i;
151  // edges located in the value-vector have the same direction as the key edge
152  std::map<NBEdge*, std::set<NBEdge*> > same;
153  // the counter-clockwise boundary of the edge regarding possible same-direction edges
154  GeomsMap geomsCCW;
155  // the clockwise boundary of the edge regarding possible same-direction edges
156  GeomsMap geomsCW;
157  EdgeVector usedEdges = myNode.getEdges();
158  computeEdgeBoundaries(usedEdges, geomsCCW, geomsCW);
159 
160  // check which edges are parallel
161  joinSameDirectionEdges(usedEdges, same);
162  // compute unique direction list
163  EdgeVector newAll = computeUniqueDirectionList(usedEdges, same, geomsCCW, geomsCW);
164  // if we have only two "directions", let's not compute the geometry using this method
165  if (newAll.size() < 2) {
166  return PositionVector();
167  }
168 
169  // All geoms are outgoing from myNode.
170  // for every direction in newAll we compute the offset at which the
171  // intersection ends and the edge starts. This value is saved in 'distances'
172  // If the geometries need to be extended to get an intersection, this is
173  // recorded in 'myExtended'
174  std::map<NBEdge*, double> distances;
175  std::map<NBEdge*, bool> myExtended;
176 
177  for (i = newAll.begin(); i != newAll.end(); ++i) {
178  EdgeVector::const_iterator cwi = i;
179  EdgeVector::const_iterator ccwi = i;
180  double ccad;
181  double cad;
182  initNeighbors(newAll, i, geomsCW, geomsCCW, cwi, ccwi, cad, ccad);
183  assert(geomsCCW.find(*i) != geomsCCW.end());
184  assert(geomsCW.find(*ccwi) != geomsCW.end());
185  assert(geomsCW.find(*cwi) != geomsCW.end());
186 
187  // there are only 2 directions and they are almost parallel
188  if (*cwi == *ccwi &&
189  (
190  // no change in lane numbers, even low angles still give a good intersection
191  (simpleContinuation && fabs(ccad - cad) < (double) 0.1)
192  // lane numbers change, a direct intersection could be far away from the node position
193  // so we use a larger threshold
194  || (!simpleContinuation && fabs(ccad - cad) < DEG2RAD(22.5)))
195  ) {
196  // compute the mean position between both edges ends ...
197  Position p;
198  if (myExtended.find(*ccwi) != myExtended.end()) {
199  p = geomsCCW[*ccwi][0];
200  p.add(geomsCW[*ccwi][0]);
201  p.mul(0.5);
202 #ifdef DEBUG_NODE_SHAPE
203  if (DEBUGCOND) {
204  std::cout << " extended: p=" << p << " angle=" << (ccad - cad) << "\n";
205  }
206 #endif
207  } else {
208  p = geomsCCW[*ccwi][0];
209  p.add(geomsCW[*ccwi][0]);
210  p.add(geomsCCW[*i][0]);
211  p.add(geomsCW[*i][0]);
212  p.mul(0.25);
213 #ifdef DEBUG_NODE_SHAPE
214  if (DEBUGCOND) {
215  std::cout << " unextended: p=" << p << " angle=" << (ccad - cad) << "\n";
216  }
217 #endif
218  }
219  // ... compute the distance to this point ...
220  double dist = MAX2(
221  geomsCCW[*i].nearest_offset_to_point2D(p),
222  geomsCW[*i].nearest_offset_to_point2D(p));
223  if (dist < 0) {
224  // ok, we have the problem that even the extrapolated geometry
225  // does not reach the point
226  // in this case, the geometry has to be extenden... too bad ...
227  // ... let's append the mean position to the geometry
228  PositionVector g = (*i)->getGeometry();
229  if (myNode.hasIncoming(*i)) {
231  } else {
233  }
234  (*i)->setGeometry(g);
235  // and rebuild previous information
236  geomsCCW[*i] = (*i)->getCCWBoundaryLine(myNode);
237  geomsCCW[*i].extrapolate(EXT);
238  geomsCW[*i] = (*i)->getCWBoundaryLine(myNode);
239  geomsCW[*i].extrapolate(EXT);
240  // the distance is now = zero (the point we have appended)
241  distances[*i] = EXT;
242  myExtended[*i] = true;
243 #ifdef DEBUG_NODE_SHAPE
244  if (DEBUGCOND) {
245  std::cout << " extending (dist=" << dist << ")\n";
246  }
247 #endif
248  } else {
249  if (!simpleContinuation) {
250  dist += myRadius;
251  } else {
252  // if the angles change, junction should have some size to avoid degenerate shape
253  double radius2 = fabs(ccad - cad) * (*i)->getNumLanes();
254  if (radius2 > NUMERICAL_EPS || openDriveOutput) {
255  radius2 = MAX2(0.15, radius2);
256  }
257  if (myNode.getCrossings().size() > 0) {
258  double width = myNode.getCrossings()[0]->customWidth;
259  if (width == NBEdge::UNSPECIFIED_WIDTH) {
260  width = OptionsCont::getOptions().getFloat("default.crossing-width");
261  }
262  radius2 = MAX2(radius2, width / 2);
263  }
264  dist += radius2;
265 #ifdef DEBUG_NODE_SHAPE
266  if (DEBUGCOND) {
267  std::cout << " using radius=" << radius2 << " ccad=" << ccad << " cad=" << cad << "\n";
268  }
269 #endif
270  }
271  distances[*i] = dist;
272  }
273 
274  } else {
275  // the angles are different enough to compute the intersection of
276  // the outer boundaries directly (or there are more than 2 directions). The "nearer" neighbor causes the furthest distance
277  const bool ccwCloser = ccad < cad;
278  const bool cwLargeTurn = needsLargeTurn(*i, *cwi, same);
279  const bool ccwLargeTurn = needsLargeTurn(*i, *ccwi, same);
280  const bool neighLargeTurn = ccwCloser ? ccwLargeTurn : cwLargeTurn;
281  const bool neigh2LargeTurn = ccwCloser ? cwLargeTurn : ccwLargeTurn;
282  // the border facing the closer neighbor
283  const PositionVector& currGeom = ccwCloser ? geomsCCW[*i] : geomsCW[*i];
284  // the border facing the far neighbor
285  const PositionVector& currGeom2 = ccwCloser ? geomsCW[*i] : geomsCCW[*i];
286  // the border of the closer neighbor
287  const PositionVector& neighGeom = ccwCloser ? geomsCW[*ccwi] : geomsCCW[*cwi];
288  // the border of the far neighbor
289  const PositionVector& neighGeom2 = ccwCloser ? geomsCCW[*cwi] : geomsCW[*ccwi];
290 #ifdef DEBUG_NODE_SHAPE
291  if (DEBUGCOND) {
292  std::cout << " i=" << (*i)->getID() << " neigh=" << (*ccwi)->getID() << " neigh2=" << (*cwi)->getID() << "\n";
293  std::cout << " ccwCloser=" << ccwCloser
294  << "\n currGeom=" << currGeom << " neighGeom=" << neighGeom
295  << "\n currGeom2=" << currGeom2 << " neighGeom2=" << neighGeom2
296  << "\n";
297  }
298 #endif
299  if (!simpleContinuation) {
300  if (currGeom.intersects(neighGeom)) {
301  distances[*i] = (neighLargeTurn ? myRadius : smallRadius) + closestIntersection(currGeom, neighGeom, EXT);
302 #ifdef DEBUG_NODE_SHAPE
303  if (DEBUGCOND) {
304  std::cout << " neigh intersects dist=" << distances[*i] << " currGeom=" << currGeom << " neighGeom=" << neighGeom << "\n";
305  }
306 #endif
307  if (*cwi != *ccwi && currGeom2.intersects(neighGeom2)) {
308  // also use the second intersection point
309  // but prevent very large node shapes
310  const double farAngleDist = ccwCloser ? cad : ccad;
311  double a1 = distances[*i];
312  double a2 = (neigh2LargeTurn ? myRadius : smallRadius) + closestIntersection(currGeom2, neighGeom2, EXT);
313 #ifdef DEBUG_NODE_SHAPE
314  if (DEBUGCOND) {
315  std::cout << " neigh2 also intersects a1=" << a1 << " a2=" << a2 << " ccad=" << RAD2DEG(ccad) << " cad=" << RAD2DEG(cad) << " dist[cwi]=" << distances[*cwi] << " dist[ccwi]=" << distances[*ccwi] << " farAngleDist=" << RAD2DEG(farAngleDist) << " currGeom2=" << currGeom2 << " neighGeom2=" << neighGeom2 << "\n";
316  }
317 #endif
318  //if (RAD2DEG(farAngleDist) < 175) {
319  // distances[*i] = MAX2(a1, MIN2(a2, a1 + 180 - RAD2DEG(farAngleDist)));
320  //}
321  if (a2 <= EXT) {
322  distances[*i] = MAX2(a1, a2);
323  } else if (ccad > DEG2RAD(90. + 45.) && cad > DEG2RAD(90. + 45.)) {
324  // do nothing.
325  } else if (farAngleDist < DEG2RAD(135) || (fabs(RAD2DEG(farAngleDist) - 180) > 1 && fabs(a2 - a1) < 10)) {
326  distances[*i] = MAX2(a1, a2);
327  }
328 #ifdef DEBUG_NODE_SHAPE
329  if (DEBUGCOND) {
330  std::cout << " a1=" << a1 << " a2=" << a2 << " dist=" << distances[*i] << "\n";
331  }
332 #endif
333  }
334  } else {
335  if (*cwi != *ccwi && currGeom2.intersects(neighGeom2)) {
336  distances[*i] = (neigh2LargeTurn ? myRadius : smallRadius) + currGeom2.intersectsAtLengths2D(neighGeom2)[0];
337 #ifdef DEBUG_NODE_SHAPE
338  if (DEBUGCOND) {
339  std::cout << " neigh2 intersects dist=" << distances[*i] << " currGeom2=" << currGeom2 << " neighGeom2=" << neighGeom2 << "\n";
340  }
341 #endif
342  } else {
343  distances[*i] = EXT + myRadius;
344 #ifdef DEBUG_NODE_SHAPE
345  if (DEBUGCOND) {
346  std::cout << " no intersects dist=" << distances[*i] << " currGeom=" << currGeom << " neighGeom=" << neighGeom << " currGeom2=" << currGeom2 << " neighGeom2=" << neighGeom2 << "\n";
347  }
348 #endif
349  }
350  }
351  } else {
352  if (currGeom.intersects(neighGeom)) {
353  distances[*i] = currGeom.intersectsAtLengths2D(neighGeom)[0];
354  } else {
355  distances[*i] = (double) EXT;
356  }
357  }
358  }
359  if (useDefaultRadius && sCurveStretch > 0) {
360  double sCurveWidth = myNode.getDisplacementError();
361  if (sCurveWidth > 0) {
362  const double sCurveRadius = myRadius + sCurveWidth / SUMO_const_laneWidth * sCurveStretch * pow((*i)->getSpeed(), 2 + sCurveStretch) / 1000;
363  const double stretch = EXT + sCurveRadius - distances[*i];
364  if (stretch > 0) {
365  distances[*i] += stretch;
366  // fixate extended geometry for repeated computation
367  const double shorten = distances[*i] - EXT;
368  (*i)->shortenGeometryAtNode(&myNode, shorten);
369  for (std::set<NBEdge*>::iterator k = same[*i].begin(); k != same[*i].end(); ++k) {
370  (*k)->shortenGeometryAtNode(&myNode, shorten);
371  }
372 #ifdef DEBUG_NODE_SHAPE
373  if (DEBUGCOND) {
374  std::cout << " stretching junction: sCurveWidth=" << sCurveWidth << " sCurveRadius=" << sCurveRadius << " stretch=" << stretch << " dist=" << distances[*i] << "\n";
375  }
376 #endif
377  }
378  }
379  }
380  }
381 
382  for (NBEdge* const edge : newAll) {
383  if (distances.find(edge) == distances.end()) {
384  assert(false);
385  distances[edge] = EXT;
386  }
387  }
388  // because of lane spread right the crossing point may be identical to the junction center and thus the distance is exactly EXT
389  const double off = EXT - NUMERICAL_EPS;
390  // prevent inverted node shapes
391  // (may happen with near-parallel edges)
392  const double minDistSum = 2 * (EXT + myRadius);
393  for (NBEdge* const edge : newAll) {
394  if (distances[edge] < off && edge->hasDefaultGeometryEndpointAtNode(&myNode)) {
395  for (EdgeVector::const_iterator j = newAll.begin(); j != newAll.end(); ++j) {
396  if (distances[*j] > off && (*j)->hasDefaultGeometryEndpointAtNode(&myNode) && distances[edge] + distances[*j] < minDistSum) {
397  const double angleDiff = fabs(NBHelpers::relAngle(edge->getAngleAtNode(&myNode), (*j)->getAngleAtNode(&myNode)));
398  if (angleDiff > 160 || angleDiff < 20) {
399 #ifdef DEBUG_NODE_SHAPE
400  if (DEBUGCOND) {
401  std::cout << " increasing dist for i=" << edge->getID() << " because of j=" << (*j)->getID() << " jDist=" << distances[*j]
402  << " oldI=" << distances[edge] << " newI=" << minDistSum - distances[*j]
403  << " angleDiff=" << angleDiff
404  << " geomI=" << edge->getGeometry() << " geomJ=" << (*j)->getGeometry() << "\n";
405  }
406 #endif
407  distances[edge] = minDistSum - distances[*j];
408  }
409  }
410  }
411  }
412  }
413 
414 
415  // build
416  PositionVector ret;
417  for (i = newAll.begin(); i != newAll.end(); ++i) {
418  const PositionVector& ccwBound = geomsCCW[*i];
419  const PositionVector& cwBound = geomsCW[*i];
420  //double offset = MIN3(distances[*i], cwBound.length2D() - POSITION_EPS, ccwBound.length2D() - POSITION_EPS);
421  double offset = distances[*i];
422  if (!(*i)->hasDefaultGeometryEndpointAtNode(&myNode)) {
423  // for non geometry-endpoints, only shorten but never extend the geometry
424  if (advanceStopLine > 0 && offset < EXT) {
425 #ifdef DEBUG_NODE_SHAPE
426  std::cout << " i=" << (*i)->getID() << " offset=" << offset << " advanceStopLine=" << advanceStopLine << "\n";
427 #endif
428  // fixate extended geometry for repeated computation
429  (*i)->extendGeometryAtNode(&myNode, advanceStopLine);
430  for (std::set<NBEdge*>::iterator k = same[*i].begin(); k != same[*i].end(); ++k) {
431  (*k)->extendGeometryAtNode(&myNode, advanceStopLine);
432  }
433  }
434  offset = MAX2(EXT - advanceStopLine, offset);
435  }
436  if (offset == -1) {
437  WRITE_WARNING("Fixing offset for edge '" + (*i)->getID() + "' at node '" + myNode.getID() + ".");
438  offset = (double) - .1;
439  }
440  Position p = ccwBound.positionAtOffset2D(offset);
441  p.setz(myNode.getPosition().z());
442  if (i != newAll.begin()) {
443  ret.append(getSmoothCorner(geomsCW[*(i - 1)], ccwBound, ret[-1], p, cornerDetail));
444  }
445  ret.push_back_noDoublePos(p);
446  //
447  Position p2 = cwBound.positionAtOffset2D(offset);
448  p2.setz(myNode.getPosition().z());
449  ret.push_back_noDoublePos(p2);
450 #ifdef DEBUG_NODE_SHAPE
451  if (DEBUGCOND) {
452  std::cout << " build stopLine for i=" << (*i)->getID() << " offset=" << offset << " dist=" << distances[*i] << " cwLength=" << cwBound.length2D() << " ccwLength=" << ccwBound.length2D() << " p=" << p << " p2=" << p2 << " ccwBound=" << ccwBound << " cwBound=" << cwBound << "\n";
453  }
454 #endif
455  (*i)->setNodeBorder(&myNode, p, p2, rectangularCut);
456  for (std::set<NBEdge*>::iterator k = same[*i].begin(); k != same[*i].end(); ++k) {
457  (*k)->setNodeBorder(&myNode, p, p2, rectangularCut);
458  }
459  }
460  // final curve segment
461  ret.append(getSmoothCorner(geomsCW[*(newAll.end() - 1)], geomsCCW[*newAll.begin()], ret[-1], ret[0], cornerDetail));
462 #ifdef DEBUG_NODE_SHAPE
463  if (DEBUGCOND) {
464  std::cout << " final shape=" << ret << "\n";
465  }
466 #endif
467  return ret;
468 }
469 
470 
471 double
472 NBNodeShapeComputer::closestIntersection(const PositionVector& geom1, const PositionVector& geom2, double offset) {
473  std::vector<double> intersections = geom1.intersectsAtLengths2D(geom2);
474  double result = intersections[0];
475  for (std::vector<double>::iterator it = intersections.begin() + 1; it != intersections.end(); ++it) {
476  if (fabs(*it - offset) < fabs(result - offset)) {
477  result = *it;
478  }
479  }
480  return result;
481 }
482 
483 bool
485  std::map<NBEdge*, std::set<NBEdge*> >& same) const {
486  const SVCPermissions p1 = e1->getPermissions();
487  const SVCPermissions p2 = e2->getPermissions();
488  if ((p1 & p2 & SVC_LARGE_TURN) != 0) {
489  // note: would could also check whether there is actually a connection
490  // between those edges
491  return true;
492  }
493  // maybe edges in the same direction need a large turn
494  for (NBEdge* e2s : same[e2]) {
495  if ((p1 & e2s->getPermissions() & SVC_LARGE_TURN) != 0
496  && (e1->getToNode() == e2s->getFromNode() || e2s->getToNode() == e1->getFromNode())) {
497  return true;
498  }
499  for (NBEdge* e1s : same[e1]) {
500  if ((e2s->getPermissions() & e1s->getPermissions() & SVC_LARGE_TURN) != 0
501  && (e2s->getToNode() == e1s->getFromNode() || e1s->getToNode() == e2s->getFromNode())) {
502  return true;
503  }
504  }
505  }
506  for (NBEdge* e1s : same[e1]) {
507  if ((p2 & e1s->getPermissions() & SVC_LARGE_TURN) != 0
508  && (e2->getToNode() == e1s->getFromNode() || e1s->getToNode() == e2->getFromNode())) {
509  return true;
510  }
511  }
512  //std::cout << " e1=" << e1->getID() << " e2=" << e2->getID() << " sameE1=" << toString(same[e1]) << " sameE2=" << toString(same[e2]) << "\n";
513  return false;
514 }
515 
518  const Position& begPoint, const Position& endPoint, int cornerDetail) {
519  PositionVector ret;
520  if (cornerDetail > 0) {
521  PositionVector begShape2 = begShape.reverse().getSubpart2D(EXT2, begShape.length());
522  const double begSplit = begShape2.nearest_offset_to_point2D(begPoint, false);
523 #ifdef DEBUG_SMOOTH_CORNERS
524  if (DEBUGCOND) {
525  std::cout << " begLength=" << begShape2.length2D() << " begSplit=" << begSplit << "\n";
526  }
527 #endif
528  if (begSplit > POSITION_EPS && begSplit < begShape2.length2D() - POSITION_EPS) {
529  begShape2 = begShape2.splitAt(begSplit, true).first;
530  } else {
531  return ret;
532  }
533  PositionVector endShape2 = endShape.getSubpart(0, endShape.length() - EXT2);
534  const double endSplit = endShape2.nearest_offset_to_point2D(endPoint, false);
535 #ifdef DEBUG_SMOOTH_CORNERS
536  if (DEBUGCOND) {
537  std::cout << " endLength=" << endShape2.length2D() << " endSplit=" << endSplit << "\n";
538  }
539 #endif
540  if (endSplit > POSITION_EPS && endSplit < endShape2.length2D() - POSITION_EPS) {
541  endShape2 = endShape2.splitAt(endSplit, true).second;
542  } else {
543  return ret;
544  }
545  // flatten z to junction z level
546  begShape2 = begShape2.interpolateZ(myNode.getPosition().z(), myNode.getPosition().z());
547  endShape2 = endShape2.interpolateZ(myNode.getPosition().z(), myNode.getPosition().z());
548 #ifdef DEBUG_SMOOTH_CORNERS
549  if (DEBUGCOND) {
550  std::cout << "getSmoothCorner begPoint=" << begPoint << " endPoint=" << endPoint
551  << " begShape=" << begShape << " endShape=" << endShape
552  << " begShape2=" << begShape2 << " endShape2=" << endShape2
553  << "\n";
554  }
555 #endif
556  if (begShape2.size() < 2 || endShape2.size() < 2) {
557  return ret;
558  }
559  const double angle = GeomHelper::angleDiff(begShape2.angleAt2D(-2), endShape2.angleAt2D(0));
560  NBNode* recordError = nullptr;
561 #ifdef DEBUG_SMOOTH_CORNERS
562  if (DEBUGCOND) {
563  std::cout << " angle=" << RAD2DEG(angle) << "\n";
564  }
565  recordError = const_cast<NBNode*>(&myNode);
566 #endif
567  // fill highly acute corners
568  //if (fabs(angle) > DEG2RAD(135)) {
569  // return ret;
570  //}
571  PositionVector curve = myNode.computeSmoothShape(begShape2, endShape2, cornerDetail + 2, false, 25, 25, recordError, NBNode::AVOID_WIDE_LEFT_TURN);
572  //PositionVector curve = myNode.computeSmoothShape(begShape2, endShape2, cornerDetail + 2, false, 25, 25, recordError, 0);
573  const double curvature = curve.length2D() / MAX2(NUMERICAL_EPS, begPoint.distanceTo2D(endPoint));
574 #ifdef DEBUG_SMOOTH_CORNERS
575  if (DEBUGCOND) {
576  std::cout << " curve=" << curve << " curveLength=" << curve.length2D() << " dist=" << begPoint.distanceTo2D(endPoint) << " curvature=" << curvature << "\n";
577  }
578 #endif
579  if (curvature > 2 && angle > DEG2RAD(85)) {
580  // simplify dubious inside corner shape
581  return ret;
582  }
583  if (curve.size() > 2) {
584  curve.erase(curve.begin());
585  curve.pop_back();
586  ret = curve;
587  }
588  }
589  return ret;
590 }
591 
592 void
594  GeomsMap& geomsCCW,
595  GeomsMap& geomsCW) {
596  // compute boundary lines and extend it by EXT m
597  for (NBEdge* const edge : edges) {
598  // store current edge's boundary as current ccw/cw boundary
599  try {
600  geomsCCW[edge] = edge->getCCWBoundaryLine(myNode);
601  } catch (InvalidArgument& e) {
602  WRITE_WARNING("While computing intersection geometry at junction '" + myNode.getID() + "': " + std::string(e.what()));
603  geomsCCW[edge] = edge->getGeometry();
604  }
605  try {
606  geomsCW[edge] = edge->getCWBoundaryLine(myNode);
607  } catch (InvalidArgument& e) {
608  WRITE_WARNING("While computing intersection geometry at junction '" + myNode.getID() + "': " + std::string(e.what()));
609  geomsCW[edge] = edge->getGeometry();
610  }
611  // ensure the boundary is valid
612  if (geomsCCW[edge].length2D() < NUMERICAL_EPS) {
613  geomsCCW[edge] = edge->getGeometry();
614  }
615  if (geomsCW[edge].length2D() < NUMERICAL_EPS) {
616  geomsCW[edge] = edge->getGeometry();
617  }
618  // extend the boundary by extrapolating it by EXT m
619  geomsCCW[edge].extrapolate2D(EXT, true);
620  geomsCW[edge].extrapolate2D(EXT, true);
621  geomsCCW[edge].extrapolate(EXT2, false, true);
622  geomsCW[edge].extrapolate(EXT2, false, true);
623  }
624 }
625 
626 void
627 NBNodeShapeComputer::joinSameDirectionEdges(const EdgeVector& edges, std::map<NBEdge*, std::set<NBEdge*> >& same) {
628  // compute same (edges where an intersection doesn't work well
629  // (always check an edge and its cw neighbor)
630  const double angleChangeLookahead = 35; // distance to look ahead for a misleading angle
631  EdgeSet foundOpposite;
632  for (EdgeVector::const_iterator i = edges.begin(); i != edges.end(); i++) {
633  EdgeVector::const_iterator j;
634  if (i == edges.end() - 1) {
635  j = edges.begin();
636  } else {
637  j = i + 1;
638  }
639  const bool incoming = (*i)->getToNode() == &myNode;
640  const bool incoming2 = (*j)->getToNode() == &myNode;
641  const bool differentDirs = (incoming != incoming2);
642  const bool sameGeom = (*i)->getGeometry() == (differentDirs ? (*j)->getGeometry().reverse() : (*j)->getGeometry());
643  const PositionVector g1 = incoming ? (*i)->getCCWBoundaryLine(myNode) : (*i)->getCWBoundaryLine(myNode);
644  const PositionVector g2 = incoming ? (*j)->getCCWBoundaryLine(myNode) : (*j)->getCWBoundaryLine(myNode);
645  const double angle1further = (g1.size() > 2 && g1[0].distanceTo2D(g1[1]) < angleChangeLookahead ?
646  g1.angleAt2D(1) : g1.angleAt2D(0));
647  const double angle2further = (g2.size() > 2 && g2[0].distanceTo2D(g2[1]) < angleChangeLookahead ?
648  g2.angleAt2D(1) : g2.angleAt2D(0));
649  const double angleDiff = GeomHelper::angleDiff(g1.angleAt2D(0), g2.angleAt2D(0));
650  const double angleDiffFurther = GeomHelper::angleDiff(angle1further, angle2further);
651  const bool ambiguousGeometry = ((angleDiff > 0 && angleDiffFurther < 0) || (angleDiff < 0 && angleDiffFurther > 0));
652  //if (ambiguousGeometry) {
653  // @todo: this warning would be helpful in many cases. However, if angle and angleFurther jump between 179 and -179 it is misleading
654  // WRITE_WARNING("Ambiguous angles at junction '" + myNode.getID() + "' for edges '" + (*i)->getID() + "' and '" + (*j)->getID() + "'.");
655  //}
656 #ifdef DEBUG_NODE_SHAPE
657  if (DEBUGCOND) {
658  std::cout << " checkSameDirection " << (*i)->getID() << " " << (*j)->getID()
659  << " diffDirs=" << differentDirs
660  << " isOpposite=" << (differentDirs && foundOpposite.count(*i) == 0)
661  << " angleDiff=" << angleDiff
662  << " ambiguousGeometry=" << ambiguousGeometry
663  << badIntersection(*i, *j, EXT)
664  << "\n";
665 
666  }
667 #endif
668  if (sameGeom || fabs(angleDiff) < DEG2RAD(20)) {
669  const bool isOpposite = differentDirs && foundOpposite.count(*i) == 0;
670  if (isOpposite) {
671  foundOpposite.insert(*i);
672  foundOpposite.insert(*j);
673  }
674  if (isOpposite || ambiguousGeometry || badIntersection(*i, *j, EXT)) {
675  // maintain equivalence relation for all members of the equivalence class
676  for (std::set<NBEdge*>::iterator k = same[*i].begin(); k != same[*i].end(); ++k) {
677  if (*j != *k) {
678  same[*k].insert(*j);
679  same[*j].insert(*k);
680  }
681  }
682  for (std::set<NBEdge*>::iterator k = same[*j].begin(); k != same[*j].end(); ++k) {
683  if (*i != *k) {
684  same[*k].insert(*i);
685  same[*i].insert(*k);
686  }
687  }
688  same[*i].insert(*j);
689  same[*j].insert(*i);
690 #ifdef DEBUG_NODE_SHAPE
691  if (DEBUGCOND) {
692  std::cout << " joinedSameDirectionEdges " << (*i)->getID() << " " << (*j)->getID() << " isOpposite=" << isOpposite << " ambiguousGeometry=" << ambiguousGeometry << "\n";
693  }
694 #endif
695  }
696  }
697  }
698 }
699 
700 
701 bool
702 NBNodeShapeComputer::badIntersection(const NBEdge* e1, const NBEdge* e2, double distance) {
703  // check whether the two edges are on top of each other. In that case they should be joined
704  // also, if they never touch along their common length
705  const double commonLength = MIN3(distance, e1->getGeometry().length(), e2->getGeometry().length());
706  PositionVector geom1 = e1->getGeometry();
707  PositionVector geom2 = e2->getGeometry();
708  // shift to make geom the centerline of the edge regardless of spreadtype
710  geom1.move2side(e1->getTotalWidth() / 2);
711  }
713  geom2.move2side(e2->getTotalWidth() / 2);
714  }
715  // always let geometry start at myNode
716  if (e1->getToNode() == &myNode) {
717  geom1 = geom1.reverse();
718  }
719  if (e2->getToNode() == &myNode) {
720  geom2 = geom2.reverse();
721  }
722  geom1 = geom1.getSubpart2D(0, commonLength);
723  geom2 = geom2.getSubpart2D(0, commonLength);
724  double endAngleDiff = 0;
725  if (geom1.size() >= 2 && geom2.size() >= 2) {
726  endAngleDiff = fabs(RAD2DEG(GeomHelper::angleDiff(
727  geom1.angleAt2D((int)geom1.size() - 2),
728  geom2.angleAt2D((int)geom2.size() - 2))));
729  }
730  const double minDistanceThreshold = (e1->getTotalWidth() + e2->getTotalWidth()) / 2 + POSITION_EPS;
731  std::vector<double> distances = geom1.distances(geom2, true);
732  const double minDist = VectorHelper<double>::minValue(distances);
733  const double maxDist = VectorHelper<double>::maxValue(distances);
734  const bool curvingTowards = geom1[0].distanceTo2D(geom2[0]) > minDistanceThreshold && minDist < minDistanceThreshold;
735  const bool onTop = (maxDist - POSITION_EPS < minDistanceThreshold) && endAngleDiff < 30;
736  geom1.extrapolate2D(EXT);
737  geom2.extrapolate2D(EXT);
738  Position intersect = geom1.intersectionPosition2D(geom2);
739  const bool intersects = intersect != Position::INVALID && geom1.distance2D(intersect) < POSITION_EPS;
740 #ifdef DEBUG_NODE_SHAPE
741  if (DEBUGCOND) {
742  std::cout << " badIntersect: onTop=" << onTop << " curveTo=" << curvingTowards << " intersects=" << intersects
743  << " endAngleDiff=" << endAngleDiff
744  << " geom1=" << geom1 << " geom2=" << geom2
745  << " distances=" << toString(distances) << " minDist=" << minDist << " maxDist=" << maxDist << " thresh=" << minDistanceThreshold
746  << " intersectPos=" << intersect
747  << "\n";
748  }
749 #endif
750  return onTop || curvingTowards || !intersects;
751 }
752 
753 
756  const EdgeVector& all,
757  std::map<NBEdge*, std::set<NBEdge*> >& same,
758  GeomsMap& geomsCCW,
759  GeomsMap& geomsCW) {
760  // store relationships
761  EdgeVector newAll = all;
762  for (NBEdge* e1 : all) {
763  // determine which of the edges marks the outer boundary
764  auto e2NewAll = std::find(newAll.begin(), newAll.end(), e1);
765 #ifdef DEBUG_NODE_SHAPE
766  if (DEBUGCOND) std::cout << "computeUniqueDirectionList e1=" << e1->getID()
767  << " deleted=" << (e2NewAll == newAll.end())
768  << " same=" << joinNamedToStringSorting(same[e1], ',') << "\n";
769 #endif
770  if (e2NewAll == newAll.end()) {
771  continue;
772  }
773  auto e1It = std::find(all.begin(), all.end(), e1);
774  auto bestCCW = e1It;
775  auto bestCW = e1It;
776  bool changed = true;
777  while (changed) {
778  changed = false;
779  for (NBEdge* e2 : same[e1]) {
780 #ifdef DEBUG_NODE_SHAPE
781  if (DEBUGCOND) {
782  std::cout << " e2=" << e2->getID() << "\n";
783  }
784 #endif
785  auto e2It = std::find(all.begin(), all.end(), e2);
786  if (e2It + 1 == bestCCW || (e2It == (all.end() - 1) && bestCCW == all.begin())) {
787  bestCCW = e2It;
788  changed = true;
789 #ifdef DEBUG_NODE_SHAPE
790  if (DEBUGCOND) {
791  std::cout << " bestCCW=" << e2->getID() << "\n";
792  }
793 #endif
794  } else if (bestCW + 1 == e2It || (bestCW == (all.end() - 1) && e2It == all.begin())) {
795  bestCW = e2It;
796  changed = true;
797 #ifdef DEBUG_NODE_SHAPE
798  if (DEBUGCOND) {
799  std::cout << " bestCW=" << e2->getID() << "\n";
800  }
801 #endif
802  }
803  }
804  }
805  if (bestCW != e1It) {
806  geomsCW[e1] = geomsCW[*bestCW];
807  computeSameEnd(geomsCW[e1], geomsCCW[e1]);
808  }
809  if (bestCCW != e1It) {
810  geomsCCW[e1] = geomsCCW[*bestCCW];
811  computeSameEnd(geomsCW[e1], geomsCCW[e1]);
812  }
813  // clean up
814  for (NBEdge* e2 : same[e1]) {
815  auto e2NewAllIt = std::find(newAll.begin(), newAll.end(), e2);
816  if (e2NewAllIt != newAll.end()) {
817  newAll.erase(e2NewAllIt);
818  }
819  }
820  }
821 #ifdef DEBUG_NODE_SHAPE
822  if (DEBUGCOND) {
823  std::cout << " newAll:\n";
824  for (NBEdge* e : newAll) {
825  std::cout << " " << e->getID() << " geomCCW=" << geomsCCW[e] << " geomsCW=" << geomsCW[e] << "\n";
826  }
827  }
828 #endif
829  return newAll;
830 }
831 
832 
833 void
834 NBNodeShapeComputer::initNeighbors(const EdgeVector& edges, const EdgeVector::const_iterator& current,
835  GeomsMap& geomsCW,
836  GeomsMap& geomsCCW,
837  EdgeVector::const_iterator& cwi,
838  EdgeVector::const_iterator& ccwi,
839  double& cad,
840  double& ccad) {
841  const double twoPI = (double)(2 * M_PI);
842  cwi = current;
843  cwi++;
844  if (cwi == edges.end()) {
845  std::advance(cwi, -((int)edges.size())); // set to edges.begin();
846  }
847  ccwi = current;
848  if (ccwi == edges.begin()) {
849  std::advance(ccwi, edges.size() - 1); // set to edges.end() - 1;
850  } else {
851  ccwi--;
852  }
853 
854  const double angleCurCCW = geomsCCW[*current].angleAt2D(0);
855  const double angleCurCW = geomsCW[*current].angleAt2D(0);
856  const double angleCCW = geomsCW[*ccwi].angleAt2D(0);
857  const double angleCW = geomsCCW[*cwi].angleAt2D(0);
858  ccad = angleCCW - angleCurCCW;
859  while (ccad < 0.) {
860  ccad += twoPI;
861  }
862  cad = angleCurCW - angleCW;
863  while (cad < 0.) {
864  cad += twoPI;
865  }
866 }
867 
868 
869 
870 const PositionVector
872 #ifdef DEBUG_NODE_SHAPE
873  if (DEBUGCOND) {
874  std::cout << "computeNodeShapeSmall node=" << myNode.getID() << "\n";
875  }
876 #endif
877  PositionVector ret;
878  for (NBEdge* e : myNode.getEdges()) {
879  // compute crossing with normal
880  PositionVector edgebound1 = e->getCCWBoundaryLine(myNode).getSubpartByIndex(0, 2);
881  PositionVector edgebound2 = e->getCWBoundaryLine(myNode).getSubpartByIndex(0, 2);
882  Position delta = edgebound1[1] - edgebound1[0];
883  delta.set(-delta.y(), delta.x()); // rotate 90 degrees
884  PositionVector cross(myNode.getPosition(), myNode.getPosition() + delta);
885  cross.extrapolate2D(500);
886  edgebound1.extrapolate2D(500);
887  edgebound2.extrapolate2D(500);
888  if (cross.intersects(edgebound1)) {
889  Position np = cross.intersectionPosition2D(edgebound1);
890  np.set(np.x(), np.y(), myNode.getPosition().z());
891  ret.push_back_noDoublePos(np);
892  }
893  if (cross.intersects(edgebound2)) {
894  Position np = cross.intersectionPosition2D(edgebound2);
895  np.set(np.x(), np.y(), myNode.getPosition().z());
896  ret.push_back_noDoublePos(np);
897  }
898  e->resetNodeBorder(&myNode);
899  }
900  return ret;
901 }
902 
903 
904 double
906  // look for incoming/outgoing edge pairs that do not go straight and allow wide vehicles
907  // (connection information is not available yet)
908  // @TODO compute the radius for each pair of neighboring edge intersections in computeNodeShapeDefault rather than use the maximum
909  const double radius = oc.getFloat("default.junctions.radius");
910  const double smallRadius = oc.getFloat("junctions.small-radius");
911  double maxRightAngle = 0; // rad
912  double extraWidthRight = 0; // m
913  double maxLeftAngle = 0; // rad
914  double extraWidthLeft = 0; // m
915  int laneDelta = 0;
916  int totalWideLanesIn = 0;
917  for (NBEdge* in : myNode.getIncomingEdges()) {
918  int wideLanesIn = 0;
919  for (int i = 0; i < in->getNumLanes(); i++) {
920  if ((in->getPermissions(i) & SVC_LARGE_TURN) != 0) {
921  wideLanesIn++;
922  }
923  }
924  totalWideLanesIn += wideLanesIn;
925  for (NBEdge* out : myNode.getOutgoingEdges()) {
926  if ((in->getPermissions() & out->getPermissions() & SVC_LARGE_TURN) != 0) {
927  if (myNode.getDirection(in, out) == LinkDirection::TURN) {
928  continue;
929  };
930  const double angle = GeomHelper::angleDiff(
931  in->getGeometry().angleAt2D(-2),
932  out->getGeometry().angleAt2D(0));
933  if (angle < 0) {
934  if (maxRightAngle < -angle) {
935  maxRightAngle = -angle;
936  extraWidthRight = MAX2(getExtraWidth(in, SVC_LARGE_TURN), getExtraWidth(out, SVC_LARGE_TURN));
937  }
938  } else {
939  if (maxLeftAngle < angle) {
940  maxLeftAngle = angle;
941  // all edges clockwise between in and out count as extra width
942  extraWidthLeft = 0;
943  EdgeVector::const_iterator pIn = std::find(myNode.getEdges().begin(), myNode.getEdges().end(), in);
945  while (*pIn != out) {
946  extraWidthLeft += (*pIn)->getTotalWidth();
947 #ifdef DEBUG_RADIUS
948  if (DEBUGCOND) {
949  std::cout << " in=" << in->getID() << " out=" << out->getID() << " extra=" << (*pIn)->getID() << " extraWidthLeft=" << extraWidthLeft << "\n";
950  }
951 #endif
953  }
954  }
955  }
956  int wideLanesOut = 0;
957  for (int i = 0; i < out->getNumLanes(); i++) {
958  if ((out->getPermissions(i) & SVC_LARGE_TURN) != 0) {
959  wideLanesOut++;
960  }
961  }
962 #ifdef DEBUG_RADIUS
963  if (DEBUGCOND) {
964  std::cout << " in=" << in->getID() << " out=" << out->getID() << " wideLanesIn=" << wideLanesIn << " wideLanesOut=" << wideLanesOut << "\n";
965  }
966 #endif
967  laneDelta = MAX2(laneDelta, abs(wideLanesOut - wideLanesIn));
968  }
969  }
970  }
971  // special case: on/off-ramp
972  if (myNode.getOutgoingEdges().size() == 1 || myNode.getIncomingEdges().size() == 1) {
973  int totalWideLanesOut = 0;
974  for (NBEdge* out : myNode.getOutgoingEdges()) {
975  for (int i = 0; i < out->getNumLanes(); i++) {
976  if ((out->getPermissions(i) & SVC_LARGE_TURN) != 0) {
977  totalWideLanesOut++;
978  }
979  }
980  }
981  if (totalWideLanesIn == totalWideLanesOut) {
982  // use total laneDelta instead of individual edge lane delta
983  laneDelta = 0;
984  }
985  }
986  // changing the number of wide-vehicle lanes on a straight segment requires a larger junction to allow for smooth driving
987  // otherwise we can reduce the radius according to the angle
988  double result = radius;
989  // left turns are assumed to cross additional edges and thus du not determine the required radius in most cases
990  double maxTurnAngle = maxRightAngle;
991  double extraWidth = extraWidthRight;
992  if (maxRightAngle < DEG2RAD(5)) {
993  maxTurnAngle = maxLeftAngle;
994  extraWidth = extraWidthLeft;
995  }
996  const double minRadius = maxTurnAngle >= DEG2RAD(30) ? MIN2(smallRadius, radius) : smallRadius;
997  if (laneDelta == 0 || maxTurnAngle >= DEG2RAD(30) || myNode.isConstantWidthTransition()) {
998  // subtract radius gained from extra lanes
999  // do not increase radius for turns that are sharper than a right angle
1000  result = radius * tan(0.5 * MIN2(0.5 * M_PI, maxTurnAngle)) - extraWidth;
1001  }
1002  result = MAX2(minRadius, result);
1003 #ifdef DEBUG_RADIUS
1004  if (DEBUGCOND) {
1005  std::cout << "getDefaultRadius n=" << myNode.getID()
1006  << " r=" << radius << " sr=" << smallRadius
1007  << " mr=" << minRadius
1008  << " laneDelta=" << laneDelta
1009  << " rightA=" << RAD2DEG(maxRightAngle)
1010  << " leftA=" << RAD2DEG(maxLeftAngle)
1011  << " maxA=" << RAD2DEG(maxTurnAngle)
1012  << " extraWidth=" << extraWidth
1013  << " result=" << result << "\n";
1014  }
1015 #endif
1016  return result;
1017 }
1018 
1019 
1020 double
1022  double result = 0;
1023  int lane = 0;
1024  while (lane < e->getNumLanes() && e->getPermissions(lane) == 0) {
1025  // ignore forbidden lanes out the outside
1026  lane++;
1027  }
1028  while (lane < e->getNumLanes() && (e->getPermissions(lane) & exclude) == 0) {
1029  result += e->getLaneWidth(lane);
1030  lane++;
1031  }
1032  return result;
1033 }
1034 
1035 
1036 /****************************************************************************/
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:280
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:35
#define EXT
#define DEBUGCOND
void computeSameEnd(PositionVector &l1, PositionVector &l2)
#define EXT2
const SVCPermissions SVCAll
all VClasses are allowed
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_PEDESTRIAN
pedestrian
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ TURN
The link is a 180 degree turn.
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
T MIN3(T a, T b, T c)
Definition: StdDefs.h:87
T MIN2(T a, T b)
Definition: StdDefs.h:74
T MAX2(T a, T b)
Definition: StdDefs.h:80
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
std::string joinNamedToStringSorting(const std::set< T * > &ns, const T_BETWEEN &between)
Definition: ToString.h:293
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
Definition: GeomHelper.cpp:179
static void nextCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
The representation of a single edge during network building.
Definition: NBEdge.h:91
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4022
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:630
LaneSpreadFunction getLaneSpreadFunction() const
Returns how this edge's lanes' lateral offset is computed.
Definition: NBEdge.cpp:942
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:541
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:752
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition: NBEdge.cpp:3876
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:349
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:534
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition: NBHelpers.cpp:45
Represents a single node (junction) during network building.
Definition: NBNode.h:66
LinkDirection getDirection(const NBEdge *const incoming, const NBEdge *const outgoing, bool leftHand=false) const
Returns the representation of the described stream's direction.
Definition: NBNode.cpp:2221
bool hasIncoming(const NBEdge *const e) const
Returns whether the given edge ends at this node.
Definition: NBNode.cpp:1744
double getDisplacementError() const
compute the displacement error during s-curve computation
Definition: NBNode.h:618
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition: NBNode.cpp:479
static const double UNSPECIFIED_RADIUS
unspecified lane width
Definition: NBNode.h:208
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:261
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:266
PositionVector computeSmoothShape(const PositionVector &begShape, const PositionVector &endShape, int numPoints, bool isTurnaround, double extrapolateBeg, double extrapolateEnd, NBNode *recordError=0, int shapeFlag=0) const
Compute a smooth curve between the given geometries.
Definition: NBNode.cpp:514
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:256
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition: NBNode.cpp:2725
bool isConstantWidthTransition() const
detects whether a given junction splits or merges lanes while keeping constant road width
Definition: NBNode.cpp:818
static const int AVOID_WIDE_LEFT_TURN
Definition: NBNode.h:212
const Position & getPosition() const
Definition: NBNode.h:248
double getRadius() const
Returns the turning radius of this node.
Definition: NBNode.h:278
PositionVector getSmoothCorner(PositionVector begShape, PositionVector endShape, const Position &begPoint, const Position &endPoint, int cornerDetail)
Compute smoothed corner shape.
double closestIntersection(const PositionVector &geom1, const PositionVector &geom2, double offset)
return the intersection point closest to the given offset
const PositionVector computeNodeShapeSmall()
Computes the node geometry using normals.
double myRadius
the computed node radius
EdgeVector computeUniqueDirectionList(const EdgeVector &all, std::map< NBEdge *, std::set< NBEdge * > > &same, GeomsMap &geomsCCW, GeomsMap &geomsCW)
Joins edges.
void computeEdgeBoundaries(const EdgeVector &edges, GeomsMap &geomsCCW, GeomsMap &geomsCW)
compute clockwise/counter-clockwise edge boundaries
std::map< NBEdge *, PositionVector > GeomsMap
NBNodeShapeComputer(const NBNode &node)
Constructor.
const PositionVector compute()
Computes the shape of the assigned junction.
~NBNodeShapeComputer()
Destructor.
bool badIntersection(const NBEdge *e1, const NBEdge *e2, double distance)
const PositionVector computeNodeShapeDefault(bool simpleContinuation)
Computes the node geometry Edges with the same direction are grouped. Then the node geometry is built...
const NBNode & myNode
The node to compute the geometry for.
void joinSameDirectionEdges(const EdgeVector &edges, std::map< NBEdge *, std::set< NBEdge * > > &same)
Joins edges and computes ccw/cw boundaries.
double getDefaultRadius(const OptionsCont &oc)
determine the default radius appropriate for the current junction
static void initNeighbors(const EdgeVector &edges, const EdgeVector::const_iterator &current, GeomsMap &geomsCW, GeomsMap &geomsCCW, EdgeVector::const_iterator &cwi, EdgeVector::const_iterator &ccwi, double &cad, double &ccad)
Initialize neighbors and angles.
bool needsLargeTurn(NBEdge *e1, NBEdge *e2, std::map< NBEdge *, std::set< NBEdge * > > &same) const
whether the given edges (along with those in the same direction) requires a large turning radius
static const SVCPermissions SVC_LARGE_TURN
static double getExtraWidth(const NBEdge *e, SVCPermissions exclude)
compute with of rightmost lanes that exlude the given permissions
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)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:58
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
void set(double x, double y)
set positions x and y
Definition: Position.h:85
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:293
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:252
double x() const
Returns the x-position.
Definition: Position.h:55
void add(const Position &pos)
Adds the given position to this one.
Definition: Position.h:125
void setz(double z)
set position z
Definition: Position.h:80
void mul(double val)
Multiplies both positions with the given value.
Definition: Position.h:105
double z() const
Returns the z-position.
Definition: Position.h:65
double y() const
Returns the y-position.
Definition: Position.h:60
A list of positions.
double length2D() const
Returns the length.
void append(const PositionVector &v, double sameThreshold=2.0)
double length() const
Returns the length.
Position intersectionPosition2D(const Position &p1, const Position &p2, const double withinDist=0.) const
Returns the position of the intersection.
void push_front_noDoublePos(const Position &p)
insert in front a non double position
void add(double xoff, double yoff, double zoff)
std::vector< double > intersectsAtLengths2D(const PositionVector &other) const
For all intersections between this vector and other, return the 2D-length of the subvector from this ...
double distance2D(const Position &p, bool perpendicular=false) const
closest 2D-distance to point p (or -1 if perpendicular is true and the point is beyond this vector)
double nearest_offset_to_point2D(const Position &p, bool perpendicular=true) const
return the nearest offest to point 2D
std::vector< double > distances(const PositionVector &s, bool perpendicular=false) const
distances of all my points to s and all of s points to myself
std::pair< PositionVector, PositionVector > splitAt(double where, bool use2D=false) const
Returns the two lists made when this list vector is splitted at the given point.
void move2side(double amount, double maxExtension=100)
move position vector to side using certain ammount
PositionVector getSubpart2D(double beginOffset, double endOffset) const
get subpart of a position vector in two dimensions (Z is ignored)
PositionVector interpolateZ(double zStart, double zEnd) const
returned vector that varies z smoothly over its length
double angleAt2D(int pos) const
get angle in certain position of position vector
void extrapolate2D(const double val, const bool onlyFirst=false)
extrapolate position vector in two dimensions (Z is ignored)
void push_back_noDoublePos(const Position &p)
insert in back a non double position
bool intersects(const Position &p1, const Position &p2) const
Returns the information whether this list of points interesects the given line.
PositionVector reverse() const
reverse position vector
PositionVector getSubpartByIndex(int beginIndex, int count) const
get subpart of a position vector using index and a cout
Position positionAtOffset2D(double pos, double lateralOffset=0) const
Returns the position at the given length.
void sub(const Position &offset)
PositionVector getSubpart(double beginOffset, double endOffset) const
get subpart of a position vector
static T maxValue(const std::vector< T > &v)
Definition: VectorHelper.h:87
static T minValue(const std::vector< T > &v)
Definition: VectorHelper.h:97
#define M_PI
Definition: odrSpiral.cpp:40