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