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