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