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