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