Eclipse SUMO - Simulation of Urban MObility
NBNode.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-2022 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/****************************************************************************/
21// The representation of a single node
22/****************************************************************************/
23#include <config.h>
24
25#include <string>
26#include <map>
27#include <cassert>
28#include <algorithm>
29#include <vector>
30#include <deque>
31#include <set>
32#include <cmath>
33#include <iterator>
43#include <iomanip>
44#include "NBNode.h"
45#include "NBAlgorithms.h"
46#include "NBNodeCont.h"
47#include "NBNodeShapeComputer.h"
48#include "NBEdgeCont.h"
49#include "NBTypeCont.h"
50#include "NBHelpers.h"
51#include "NBDistrict.h"
52#include "NBContHelper.h"
53#include "NBRequest.h"
54#include "NBOwnTLDef.h"
55#include "NBLoadedSUMOTLDef.h"
58
59// allow to extend a crossing across multiple edges
60#define EXTEND_CROSSING_ANGLE_THRESHOLD 35.0 // degrees
61// create intermediate walking areas if either of the following thresholds is exceeded
62#define SPLIT_CROSSING_WIDTH_THRESHOLD 1.5 // meters
63#define SPLIT_CROSSING_ANGLE_THRESHOLD 5 // degrees
64
65// minimum length for a weaving section at a combined on-off ramp
66#define MIN_WEAVE_LENGTH 20.0
67
68//#define DEBUG_CONNECTION_GUESSING
69//#define DEBUG_SMOOTH_GEOM
70//#define DEBUG_PED_STRUCTURES
71//#define DEBUG_EDGE_SORTING
72//#define DEBUGCOND true
73#define DEBUG_NODE_ID "C"
74#define DEBUGCOND (getID() == DEBUG_NODE_ID)
75#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUG_NODE_ID))
76
77// ===========================================================================
78// static members
79// ===========================================================================
80const int NBNode::FORWARD(1);
81const int NBNode::BACKWARD(-1);
82const double NBNode::UNSPECIFIED_RADIUS = -1;
87const int NBNode::SCURVE_IGNORE(16);
88const int NBNode::INDIRECT_LEFT(32);
89
90// ===========================================================================
91// method definitions
92// ===========================================================================
93/* -------------------------------------------------------------------------
94 * NBNode::ApproachingDivider-methods
95 * ----------------------------------------------------------------------- */
97 const EdgeVector& approaching, NBEdge* currentOutgoing) :
98 myApproaching(approaching),
99 myCurrentOutgoing(currentOutgoing),
100 myIsBikeEdge(currentOutgoing->getPermissions() == SVC_BICYCLE) {
101 // collect lanes which are expliclity targeted
102 std::set<int> approachedLanes;
103 for (const NBEdge* const approachingEdge : myApproaching) {
104 for (const NBEdge::Connection& con : approachingEdge->getConnections()) {
105 if (con.toEdge == myCurrentOutgoing) {
106 approachedLanes.insert(con.toLane);
107 }
108 }
109 }
110 // compute the indices of lanes that should be targeted (excluding pedestrian
111 // lanes that will be connected from walkingAreas and forbidden lanes)
112 // if the lane is targeted by an explicitly set connection we need
113 // to make it available anyway
114 for (int i = 0; i < currentOutgoing->getNumLanes(); ++i) {
115 if ((currentOutgoing->getPermissions(i) == SVC_PEDESTRIAN
116 // don't consider bicycle lanes as targets unless the target
117 // edge is exclusively for bicycles
118 || (currentOutgoing->getPermissions(i) == SVC_BICYCLE && !myIsBikeEdge)
119 || isForbidden(currentOutgoing->getPermissions(i)))
120 && approachedLanes.count(i) == 0) {
121 continue;
122 }
123 myAvailableLanes.push_back(i);
124 }
125}
126
127
129
130
131void
132NBNode::ApproachingDivider::execute(const int src, const int dest) {
133 assert((int)myApproaching.size() > src);
134 // get the origin edge
135 NBEdge* incomingEdge = myApproaching[src];
137 return;
138 }
139 if (myAvailableLanes.size() == 0) {
140 return;
141 }
142 std::vector<int> approachingLanes = incomingEdge->getConnectionLanes(myCurrentOutgoing, myIsBikeEdge || incomingEdge->getPermissions() == SVC_BICYCLE);
143 if (approachingLanes.size() == 0) {
144 return;
145 }
146#ifdef DEBUG_CONNECTION_GUESSING
147 if (DEBUGCOND2(incomingEdge->getToNode())) {
148 std::cout << "Bre:ex src=" << src << " dest=" << dest << " in=" << incomingEdge->getID() << " apLanes=" << toString(approachingLanes) << "\n";
149 }
150
151#endif
152 std::deque<int>* approachedLanes = spread(approachingLanes, dest);
153 assert(approachedLanes->size() <= myAvailableLanes.size());
154 // set lanes
155 for (int i = 0; i < (int)approachedLanes->size(); i++) {
156 assert((int)approachingLanes.size() > i);
157 int approached = myAvailableLanes[(*approachedLanes)[i]];
158 incomingEdge->setConnection(approachingLanes[i], myCurrentOutgoing, approached, NBEdge::Lane2LaneInfoType::COMPUTED);
159 }
160 delete approachedLanes;
161}
162
163
164std::deque<int>*
165NBNode::ApproachingDivider::spread(const std::vector<int>& approachingLanes, int dest) const {
166 std::deque<int>* ret = new std::deque<int>();
167 const int numLanes = (int)approachingLanes.size();
168 // when only one lane is approached, we check, whether the double-value
169 // is assigned more to the left or right lane
170 if (numLanes == 1) {
171 ret->push_back(dest);
172 return ret;
173 }
174
175 const int numOutgoingLanes = (int)myAvailableLanes.size();
176 //
177 ret->push_back(dest);
178 int noSet = 1;
179 int roffset = 1;
180 int loffset = 1;
181 while (noSet < numLanes) {
182 // It may be possible, that there are not enough lanes the source
183 // lanes may be divided on
184 // In this case, they remain unset
185 // !!! this is only a hack. It is possible, that this yields in
186 // uncommon divisions
187 if (numOutgoingLanes == noSet) {
188 return ret;
189 }
190
191 // as due to the conversion of double->uint the numbers will be lower
192 // than they should be, we try to append to the left side first
193 //
194 // check whether the left boundary of the approached street has
195 // been overridden; if so, move all lanes to the right
196 if (dest + loffset >= numOutgoingLanes) {
197 loffset -= 1;
198 roffset += 1;
199 for (int i = 0; i < (int)ret->size(); i++) {
200 (*ret)[i] = (*ret)[i] - 1;
201 }
202 }
203 // append the next lane to the left of all edges
204 // increase the position (destination edge)
205 ret->push_back(dest + loffset);
206 noSet++;
207 loffset += 1;
208
209 // as above
210 if (numOutgoingLanes == noSet) {
211 return ret;
212 }
213
214 // now we try to append the next lane to the right side, when needed
215 if (noSet < numLanes) {
216 // check whether the right boundary of the approached street has
217 // been overridden; if so, move all lanes to the right
218 if (dest < roffset) {
219 loffset += 1;
220 roffset -= 1;
221 for (int i = 0; i < (int)ret->size(); i++) {
222 (*ret)[i] = (*ret)[i] + 1;
223 }
224 }
225 ret->push_front(dest - roffset);
226 noSet++;
227 roffset += 1;
228 }
229 }
230 return ret;
231}
232
233
234/* -------------------------------------------------------------------------
235 * NBNode::Crossing-methods
236 * ----------------------------------------------------------------------- */
237NBNode::Crossing::Crossing(const NBNode* _node, const EdgeVector& _edges, double _width, bool _priority, int _customTLIndex, int _customTLIndex2, const PositionVector& _customShape) :
239 node(_node),
240 edges(_edges),
241 customWidth(_width),
242 width(_width),
243 priority(_priority),
244 customShape(_customShape),
245 tlLinkIndex(_customTLIndex),
246 tlLinkIndex2(_customTLIndex2),
247 customTLIndex(_customTLIndex),
248 customTLIndex2(_customTLIndex2),
249 valid(true) {
250}
251
252
253/* -------------------------------------------------------------------------
254 * NBNode-methods
255 * ----------------------------------------------------------------------- */
256NBNode::NBNode(const std::string& id, const Position& position,
257 SumoXMLNodeType type) :
258 Named(StringUtils::convertUmlaute(id)),
259 myPosition(position),
260 myType(type),
261 myDistrict(nullptr),
262 myHaveCustomPoly(false),
263 myRequest(nullptr),
265 myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
266 myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
271 myIsBentPriority(false),
272 myTypeWasGuessed(false) {
274 throw ProcessError("Invalid node id '" + myID + "'.");
275 }
276}
277
278
279NBNode::NBNode(const std::string& id, const Position& position, NBDistrict* district) :
280 Named(StringUtils::convertUmlaute(id)),
281 myPosition(position),
282 myType(district == nullptr ? SumoXMLNodeType::UNKNOWN : SumoXMLNodeType::DISTRICT),
283 myDistrict(district),
284 myHaveCustomPoly(false),
285 myRequest(nullptr),
286 myRadius(UNSPECIFIED_RADIUS),
287 myKeepClear(OptionsCont::getOptions().getBool("default.junctions.keep-clear")),
288 myRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(OptionsCont::getOptions().getString("default.right-of-way"))),
289 myFringeType(FringeType::DEFAULT),
290 myDiscardAllCrossings(false),
291 myCrossingsLoadedFromSumoNet(0),
292 myDisplacementError(0),
293 myIsBentPriority(false),
294 myTypeWasGuessed(false) {
296 throw ProcessError("Invalid node id '" + myID + "'.");
297 }
298}
299
300
302 delete myRequest;
303}
304
305
306void
308 bool updateEdgeGeometries) {
309 myPosition = position;
310 // patch type
311 myType = type;
312 if (!isTrafficLight(myType)) {
314 }
315 if (updateEdgeGeometries) {
316 for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
317 PositionVector geom = (*i)->getGeometry();
318 geom[-1] = myPosition;
319 (*i)->setGeometry(geom);
320 }
321 for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
322 PositionVector geom = (*i)->getGeometry();
323 geom[0] = myPosition;
324 (*i)->setGeometry(geom);
325 }
326 }
327}
328
329
330
331// ----------- Applying offset
332void
333NBNode::reshiftPosition(double xoff, double yoff) {
334 myPosition.add(xoff, yoff, 0);
335 myPoly.add(xoff, yoff, 0);
336 for (auto& wacs : myWalkingAreaCustomShapes) {
337 wacs.shape.add(xoff, yoff, 0);
338 }
339 for (auto& c : myCrossings) {
340 c->customShape.add(xoff, yoff, 0);
341 }
342}
343
344
345void
347 myPosition.mul(1, -1);
348 myPoly.mirrorX();
349 // mirror pre-computed geometry of crossings and walkingareas
350 for (auto& c : myCrossings) {
351 c->customShape.mirrorX();
352 c->shape.mirrorX();
353 }
354 for (auto& wa : myWalkingAreas) {
355 wa.shape.mirrorX();
356 }
357 for (auto& wacs : myWalkingAreaCustomShapes) {
358 wacs.shape.mirrorX();
359 }
360}
361
362
363// ----------- Methods for dealing with assigned traffic lights
364void
366 myTrafficLights.insert(tlDef);
367 // rail signals receive a temporary traffic light in order to set connection tl-linkIndex
370 }
371}
372
373
374void
376 tlDef->removeNode(this);
377 myTrafficLights.erase(tlDef);
378}
379
380
381void
382NBNode::removeTrafficLights(bool setAsPriority) {
383 std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
384 for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
386 }
387 if (setAsPriority) {
390 }
391}
392
393
394void
395NBNode::invalidateTLS(NBTrafficLightLogicCont& tlCont, bool removedConnections, bool addedConnections) {
396 if (isTLControlled()) {
397 std::set<NBTrafficLightDefinition*> oldDefs(myTrafficLights);
398 for (std::set<NBTrafficLightDefinition*>::iterator it = oldDefs.begin(); it != oldDefs.end(); ++it) {
399 NBTrafficLightDefinition* orig = *it;
400 if (dynamic_cast<NBLoadedSUMOTLDef*>(orig) != nullptr) {
401 dynamic_cast<NBLoadedSUMOTLDef*>(orig)->registerModifications(removedConnections, addedConnections);
402 } else if (dynamic_cast<NBOwnTLDef*>(orig) == nullptr) {
403 NBTrafficLightDefinition* newDef = new NBOwnTLDef(orig->getID(), orig->getOffset(), orig->getType());
404 const std::vector<NBNode*>& nodes = orig->getNodes();
405 while (!nodes.empty()) {
406 newDef->addNode(nodes.front());
407 nodes.front()->removeTrafficLight(orig);
408 }
409 tlCont.removeFully(orig->getID());
410 tlCont.insert(newDef);
411 }
412 }
413 }
414}
415
416
417void
418NBNode::shiftTLConnectionLaneIndex(NBEdge* edge, int offset, int threshold) {
419 for (std::set<NBTrafficLightDefinition*>::iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
420 (*it)->shiftTLConnectionLaneIndex(edge, offset, threshold);
421 }
422}
423
424// ----------- Prunning the input
425int
427 int ret = 0;
428 int pos = 0;
429 EdgeVector::const_iterator j = myIncomingEdges.begin();
430 while (j != myIncomingEdges.end()) {
431 // skip edges which are only incoming and not outgoing
432 if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), *j) == myOutgoingEdges.end()) {
433 ++j;
434 ++pos;
435 continue;
436 }
437 // an edge with both its origin and destination being the current
438 // node should be removed
439 NBEdge* dummy = *j;
440 WRITE_WARNINGF(TL(" Removing self-looping edge '%'"), dummy->getID());
441 // get the list of incoming edges connected to the self-loop
442 EdgeVector incomingConnected = dummy->getIncomingEdges();
443 // get the list of outgoing edges connected to the self-loop
444 EdgeVector outgoingConnected = dummy->getConnectedEdges();
445 // let the self-loop remap its connections
446 dummy->remapConnections(incomingConnected);
447 remapRemoved(tc, dummy, incomingConnected, outgoingConnected);
448 // delete the self-loop
449 ec.erase(dc, dummy);
450 j = myIncomingEdges.begin() + pos;
451 ++ret;
452 }
453 return ret;
454}
455
456
457// -----------
458void
460 assert(edge != 0);
461 if (find(myIncomingEdges.begin(), myIncomingEdges.end(), edge) == myIncomingEdges.end()) {
462 myIncomingEdges.push_back(edge);
463 myAllEdges.push_back(edge);
464 }
465}
466
467
468void
470 assert(edge != 0);
471 if (find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge) == myOutgoingEdges.end()) {
472 myOutgoingEdges.push_back(edge);
473 myAllEdges.push_back(edge);
474 }
475}
476
477
478bool
479NBNode::isSimpleContinuation(bool checkLaneNumbers, bool checkWidth) const {
480 // one in, one out->continuation
481 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
482 NBEdge* in = myIncomingEdges.front();
483 NBEdge* out = myOutgoingEdges.front();
484 // both must have the same number of lanes
485 return ((!checkLaneNumbers || in->getNumLanes() == out->getNumLanes())
486 && (!checkWidth || in->getTotalWidth() == out->getTotalWidth()));
487 }
488 // two in and two out and both in reverse direction
489 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
490 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
491 NBEdge* in = *i;
492 EdgeVector::const_iterator opposite = find_if(myOutgoingEdges.begin(), myOutgoingEdges.end(), NBContHelper::opposite_finder(in));
493 // must have an opposite edge
494 if (opposite == myOutgoingEdges.end()) {
495 return false;
496 }
497 // both must have the same number of lanes
499 if (checkLaneNumbers && in->getNumLanes() != (*opposite)->getNumLanes()) {
500 return false;
501 }
502 if (checkWidth && in->getTotalWidth() != (*opposite)->getTotalWidth()) {
503 return false;
504 }
505 }
506 return true;
507 }
508 // nope
509 return false;
510}
511
512
515 const PositionVector& endShape,
516 int numPoints,
517 bool isTurnaround,
518 double extrapolateBeg,
519 double extrapolateEnd,
520 NBNode* recordError,
521 int shapeFlag) const {
522
523 bool ok = true;
524 if ((shapeFlag & INDIRECT_LEFT) != 0) {
525 return indirectLeftShape(begShape, endShape, numPoints);
526 }
527 PositionVector init = bezierControlPoints(begShape, endShape, isTurnaround, extrapolateBeg, extrapolateEnd, ok, recordError, DEG2RAD(5), shapeFlag);
528#ifdef DEBUG_SMOOTH_GEOM
529 if (DEBUGCOND) {
530 std::cout << "computeSmoothShape node " << getID() << " init=" << init << "\n";
531 }
532#endif
533 if (init.size() == 0) {
534 PositionVector ret;
535 ret.push_back(begShape.back());
536 ret.push_back(endShape.front());
537 return ret;
538 } else {
539 return init.bezier(numPoints).smoothedZFront();
540 }
541}
542
545 const PositionVector& begShape,
546 const PositionVector& endShape,
547 bool isTurnaround,
548 double extrapolateBeg,
549 double extrapolateEnd,
550 bool& ok,
551 NBNode* recordError,
552 double straightThresh,
553 int shapeFlag) {
554
555 const Position beg = begShape.back();
556 const Position end = endShape.front();
557 const double dist = beg.distanceTo2D(end);
558 PositionVector init;
559 if (dist < POSITION_EPS || beg.distanceTo2D(begShape[-2]) < POSITION_EPS || end.distanceTo2D(endShape[1]) < POSITION_EPS) {
560#ifdef DEBUG_SMOOTH_GEOM
561 if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end
562 << " dist=" << dist
563 << " distBegLast=" << beg.distanceTo2D(begShape[-2])
564 << " distEndFirst=" << end.distanceTo2D(endShape[1])
565 << "\n";
566#endif
567 // typically, this node a is a simpleContinuation. see also #2539
568 return init;
569 } else {
570 init.push_back(beg);
571 if (isTurnaround) {
572 // turnarounds:
573 // - end of incoming lane
574 // - position between incoming/outgoing end/begin shifted by the distance orthogonally
575 // - begin of outgoing lane
576 Position center = PositionVector::positionAtOffset2D(beg, end, beg.distanceTo2D(end) / (double) 2.);
577 center.sub(beg.y() - end.y(), end.x() - beg.x());
578 init.push_back(center);
579 } else {
580 const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
581 PositionVector endShapeBegLine(endShape[0], endShape[1]);
582 PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
583 endShapeBegLine.extrapolate2D(100, true);
584 begShapeEndLineRev.extrapolate2D(100, true);
585 if (fabs(angle) < M_PI / 4.) {
586 // very low angle: could be an s-shape or a straight line
587 const double displacementAngle = GeomHelper::angleDiff(begShape.angleAt2D(-2), beg.angleTo2D(end));
588 const double bendDeg = RAD2DEG(fabs(displacementAngle - angle));
589 const double halfDistance = dist / 2;
590 if (fabs(displacementAngle) <= straightThresh && fabs(angle) <= straightThresh) {
591#ifdef DEBUG_SMOOTH_GEOM
592 if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints identified straight line beg=" << beg << " end=" << end
593 << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle) << "\n";
594#endif
595 return PositionVector();
596 } else if (bendDeg > 22.5 && pow(bendDeg / 45, 2) / dist > 0.13) {
597 // do not allow s-curves with extreme bends
598 // (a linear dependency is to restrictive at low displacementAngles and too permisive at high angles)
599#ifdef DEBUG_SMOOTH_GEOM
600 if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found extreme s-curve, falling back to straight line beg=" << beg << " end=" << end
601 << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
602 << " dist=" << dist << " bendDeg=" << bendDeg << " bd2=" << pow(bendDeg / 45, 2)
603 << " displacementError=" << sin(displacementAngle) * dist
604 << " begShape=" << begShape << " endShape=" << endShape << "\n";
605#endif
606 ok = false;
607 if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
608 recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)fabs(sin(displacementAngle) * dist));
609 }
610 return PositionVector();
611 } else {
612 const double endLength = begShape[-2].distanceTo2D(begShape[-1]);
613 const double off1 = endLength + MIN2(extrapolateBeg, halfDistance);
614 init.push_back(PositionVector::positionAtOffset2D(begShapeEndLineRev[1], begShapeEndLineRev[0], off1));
615 const double off2 = 100. - MIN2(extrapolateEnd, halfDistance);
616 init.push_back(PositionVector::positionAtOffset2D(endShapeBegLine[0], endShapeBegLine[1], off2));
617#ifdef DEBUG_SMOOTH_GEOM
618 if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints found s-curve beg=" << beg << " end=" << end
619 << " angle=" << RAD2DEG(angle) << " displacementAngle=" << RAD2DEG(displacementAngle)
620 << " halfDistance=" << halfDistance << "\n";
621#endif
622 }
623 } else {
624 // turning
625 // - end of incoming lane
626 // - intersection of the extrapolated lanes
627 // - begin of outgoing lane
628 // attention: if there is no intersection, use a straight line
629 Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
630 if (intersect == Position::INVALID) {
631#ifdef DEBUG_SMOOTH_GEOM
632 if (DEBUGCOND2(recordError)) {
633 std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end << " intersect=" << intersect
634 << " endShapeBegLine=" << endShapeBegLine
635 << " begShapeEndLineRev=" << begShapeEndLineRev
636 << "\n";
637 }
638#endif
639 ok = false;
640 if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
641 // it's unclear if this error can be solved via stretching the intersection.
642 recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
643 }
644 return PositionVector();
645 }
646 const double minControlLength = MIN2((double)1.0, dist / 2);
647 const double distBeg = intersect.distanceTo2D(beg);
648 const double distEnd = intersect.distanceTo2D(end);
649 const bool lengthenBeg = distBeg <= minControlLength;
650 const bool lengthenEnd = distEnd <= minControlLength;
651 if (lengthenBeg && lengthenEnd) {
652#ifdef DEBUG_SMOOTH_GEOM
653 if (DEBUGCOND2(recordError)) std::cout << " bezierControlPoints failed beg=" << beg << " end=" << end << " intersect=" << intersect
654 << " distBeg=" << distBeg << " distEnd=" << distEnd << "\n";
655#endif
656 if (recordError != nullptr && (shapeFlag & SCURVE_IGNORE) == 0) {
657 // This should be fixable with minor stretching
658 recordError->myDisplacementError = MAX2(recordError->myDisplacementError, (double)1.0);
659 }
660 ok = false;
661 return PositionVector();
662 } else if ((shapeFlag & FOUR_CONTROL_POINTS)) {
663 init.push_back(begShapeEndLineRev.positionAtOffset2D(100 - extrapolateBeg));
664 init.push_back(endShapeBegLine.positionAtOffset2D(100 - extrapolateEnd));
665 } else if (lengthenBeg || lengthenEnd) {
666 init.push_back(begShapeEndLineRev.positionAtOffset2D(100 - minControlLength));
667 init.push_back(endShapeBegLine.positionAtOffset2D(100 - minControlLength));
668 } else if ((shapeFlag & AVOID_WIDE_LEFT_TURN) != 0
669 // there are two reasons for enabling special geometry rules:
670 // 1) sharp edge angles which could cause overshoot
671 // 2) junction geometries with a large displacement between opposite left turns
672 // which would cause the default geometry to overlap
673 && ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) != 0
674 || (angle > DEG2RAD(95) && (distBeg > 20 || distEnd > 20)))) {
675 //std::cout << " bezierControlPoints intersect=" << intersect << " dist=" << dist << " distBeg=" << distBeg << " distEnd=" << distEnd << " angle=" << RAD2DEG(angle) << " flag=" << shapeFlag << "\n";
676 const double factor = ((shapeFlag & AVOID_INTERSECTING_LEFT_TURNS) == 0 ? 1
677 : MIN2(0.6, 16 / dist));
678 init.push_back(begShapeEndLineRev.positionAtOffset2D(100 - MIN2(distBeg * factor / 1.2, dist * factor / 1.8)));
679 init.push_back(endShapeBegLine.positionAtOffset2D(100 - MIN2(distEnd * factor / 1.2, dist * factor / 1.8)));
680 } else if ((shapeFlag & AVOID_WIDE_RIGHT_TURN) != 0 && angle < DEG2RAD(-95) && (distBeg > 20 || distEnd > 20)) {
681 //std::cout << " bezierControlPoints intersect=" << intersect << " distBeg=" << distBeg << " distEnd=" << distEnd << "\n";
682 init.push_back(begShapeEndLineRev.positionAtOffset2D(100 - MIN2(distBeg / 1.4, dist / 2)));
683 init.push_back(endShapeBegLine.positionAtOffset2D(100 - MIN2(distEnd / 1.4, dist / 2)));
684 } else {
685 double z;
686 const double z1 = begShapeEndLineRev.positionAtOffset2D(begShapeEndLineRev.nearest_offset_to_point2D(intersect)).z();
687 const double z2 = endShapeBegLine.positionAtOffset2D(endShapeBegLine.nearest_offset_to_point2D(intersect)).z();
688 const double z3 = 0.5 * (beg.z() + end.z());
689 // if z1 and z2 are on the same side in regard to z3 then we
690 // can use their avarage. Otherwise, the intersection in 3D
691 // is not good and we are better of using z3
692 if ((z1 <= z3 && z2 <= z3) || (z1 >= z3 && z2 >= z3)) {
693 z = 0.5 * (z1 + z2);
694 } else {
695 z = z3;
696 }
697 intersect.set(intersect.x(), intersect.y(), z);
698 init.push_back(intersect);
699 }
700 }
701 }
702 init.push_back(end);
703 }
704 return init;
705}
706
708NBNode::indirectLeftShape(const PositionVector& begShape, const PositionVector& endShape, int numPoints) const {
709 UNUSED_PARAMETER(numPoints);
710 PositionVector result;
711 result.push_back(begShape.back());
712 //const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
713 PositionVector endShapeBegLine(endShape[0], endShape[1]);
714 PositionVector begShapeEndLineRev(begShape[-1], begShape[-2]);
715 endShapeBegLine.extrapolate2D(100, true);
716 begShapeEndLineRev.extrapolate2D(100, true);
717 Position intersect = endShapeBegLine.intersectionPosition2D(begShapeEndLineRev);
718 if (intersect == Position::INVALID) {
719 WRITE_WARNING("Could not compute indirect left turn shape at node '" + getID() + "'");
720 } else {
721 Position dir = intersect;
722 dir.sub(endShape[0]);
723 dir.norm2d();
724 const double radius = myRadius == NBNode::UNSPECIFIED_RADIUS ? OptionsCont::getOptions().getFloat("default.junctions.radius") : myRadius;
725 dir.mul(radius);
726 result.push_back(intersect + dir);
727 }
728 result.push_back(endShape.front());
729 return result;
730}
731
733NBNode::computeInternalLaneShape(const NBEdge* fromE, const NBEdge::Connection& con, int numPoints, NBNode* recordError, int shapeFlag) const {
734 if (con.fromLane >= fromE->getNumLanes()) {
735 throw ProcessError("Connection '" + con.getDescription(fromE) + "' starts at a non-existant lane.");
736 }
737 if (con.toLane >= con.toEdge->getNumLanes()) {
738 throw ProcessError("Connection '" + con.getDescription(fromE) + "' targets a non-existant lane.");
739 }
740 PositionVector fromShape = fromE->getLaneShape(con.fromLane);
741 PositionVector toShape = con.toEdge->getLaneShape(con.toLane);
742 PositionVector ret;
743 bool useCustomShape = con.customShape.size() > 0;
744 if (useCustomShape) {
745 // ensure that the shape starts and ends at the intersection boundary
746 PositionVector startBorder = fromE->getNodeBorder(this);
747 if (startBorder.size() == 0) {
748 startBorder = fromShape.getOrthogonal(fromShape.back(), 1, true);
749 }
750 PositionVector tmp = NBEdge::startShapeAt(con.customShape, this, startBorder);
751 if (tmp.size() < 2) {
752 WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
753 useCustomShape = false;
754 } else {
755 if (tmp.length2D() > con.customShape.length2D() + POSITION_EPS) {
756 // shape was lengthened at the start, make sure it attaches at the center of the lane
757 tmp[0] = fromShape.back();
758 } else if (recordError != nullptr) {
759 const double offset = tmp[0].distanceTo2D(fromShape.back());
760 if (offset > fromE->getLaneWidth(con.fromLane) / 2) {
761 WRITE_WARNINGF(TL("Custom shape has distance % to incoming lane for connection %."), offset, con.getDescription(fromE));
762 }
763 }
764 PositionVector endBorder = con.toEdge->getNodeBorder(this);
765 if (endBorder.size() == 0) {
766 endBorder = toShape.getOrthogonal(toShape.front(), 1, false);
767 }
768 ret = NBEdge::startShapeAt(tmp.reverse(), this, endBorder).reverse();
769 if (ret.size() < 2) {
770 WRITE_WARNINGF(TL("Could not use custom shape for connection %."), con.getDescription(fromE));
771 useCustomShape = false;
772 } else if (ret.length2D() > tmp.length2D() + POSITION_EPS) {
773 // shape was lengthened at the end, make sure it attaches at the center of the lane
774 ret[-1] = toShape.front();
775 } else if (recordError != nullptr) {
776 const double offset = ret[-1].distanceTo2D(toShape.front());
777 if (offset > con.toEdge->getLaneWidth(con.toLane) / 2) {
778 WRITE_WARNINGF(TL("Custom shape has distance % to outgoing lane for connection %."), offset, con.getDescription(fromE));
779 }
780 }
781 }
782 }
783 if (!useCustomShape) {
784 displaceShapeAtWidthChange(fromE, con, fromShape, toShape);
785 double extrapolateBeg = 5. * fromE->getNumLanes();
786 double extrapolateEnd = 5. * con.toEdge->getNumLanes();
787 LinkDirection dir = getDirection(fromE, con.toEdge);
788 if (dir == LinkDirection::LEFT || dir == LinkDirection::TURN) {
789 shapeFlag += AVOID_WIDE_LEFT_TURN;
790 }
791 if (con.indirectLeft) {
792 shapeFlag += INDIRECT_LEFT;
793 }
794#ifdef DEBUG_SMOOTH_GEOM
795 if (DEBUGCOND) {
796 std::cout << "computeInternalLaneShape node " << getID() << " fromE=" << fromE->getID() << " toE=" << con.toEdge->getID() << "\n";
797 }
798#endif
799 ret = computeSmoothShape(fromShape, toShape,
800 numPoints, fromE->getTurnDestination() == con.toEdge,
801 extrapolateBeg, extrapolateEnd, recordError, shapeFlag);
802 }
803 const NBEdge::Lane& lane = fromE->getLaneStruct(con.fromLane);
804 if (lane.endOffset > 0) {
805 PositionVector beg = lane.shape.getSubpart(lane.shape.length() - lane.endOffset, lane.shape.length());
806 beg.append(ret);
807 ret = beg;
808 }
809 if (con.toEdge->isBidiRail() && con.toEdge->getTurnDestination(true)->getEndOffset() > 0) {
810 PositionVector end = toShape.getSubpart(0, con.toEdge->getTurnDestination(true)->getEndOffset());
811 ret.append(end);
812 }
813 return ret;
814}
815
816
817bool
819 return (myIncomingEdges.size() == 1
820 && myOutgoingEdges.size() == 1
821 && myIncomingEdges[0]->getNumLanes() != myOutgoingEdges[0]->getNumLanes()
822 && myIncomingEdges[0]->getTotalWidth() == myOutgoingEdges[0]->getTotalWidth());
823}
824
825void
827 PositionVector& fromShape, PositionVector& toShape) const {
829 // displace shapes
830 NBEdge* in = myIncomingEdges[0];
831 NBEdge* out = myOutgoingEdges[0];
832 double outCenter = out->getLaneWidth(con.toLane) / 2;
833 for (int i = 0; i < con.toLane; ++i) {
834 outCenter += out->getLaneWidth(i);
835 }
836 double inCenter = in->getLaneWidth(con.fromLane) / 2;
837 for (int i = 0; i < con.fromLane; ++i) {
838 inCenter += in->getLaneWidth(i);
839 }
840 //std::cout << "displaceShapeAtWidthChange inCenter=" << inCenter << " outCenter=" << outCenter << "\n";
841 try {
842 if (in->getNumLanes() > out->getNumLanes()) {
843 // shift toShape so the internal lane ends straight at the displaced entry point
844 toShape.move2side(outCenter - inCenter);
845 } else {
846 // shift fromShape so the internal lane starts straight at the displaced exit point
847 fromShape.move2side(inCenter - outCenter);
848
849 }
850 } catch (InvalidArgument&) { }
851 } else {
852 SVCPermissions fromP = from->getPermissions(con.fromLane);
854 if ((fromP & toP) == SVC_BICYCLE && (fromP | toP) != SVC_BICYCLE) {
855 double shift = (from->getLaneWidth(con.fromLane) - con.toEdge->getLaneWidth(con.toLane)) / 2;
856 if (toP == SVC_BICYCLE) {
857 // let connection to dedicated bicycle lane start on the right side of a mixed lane for straight an right-going connections
858 // (on the left side for left turns)
859 // XXX indirect left turns should also start on the right side
860 LinkDirection dir = getDirection(from, con.toEdge);
861 if ((dir == LinkDirection::LEFT) || (dir == LinkDirection::PARTLEFT) || (dir == LinkDirection::TURN)) {
862 fromShape.move2side(-shift);
863 } else {
864 fromShape.move2side(shift);
865 }
866 } else if (fromP == SVC_BICYCLE) {
867 // let connection from dedicated bicycle end on the right side of a mixed lane
868 toShape.move2side(-shift);
869 }
870 }
871 }
872}
873
874bool
875NBNode::needsCont(const NBEdge* fromE, const NBEdge* otherFromE,
876 const NBEdge::Connection& c, const NBEdge::Connection& otherC) const {
877 const NBEdge* toE = c.toEdge;
878 const NBEdge* otherToE = otherC.toEdge;
879
883 return false;
884 }
885 LinkDirection d1 = getDirection(fromE, toE);
886 const bool thisRight = (d1 == LinkDirection::RIGHT || d1 == LinkDirection::PARTRIGHT);
887 const bool rightTurnConflict = (thisRight &&
888 NBNode::rightTurnConflict(fromE, toE, c.fromLane, otherFromE, otherToE, otherC.fromLane));
889 if (thisRight && !rightTurnConflict) {
890 return false;
891 }
892 if (myRequest && myRequest->indirectLeftTurnConflict(fromE, c, otherFromE, otherC, false)) {
893 return true;
894 }
895 if (!(foes(otherFromE, otherToE, fromE, toE) || myRequest == nullptr || rightTurnConflict)) {
896 // if they do not cross, no waiting place is needed
897 return false;
898 }
899 LinkDirection d2 = getDirection(otherFromE, otherToE);
900 if (d2 == LinkDirection::TURN) {
901 return false;
902 }
903 const bool thisLeft = (d1 == LinkDirection::LEFT || d1 == LinkDirection::TURN);
904 const bool otherLeft = (d2 == LinkDirection::LEFT || d2 == LinkDirection::TURN);
905 const bool bothLeft = thisLeft && otherLeft;
906 if (fromE == otherFromE && !thisRight) {
907 // ignore same edge links except for right-turns
908 return false;
909 }
910 if (thisRight && d2 != LinkDirection::STRAIGHT) {
911 return false;
912 }
913 if (c.tlID != "" && !bothLeft) {
915 for (std::set<NBTrafficLightDefinition*>::const_iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
916 if ((*it)->needsCont(fromE, toE, otherFromE, otherToE)) {
917 return true;
918 }
919 }
920 return false;
921 }
922 if (fromE->getJunctionPriority(this) > 0 && otherFromE->getJunctionPriority(this) > 0) {
923 return mustBrake(fromE, toE, c.fromLane, c.toLane, false);
924 }
925 return false;
926}
927
928bool
930 const NBEdge* foeFrom, const NBEdge::Connection& foe) const {
931 return (foe.haveVia && isTLControlled() && c.tlLinkIndex >= 0 && foe.tlLinkIndex >= 0
932 && !foeFrom->isTurningDirectionAt(foe.toEdge)
933 && foes(from, c.toEdge, foeFrom, foe.toEdge)
934 && !needsCont(foeFrom, from, foe, c));
935}
936
937
938void
940 std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
941 for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
942 // if this is the only controlled node we keep the tlDef as it is to generate a warning later
943 if ((*i)->getNodes().size() > 1) {
944 myTrafficLights.erase(*i);
945 (*i)->removeNode(this);
946 (*i)->setParticipantsInformation();
947 (*i)->setTLControllingInformation();
948 }
949 }
950}
951
952
953void
955 delete myRequest; // possibly recomputation step
956 myRequest = nullptr;
957 if (myIncomingEdges.size() == 0 || myOutgoingEdges.size() == 0) {
958 // no logic if nothing happens here
961 return;
962 }
963 // compute the logic if necessary or split the junction
965 // build the request
967 // check whether it is not too large
968 int numConnections = numNormalConnections();
969 if (numConnections >= SUMO_MAX_CONNECTIONS) {
970 // yep -> make it untcontrolled, warn
971 delete myRequest;
972 myRequest = nullptr;
975 } else {
977 }
978 WRITE_WARNINGF(TL("Junction '%' is too complicated (% connections, max %); will be set to %."),
979 getID(), numConnections, SUMO_MAX_CONNECTIONS, toString(myType));
980 } else if (numConnections == 0) {
981 delete myRequest;
982 myRequest = nullptr;
985 } else {
987 }
988 }
989}
990
991
992void
993NBNode::computeLogic2(bool checkLaneFoes) {
994 if (myRequest != nullptr) {
995 myRequest->computeLogic(checkLaneFoes);
996 }
997}
998
999void
1001 if (hasConflict()) {
1002 if (!myKeepClear) {
1003 for (NBEdge* incoming : myIncomingEdges) {
1004 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1005 for (NBEdge::Connection& c : connections) {
1006 c.keepClear = KEEPCLEAR_FALSE;
1007 }
1008 }
1009 } else if (geometryLike() && myCrossings.size() == 0 && !isTLControlled()) {
1010 int linkIndex = 0;
1011 for (NBEdge* incoming : myIncomingEdges) {
1012 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1013 for (NBEdge::Connection& c : connections) {
1014 if (c.keepClear == KEEPCLEAR_UNSPECIFIED && myRequest->hasConflictAtLink(linkIndex)) {
1015 const LinkState linkState = getLinkState(incoming, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
1016 if (linkState == LINKSTATE_MAJOR) {
1017 c.keepClear = KEEPCLEAR_FALSE;
1018 }
1019 }
1020 }
1021 linkIndex++;
1022 }
1023 }
1024 }
1025}
1026
1027
1028bool
1030 if (myRequest) {
1031 myRequest->writeLogic(into);
1032 return true;
1033 }
1034 return false;
1035}
1036
1037
1038const std::string
1039NBNode::getFoes(int linkIndex) const {
1040 if (myRequest == nullptr) {
1041 return "";
1042 } else {
1043 return myRequest->getFoes(linkIndex);
1044 }
1045}
1046
1047
1048const std::string
1049NBNode::getResponse(int linkIndex) const {
1050 if (myRequest == nullptr) {
1051 return "";
1052 } else {
1053 return myRequest->getResponse(linkIndex);
1054 }
1055}
1056
1057bool
1059 if (myRequest == nullptr) {
1060 return false;
1061 } else {
1062 return myRequest->hasConflict();
1063 }
1064}
1065
1066void
1069 sortEdges(false);
1070 computeNodeShape(-1);
1071 for (NBEdge* edge : myAllEdges) {
1072 edge->computeEdgeShape();
1073 }
1074}
1075
1076void
1077NBNode::computeNodeShape(double mismatchThreshold) {
1078 if (myHaveCustomPoly) {
1079 return;
1080 }
1081 if (myIncomingEdges.size() == 0 && myOutgoingEdges.size() == 0) {
1082 // may be an intermediate step during network editing
1083 myPoly.clear();
1084 myPoly.push_back(myPosition);
1085 return;
1086 }
1087 if (OptionsCont::getOptions().getFloat("default.junctions.radius") < 0) {
1088 // skip shape computation by option
1089 return;
1090 }
1091 try {
1092 NBNodeShapeComputer computer(*this);
1093 myPoly = computer.compute();
1094 if (myRadius == UNSPECIFIED_RADIUS && !OptionsCont::getOptions().isDefault("default.junctions.radius")) {
1095 myRadius = computer.getRadius();
1096 }
1097 if (myPoly.size() > 0) {
1098 PositionVector tmp = myPoly;
1099 tmp.push_back_noDoublePos(tmp[0]); // need closed shape
1100 if (mismatchThreshold >= 0
1101 && !tmp.around(myPosition)
1102 && tmp.distance2D(myPosition) > mismatchThreshold) {
1103 WRITE_WARNINGF(TL("Shape for junction '%' has distance % to its given position."), myID, tmp.distance2D(myPosition));
1104 }
1105 }
1106 } catch (InvalidArgument&) {
1107 WRITE_WARNINGF(TL("For junction '%': could not compute shape."), myID);
1108 // make sure our shape is not empty because our XML schema forbids empty attributes
1109 myPoly.clear();
1110 myPoly.push_back(myPosition);
1111 }
1112}
1113
1114
1115void
1117 // special case a):
1118 // one in, one out, the outgoing has more lanes
1119 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1120 NBEdge* in = myIncomingEdges[0];
1121 NBEdge* out = myOutgoingEdges[0];
1122 // check if it's not the turnaround
1123 if (in->getTurnDestination() == out) {
1124 // will be added later or not...
1125 return;
1126 }
1127#ifdef DEBUG_CONNECTION_GUESSING
1128 if (DEBUGCOND) {
1129 std::cout << "l2l node=" << getID() << " specialCase a\n";
1130 }
1131#endif
1132 int inOffset, outOffset, addedLanes;
1133 getReduction(out, in, outOffset, inOffset, addedLanes);
1135 && addedLanes > 0
1136 && in->isConnectedTo(out)) {
1137 const int addedRight = addedLanesRight(out, addedLanes);
1138 const int addedLeft = addedLanes - addedRight;
1139 // "straight" connections
1140 for (int i = inOffset; i < in->getNumLanes(); ++i) {
1141 in->setConnection(i, out, i - inOffset + outOffset + addedRight, NBEdge::Lane2LaneInfoType::COMPUTED);
1142 }
1143 // connect extra lane on the right
1144 for (int i = 0; i < addedRight; ++i) {
1145 in->setConnection(inOffset, out, outOffset + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1146 }
1147 // connect extra lane on the left
1148 const int inLeftMost = in->getNumLanes() - 1;
1149 const int outOffset2 = outOffset + addedRight + in->getNumLanes() - inOffset;
1150 for (int i = 0; i < addedLeft; ++i) {
1151 in->setConnection(inLeftMost, out, outOffset2 + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1152 }
1153 return;
1154 }
1155 }
1156 // special case b):
1157 // two in, one out, the outgoing has the same number of lanes as the sum of the incoming
1158 // --> highway on-ramp
1159 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 1) {
1160 NBEdge* out = myOutgoingEdges[0];
1161 NBEdge* in1 = myIncomingEdges[0];
1162 NBEdge* in2 = myIncomingEdges[1];
1163 const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1164 int in1Offset = MAX2(0, in1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1165 int in2Offset = MAX2(0, in2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1166 if (in1->getNumLanes() + in2->getNumLanes() - in1Offset - in2Offset == out->getNumLanes() - outOffset
1169 && in1 != out
1170 && in2 != out
1171 && in1->isConnectedTo(out)
1172 && in2->isConnectedTo(out)
1173 && in1->getSpecialLane(SVC_BICYCLE) == -1
1174 && in2->getSpecialLane(SVC_BICYCLE) == -1
1175 && out->getSpecialLane(SVC_BICYCLE) == -1
1176 && isLongEnough(out, MIN_WEAVE_LENGTH)) {
1177#ifdef DEBUG_CONNECTION_GUESSING
1178 if (DEBUGCOND) {
1179 std::cout << "l2l node=" << getID() << " specialCase b\n";
1180 }
1181#endif
1182 // for internal: check which one is the rightmost
1183 double a1 = in1->getAngleAtNode(this);
1184 double a2 = in2->getAngleAtNode(this);
1185 double ccw = GeomHelper::getCCWAngleDiff(a1, a2);
1186 double cw = GeomHelper::getCWAngleDiff(a1, a2);
1187 if (ccw > cw) {
1188 std::swap(in1, in2);
1189 std::swap(in1Offset, in2Offset);
1190 }
1191 in1->addLane2LaneConnections(in1Offset, out, outOffset, in1->getNumLanes() - in1Offset, NBEdge::Lane2LaneInfoType::VALIDATED, true);
1192 in2->addLane2LaneConnections(in2Offset, out, in1->getNumLanes() + outOffset - in1Offset, in2->getNumLanes() - in2Offset, NBEdge::Lane2LaneInfoType::VALIDATED, true);
1193 if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1195 }
1196 return;
1197 }
1198 }
1199 // special case c):
1200 // one in, two out, the incoming has the same number of lanes or only 1 lane less than the sum of the outgoing lanes
1201 // --> highway off-ramp
1202 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 2) {
1203 NBEdge* in = myIncomingEdges[0];
1204 NBEdge* out1 = myOutgoingEdges[0];
1205 NBEdge* out2 = myOutgoingEdges[1];
1206 const int inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1207 int out1Offset = MAX2(0, out1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1208 int out2Offset = MAX2(0, out2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1209 const int deltaLaneSum = (out2->getNumLanes() + out1->getNumLanes() - out1Offset - out2Offset) - (in->getNumLanes() - inOffset);
1210 if ((deltaLaneSum == 0 || (deltaLaneSum == 1 && in->getPermissionVariants(inOffset, in->getNumLanes()).size() == 1))
1212 && in != out1
1213 && in != out2
1214 && in->isConnectedTo(out1)
1215 && in->isConnectedTo(out2)
1216 && !in->isTurningDirectionAt(out1)
1217 && !in->isTurningDirectionAt(out2)
1218 ) {
1219#ifdef DEBUG_CONNECTION_GUESSING
1220 if (DEBUGCOND) {
1221 std::cout << "l2l node=" << getID() << " specialCase c\n";
1222 }
1223#endif
1224 // for internal: check which one is the rightmost
1225 if (NBContHelper::relative_outgoing_edge_sorter(in)(out2, out1)) {
1226 std::swap(out1, out2);
1227 std::swap(out1Offset, out2Offset);
1228 }
1229 in->addLane2LaneConnections(inOffset, out1, out1Offset, out1->getNumLanes() - out1Offset, NBEdge::Lane2LaneInfoType::VALIDATED, true);
1230 in->addLane2LaneConnections(out1->getNumLanes() + inOffset - out1Offset - deltaLaneSum, out2, out2Offset, out2->getNumLanes() - out2Offset, NBEdge::Lane2LaneInfoType::VALIDATED, false);
1231 if (in->getSpecialLane(SVC_BICYCLE) >= 0) {
1234 }
1235 return;
1236 }
1237 }
1238 // special case d):
1239 // one in, one out, the outgoing has one lane less and node has type 'zipper'
1240 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1 && myType == SumoXMLNodeType::ZIPPER) {
1241 NBEdge* in = myIncomingEdges[0];
1242 NBEdge* out = myOutgoingEdges[0];
1243 // check if it's not the turnaround
1244 if (in->getTurnDestination() == out) {
1245 // will be added later or not...
1246 return;
1247 }
1248#ifdef DEBUG_CONNECTION_GUESSING
1249 if (DEBUGCOND) {
1250 std::cout << "l2l node=" << getID() << " specialCase d\n";
1251 }
1252#endif
1253 const int inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
1254 const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1256 && in->getNumLanes() - inOffset == out->getNumLanes() - outOffset + 1
1257 && in != out
1258 && in->isConnectedTo(out)) {
1259 for (int i = inOffset; i < in->getNumLanes(); ++i) {
1260 in->setConnection(i, out, MIN2(outOffset + i, out->getNumLanes() - 1), NBEdge::Lane2LaneInfoType::COMPUTED, true);
1261 }
1262 return;
1263 }
1264 }
1265 // special case f):
1266 // one in, one out, out has reduced or same number of lanes
1267 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1268 NBEdge* in = myIncomingEdges[0];
1269 NBEdge* out = myOutgoingEdges[0];
1270 // check if it's not the turnaround
1271 if (in->getTurnDestination() == out) {
1272 // will be added later or not...
1273 return;
1274 }
1275#ifdef DEBUG_CONNECTION_GUESSING
1276 if (DEBUGCOND) {
1277 std::cout << "l2l node=" << getID() << " specialCase f\n";
1278 }
1279#endif
1280 int inOffset, outOffset, reduction;
1281 getReduction(in, out, inOffset, outOffset, reduction);
1283 && reduction >= 0
1284 && in != out
1285 && in->isConnectedTo(out)) {
1286 // in case of reduced lane number, let the rightmost lanse end
1287 inOffset += reduction;
1288 for (int i = outOffset; i < out->getNumLanes(); ++i) {
1289 in->setConnection(i + inOffset - outOffset, out, i, NBEdge::Lane2LaneInfoType::COMPUTED);
1290 }
1291 //std::cout << " special case f at node=" << getID() << " inOffset=" << inOffset << " outOffset=" << outOffset << "\n";
1292 return;
1293 }
1294 }
1295
1296 // go through this node's outgoing edges
1297 // for every outgoing edge, compute the distribution of the node's
1298 // incoming edges on this edge when approaching this edge
1299 // the incoming edges' steps will then also be marked as LANE2LANE_RECHECK...
1300 EdgeVector approaching;
1301 for (NBEdge* currentOutgoing : myOutgoingEdges) {
1302 // get the information about edges that do approach this edge
1303 getEdgesThatApproach(currentOutgoing, approaching);
1304 const int numApproaching = (int)approaching.size();
1305 if (numApproaching != 0) {
1306 ApproachingDivider divider(approaching, currentOutgoing);
1307 Bresenham::compute(&divider, numApproaching, divider.numAvailableLanes());
1308 }
1309#ifdef DEBUG_CONNECTION_GUESSING
1310 if (DEBUGCOND) {
1311 std::cout << "l2l node=" << getID() << " bresenham:\n";
1312 for (NBEdge* e : myIncomingEdges) {
1313 const std::vector<NBEdge::Connection>& elv = e->getConnections();
1314 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1315 std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << (*k).toEdge->getID() << "_" << (*k).toLane << "\n";
1316 }
1317 }
1318 }
1319#endif
1320 recheckVClassConnections(currentOutgoing);
1321
1322 // in case of lane change restrictions on the outgoing edge, ensure that
1323 // all it's lane can be reached from each connected incoming edge
1324 bool targetProhibitsChange = false;
1325 for (int i = 0; i < currentOutgoing->getNumLanes(); i++) {
1326 const NBEdge::Lane& lane = currentOutgoing->getLanes()[i];
1327 if ((lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && i + 1 < currentOutgoing->getNumLanes())
1328 || (lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && i > 0)) {
1329 targetProhibitsChange = true;
1330 break;
1331 }
1332 }
1333 if (targetProhibitsChange) {
1334 //std::cout << " node=" << getID() << " outgoing=" << currentOutgoing->getID() << " targetProhibitsChange\n";
1335 for (NBEdge* incoming : myIncomingEdges) {
1336 if (incoming->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1337 std::map<int, int> outToIn;
1338 for (const NBEdge::Connection& c : incoming->getConnections()) {
1339 if (c.toEdge == currentOutgoing) {
1340 outToIn[c.toLane] = c.fromLane;
1341 }
1342 }
1343 for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); toLane++) {
1344 if (outToIn.count(toLane) == 0) {
1345 bool added = false;
1346 // find incoming lane for neighboring outgoing
1347 for (int i = 0; i < toLane; i++) {
1348 if (outToIn.count(i) != 0) {
1349 incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1350 added = true;
1351 break;
1352 }
1353 }
1354 if (!added) {
1355 for (int i = toLane; i < currentOutgoing->getNumLanes(); i++) {
1356 if (outToIn.count(i) != 0) {
1357 incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1358 added = true;
1359 break;
1360 }
1361 }
1362 }
1363 }
1364 }
1365 }
1366 }
1367 }
1368 }
1369 // special case e): rail_crossing
1370 // there should only be straight connections here
1372 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1373 const std::vector<NBEdge::Connection> cons = (*i)->getConnections();
1374 for (std::vector<NBEdge::Connection>::const_iterator k = cons.begin(); k != cons.end(); ++k) {
1375 if (getDirection(*i, (*k).toEdge) == LinkDirection::TURN) {
1376 (*i)->removeFromConnections((*k).toEdge);
1377 }
1378 }
1379 }
1380 }
1381
1382 // ... but we may have the case that there are no outgoing edges
1383 // In this case, we have to mark the incoming edges as being in state
1384 // LANE2LANE( not RECHECK) by hand
1385 if (myOutgoingEdges.size() == 0) {
1386 for (NBEdge* incoming : myIncomingEdges) {
1387 incoming->markAsInLane2LaneState();
1388 }
1389 }
1390
1391#ifdef DEBUG_CONNECTION_GUESSING
1392 if (DEBUGCOND) {
1393 std::cout << "final connections at " << getID() << "\n";
1394 for (NBEdge* e : myIncomingEdges) {
1395 const std::vector<NBEdge::Connection>& elv = e->getConnections();
1396 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1397 std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << (*k).toEdge->getID() << "_" << (*k).toLane << "\n";
1398 }
1399 }
1400 }
1401#endif
1402}
1403
1404void
1406 int bikeLaneTarget = currentOutgoing->getSpecialLane(SVC_BICYCLE);
1407
1408 // ensure that all modes have a connection if possible
1409 for (NBEdge* incoming : myIncomingEdges) {
1410 if (incoming->getConnectionLanes(currentOutgoing).size() > 0 && incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1411 // no connections are needed for pedestrians during this step
1412 // no satisfaction is possible if the outgoing edge disallows
1413 SVCPermissions unsatisfied = incoming->getPermissions() & currentOutgoing->getPermissions() & ~SVC_PEDESTRIAN;
1414 //std::cout << "initial unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1415 const std::vector<NBEdge::Connection>& elv = incoming->getConnections();
1416 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1417 const NBEdge::Connection& c = *k;
1418 if (c.toEdge == currentOutgoing && c.toLane >= 0) {
1419 const SVCPermissions satisfied = (incoming->getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane));
1420 //std::cout << " from=" << incoming->getID() << "_" << c.fromLane << " to=" << c.toEdge->getID() << "_" << c.toLane << " satisfied=" << getVehicleClassNames(satisfied) << "\n";
1421 unsatisfied &= ~satisfied;
1422 }
1423 }
1424 if (unsatisfied != 0) {
1425#ifdef DEBUG_CONNECTION_GUESSING
1426 if (DEBUGCOND) {
1427 std::cout << " unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1428 }
1429#endif
1430 int fromLane = 0;
1431 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1432 if ((incoming->getPermissions(fromLane) & unsatisfied) != 0) {
1433 for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); ++toLane) {
1434 const SVCPermissions satisfied = incoming->getPermissions(fromLane) & currentOutgoing->getPermissions(toLane) & unsatisfied;
1435 if (satisfied != 0 && !incoming->getLaneStruct(fromLane).connectionsDone) {
1436 bool mayUseSameDestination = unsatisfied == SVC_TRAM;
1437 incoming->setConnection((int)fromLane, currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED, mayUseSameDestination);
1438#ifdef DEBUG_CONNECTION_GUESSING
1439 if (DEBUGCOND) {
1440 std::cout << " new connection from=" << fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1441 }
1442#endif
1443 unsatisfied &= ~satisfied;
1444 }
1445 }
1446 }
1447 fromLane++;
1448 }
1449#ifdef DEBUG_CONNECTION_GUESSING
1450 if (DEBUGCOND) {
1451 if (unsatisfied != 0) {
1452 std::cout << " still unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1453 }
1454 }
1455#endif
1456 }
1457 }
1458 // prevent dead-end bicycle lanes (they were excluded by the ApproachingDivider)
1459 // and the bicycle mode might already be satisfied by other lanes
1460 // assume that left-turns and turn-arounds are better satisfied from lanes to the left
1461 LinkDirection dir = getDirection(incoming, currentOutgoing);
1462 if (incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE
1463 && ((bikeLaneTarget >= 0 && dir != LinkDirection::TURN)
1465 bool builtConnection = false;
1466 for (int i = 0; i < (int)incoming->getNumLanes(); i++) {
1467 if (incoming->getPermissions(i) == SVC_BICYCLE
1468 && incoming->getConnectionsFromLane(i, currentOutgoing).size() == 0) {
1469 // find a dedicated bike lane as target
1470 if (bikeLaneTarget >= 0) {
1471 incoming->setConnection(i, currentOutgoing, bikeLaneTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1472 builtConnection = true;
1473 } else {
1474 // use any lane that allows bicycles
1475 for (int i2 = 0; i2 < (int)currentOutgoing->getNumLanes(); i2++) {
1476 if ((currentOutgoing->getPermissions(i2) & SVC_BICYCLE) != 0) {
1477 // possibly a double-connection
1478 const bool allowDouble = (incoming->getPermissions(i) == SVC_BICYCLE
1480 incoming->setConnection(i, currentOutgoing, i2, NBEdge::Lane2LaneInfoType::COMPUTED, allowDouble);
1481 builtConnection = true;
1482 break;
1483 }
1484 }
1485 }
1486 }
1487 }
1488 if (!builtConnection && bikeLaneTarget >= 0
1489 && incoming->getConnectionsFromLane(-1, currentOutgoing, bikeLaneTarget).size() == 0) {
1490 // find origin lane that allows bicycles
1491 int start = 0;
1492 int end = (int)incoming->getNumLanes();
1493 int inc = 1;
1494 if (dir == LinkDirection::TURN || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) {
1495 std::swap(start, end);
1496 inc = -1;
1497 }
1498 for (int i = start; i < end; i += inc) {
1499 if ((incoming->getPermissions(i) & SVC_BICYCLE) != 0) {
1500 incoming->setConnection(i, currentOutgoing, bikeLaneTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1501 break;
1502 }
1503 }
1504 }
1505 }
1506 }
1507}
1508
1509void
1510NBNode::getReduction(const NBEdge* in, const NBEdge* out, int& inOffset, int& outOffset, int& reduction) const {
1511 inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
1512 outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1513 reduction = (in->getNumLanes() - inOffset) - (out->getNumLanes() - outOffset);
1514}
1515
1516
1517int
1518NBNode::addedLanesRight(NBEdge* out, int addedLanes) const {
1519 if (out->isOffRamp()) {
1520 return addedLanes;
1521 }
1522 NBNode* to = out->getToNode();
1523 // check whether a right lane ends
1524 if (to->getIncomingEdges().size() == 1
1525 && to->getOutgoingEdges().size() == 1) {
1526 int inOffset, outOffset, reduction;
1527 to->getReduction(out, to->getOutgoingEdges()[0], inOffset, outOffset, reduction);
1528 if (reduction > 0) {
1529 return reduction;
1530 }
1531 }
1532 // check for the presence of right and left turns at the next intersection
1533 int outLanesRight = 0;
1534 int outLanesLeft = 0;
1535 int outLanesStraight = 0;
1536 for (NBEdge* succ : to->getOutgoingEdges()) {
1537 if (out->isConnectedTo(succ)) {
1538 const int outOffset = MAX2(0, succ->getFirstNonPedestrianLaneIndex(FORWARD, true));
1539 const int usableLanes = succ->getNumLanes() - outOffset;
1540 LinkDirection dir = to->getDirection(out, succ);
1541 if (dir == LinkDirection::STRAIGHT) {
1542 outLanesStraight += usableLanes;
1543 } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1544 outLanesRight += usableLanes;
1545 } else {
1546 outLanesLeft += usableLanes;
1547 }
1548 }
1549 }
1550 const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1551 const int usableLanes = out->getNumLanes() - outOffset;
1552 int addedTurnLanes = MIN3(
1553 addedLanes,
1554 MAX2(0, usableLanes - outLanesStraight),
1555 outLanesRight + outLanesLeft);
1556 if (outLanesLeft == 0) {
1557 return addedTurnLanes;
1558 } else {
1559 return MIN2(addedTurnLanes / 2, outLanesRight);
1560 }
1561}
1562
1563
1564bool
1565NBNode::isLongEnough(NBEdge* out, double minLength) {
1566 double seen = out->getLoadedLength();
1567 while (seen < minLength) {
1568 // advance along trivial continuations
1569 if (out->getToNode()->getOutgoingEdges().size() != 1
1570 || out->getToNode()->getIncomingEdges().size() != 1) {
1571 return false;
1572 } else {
1573 out = out->getToNode()->getOutgoingEdges()[0];
1574 seen += out->getLoadedLength();
1575 }
1576 }
1577 return true;
1578}
1579
1580
1581void
1582NBNode::getEdgesThatApproach(NBEdge* currentOutgoing, EdgeVector& approaching) {
1583 // get the position of the node to get the approaching nodes of
1584 EdgeVector::const_iterator i = std::find(myAllEdges.begin(),
1585 myAllEdges.end(), currentOutgoing);
1586 // get the first possible approaching edge
1588 // go through the list of edges clockwise and add the edges
1589 approaching.clear();
1590 for (; *i != currentOutgoing;) {
1591 // check only incoming edges
1592 if ((*i)->getToNode() == this && (*i)->getTurnDestination() != currentOutgoing) {
1593 std::vector<int> connLanes = (*i)->getConnectionLanes(currentOutgoing);
1594 if (connLanes.size() != 0) {
1595 approaching.push_back(*i);
1596 }
1597 }
1599 }
1600}
1601
1602
1603void
1604NBNode::replaceOutgoing(NBEdge* which, NBEdge* by, int laneOff) {
1605 // replace the edge in the list of outgoing nodes
1606 EdgeVector::iterator i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), which);
1607 if (i != myOutgoingEdges.end()) {
1608 (*i) = by;
1609 i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1610 (*i) = by;
1611 }
1612 // replace the edge in connections of incoming edges
1613 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); ++i) {
1614 (*i)->replaceInConnections(which, by, laneOff);
1615 }
1616 // replace within the connetion prohibition dependencies
1617 replaceInConnectionProhibitions(which, by, 0, laneOff);
1618}
1619
1620
1621void
1623 // replace edges
1624 int laneOff = 0;
1625 for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1626 replaceOutgoing(*i, by, laneOff);
1627 laneOff += (*i)->getNumLanes();
1628 }
1629 // removed double occurences
1631 // check whether this node belongs to a district and the edges
1632 // must here be also remapped
1633 if (myDistrict != nullptr) {
1634 myDistrict->replaceOutgoing(which, by);
1635 }
1636}
1637
1638
1639void
1640NBNode::replaceIncoming(NBEdge* which, NBEdge* by, int laneOff) {
1641 // replace the edge in the list of incoming nodes
1642 EdgeVector::iterator i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), which);
1643 if (i != myIncomingEdges.end()) {
1644 (*i) = by;
1645 i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1646 (*i) = by;
1647 }
1648 // replace within the connetion prohibition dependencies
1649 replaceInConnectionProhibitions(which, by, laneOff, 0);
1650}
1651
1652
1653void
1655 // replace edges
1656 int laneOff = 0;
1657 for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1658 replaceIncoming(*i, by, laneOff);
1659 laneOff += (*i)->getNumLanes();
1660 }
1661 // removed double occurences
1663 // check whether this node belongs to a district and the edges
1664 // must here be also remapped
1665 if (myDistrict != nullptr) {
1666 myDistrict->replaceIncoming(which, by);
1667 }
1668}
1669
1670
1671
1672void
1674 int whichLaneOff, int byLaneOff) {
1675 // replace in keys
1676 NBConnectionProhibits::iterator j = myBlockedConnections.begin();
1677 while (j != myBlockedConnections.end()) {
1678 bool changed = false;
1679 NBConnection c = (*j).first;
1680 if (c.replaceFrom(which, whichLaneOff, by, byLaneOff)) {
1681 changed = true;
1682 }
1683 if (c.replaceTo(which, whichLaneOff, by, byLaneOff)) {
1684 changed = true;
1685 }
1686 if (changed) {
1687 myBlockedConnections[c] = (*j).second;
1688 myBlockedConnections.erase(j);
1689 j = myBlockedConnections.begin();
1690 } else {
1691 j++;
1692 }
1693 }
1694 // replace in values
1695 for (j = myBlockedConnections.begin(); j != myBlockedConnections.end(); j++) {
1696 NBConnectionVector& prohibiting = (*j).second;
1697 for (NBConnectionVector::iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1698 NBConnection& sprohibiting = *k;
1699 sprohibiting.replaceFrom(which, whichLaneOff, by, byLaneOff);
1700 sprohibiting.replaceTo(which, whichLaneOff, by, byLaneOff);
1701 }
1702 }
1703}
1704
1705
1706
1707void
1709 // check incoming
1710 for (int i = 0; myIncomingEdges.size() > 0 && i < (int)myIncomingEdges.size() - 1; i++) {
1711 int j = i + 1;
1712 while (j < (int)myIncomingEdges.size()) {
1713 if (myIncomingEdges[i] == myIncomingEdges[j]) {
1714 myIncomingEdges.erase(myIncomingEdges.begin() + j);
1715 } else {
1716 j++;
1717 }
1718 }
1719 }
1720 // check outgoing
1721 for (int i = 0; myOutgoingEdges.size() > 0 && i < (int)myOutgoingEdges.size() - 1; i++) {
1722 int j = i + 1;
1723 while (j < (int)myOutgoingEdges.size()) {
1724 if (myOutgoingEdges[i] == myOutgoingEdges[j]) {
1725 myOutgoingEdges.erase(myOutgoingEdges.begin() + j);
1726 } else {
1727 j++;
1728 }
1729 }
1730 }
1731 // check all
1732 for (int i = 0; myAllEdges.size() > 0 && i < (int)myAllEdges.size() - 1; i++) {
1733 int j = i + 1;
1734 while (j < (int)myAllEdges.size()) {
1735 if (myAllEdges[i] == myAllEdges[j]) {
1736 myAllEdges.erase(myAllEdges.begin() + j);
1737 } else {
1738 j++;
1739 }
1740 }
1741 }
1742}
1743
1744
1745bool
1746NBNode::hasIncoming(const NBEdge* const e) const {
1747 return std::find(myIncomingEdges.begin(), myIncomingEdges.end(), e) != myIncomingEdges.end();
1748}
1749
1750
1751bool
1752NBNode::hasOutgoing(const NBEdge* const e) const {
1753 return std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), e) != myOutgoingEdges.end();
1754}
1755
1756
1757NBEdge*
1760 if (find(edges.begin(), edges.end(), e) != edges.end()) {
1761 edges.erase(find(edges.begin(), edges.end(), e));
1762 }
1763 if (edges.size() == 0) {
1764 return nullptr;
1765 }
1766 if (e->getToNode() == this) {
1767 sort(edges.begin(), edges.end(), NBContHelper::edge_opposite_direction_sorter(e, this, false));
1768 } else {
1769 sort(edges.begin(), edges.end(), NBContHelper::edge_similar_direction_sorter(e));
1770 }
1771 return edges[0];
1772}
1773
1774
1775void
1777 const NBConnection& mustStop) {
1778 if (mayDrive.getFrom() == nullptr ||
1779 mayDrive.getTo() == nullptr ||
1780 mustStop.getFrom() == nullptr ||
1781 mustStop.getTo() == nullptr) {
1782
1783 WRITE_WARNING(TL("Something went wrong during the building of a connection..."));
1784 return; // !!! mark to recompute connections
1785 }
1787 conn.push_back(mayDrive);
1788 myBlockedConnections[mustStop] = conn;
1789}
1790
1791
1792NBEdge*
1793NBNode::getPossiblySplittedIncoming(const std::string& edgeid) {
1794 int size = (int) edgeid.length();
1795 for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1796 std::string id = (*i)->getID();
1797 if (id.substr(0, size) == edgeid) {
1798 return *i;
1799 }
1800 }
1801 return nullptr;
1802}
1803
1804
1805NBEdge*
1806NBNode::getPossiblySplittedOutgoing(const std::string& edgeid) {
1807 int size = (int) edgeid.length();
1808 for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
1809 std::string id = (*i)->getID();
1810 if (id.substr(0, size) == edgeid) {
1811 return *i;
1812 }
1813 }
1814 return nullptr;
1815}
1816
1817
1818void
1819NBNode::removeEdge(NBEdge* edge, bool removeFromConnections) {
1820 EdgeVector::iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), edge);
1821 if (i != myAllEdges.end()) {
1822 myAllEdges.erase(i);
1823 i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge);
1824 if (i != myOutgoingEdges.end()) {
1825 myOutgoingEdges.erase(i);
1826 // potential self-loop
1827 i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
1828 if (i != myIncomingEdges.end()) {
1829 myIncomingEdges.erase(i);
1830 }
1831 } else {
1832 i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
1833 if (i != myIncomingEdges.end()) {
1834 myIncomingEdges.erase(i);
1835 } else {
1836 // edge must have been either incoming or outgoing
1837 assert(false);
1838 }
1839 }
1840 if (removeFromConnections) {
1841 for (i = myAllEdges.begin(); i != myAllEdges.end(); ++i) {
1842 (*i)->removeFromConnections(edge);
1843 }
1844 }
1845 // invalidate controlled connections for loaded traffic light plans
1846 const bool incoming = edge->getToNode() == this;
1847 for (NBTrafficLightDefinition* const tld : myTrafficLights) {
1848 tld->replaceRemoved(edge, -1, nullptr, -1, incoming);
1849 }
1850 }
1851}
1852
1853
1856 Position pos(0, 0);
1857 for (const NBEdge* const in : myIncomingEdges) {
1858 Position toAdd = in->getFromNode()->getPosition();
1859 toAdd.sub(myPosition);
1860 toAdd.norm2d();
1861 pos.add(toAdd);
1862 }
1863 for (const NBEdge* const out : myOutgoingEdges) {
1864 Position toAdd = out->getToNode()->getPosition();
1865 toAdd.sub(myPosition);
1866 toAdd.norm2d();
1867 pos.add(toAdd);
1868 }
1869 pos.mul(-1. / (double)(myIncomingEdges.size() + myOutgoingEdges.size()));
1870 if (pos.x() == 0. && pos.y() == 0.) {
1871 pos = Position(1, 0);
1872 }
1873 pos.norm2d();
1874 return pos;
1875}
1876
1877
1878
1879void
1881 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1882 (*i)->invalidateConnections(reallowSetting);
1883 }
1884}
1885
1886
1887void
1889 for (EdgeVector::const_iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
1890 (*i)->invalidateConnections(reallowSetting);
1891 }
1892}
1893
1894
1895bool
1896NBNode::mustBrake(const NBEdge* const from, const NBEdge* const to, int fromLane, int toLane, bool includePedCrossings) const {
1897 // unregulated->does not need to brake
1898 if (myRequest == nullptr) {
1899 return false;
1900 }
1901 // vehicles which do not have a following lane must always decelerate to the end
1902 if (to == nullptr) {
1903 return true;
1904 }
1905 // maybe we need to brake due to entering a bidi-edge
1906 if (to->isBidiEdge() && !from->isBidiEdge()) {
1907 return true;
1908 }
1909 // check whether any other connection on this node prohibits this connection
1910 return myRequest->mustBrake(from, to, fromLane, toLane, includePedCrossings);
1911}
1912
1913bool
1914NBNode::mustBrakeForCrossing(const NBEdge* const from, const NBEdge* const to, const NBNode::Crossing& crossing) const {
1915 return NBRequest::mustBrakeForCrossing(this, from, to, crossing);
1916}
1917
1918bool
1920 // code is called for connections exiting after an internal junction.
1921 // Therefore we can assume that the connection is turning and do not check
1922 // for direction or crossing priority anymore.
1923 for (auto& c : myCrossings) {
1924 if (std::find(c->edges.begin(), c->edges.end(), to) != c->edges.end()) {
1925 return true;
1926 }
1927 }
1928 return false;
1929}
1930
1931
1932bool
1933NBNode::rightTurnConflict(const NBEdge* from, const NBEdge* to, int fromLane,
1934 const NBEdge* prohibitorFrom, const NBEdge* prohibitorTo, int prohibitorFromLane) {
1935 if (from != prohibitorFrom) {
1936 return false;
1937 }
1938 if (from->isTurningDirectionAt(to)
1939 || prohibitorFrom->isTurningDirectionAt(prohibitorTo)) {
1940 // XXX should warn if there are any non-turning connections left of this
1941 return false;
1942 }
1943 // conflict if to is between prohibitorTo and from when going clockwise
1944 if (to->getStartAngle() == prohibitorTo->getStartAngle()) {
1945 // reduce rounding errors
1946 return false;
1947 }
1948 const LinkDirection d1 = from->getToNode()->getDirection(from, to);
1949 // must be a right turn to qualify as rightTurnConflict
1950 if (d1 == LinkDirection::STRAIGHT) {
1951 // no conflict for straight going connections
1952 // XXX actually this should check the main direction (which could also
1953 // be a turn)
1954 return false;
1955 } else {
1956 const LinkDirection d2 = prohibitorFrom->getToNode()->getDirection(prohibitorFrom, prohibitorTo);
1957 /* std::cout
1958 << "from=" << from->getID() << " to=" << to->getID() << " fromLane=" << fromLane
1959 << " pFrom=" << prohibitorFrom->getID() << " pTo=" << prohibitorTo->getID() << " pFromLane=" << prohibitorFromLane
1960 << " d1=" << toString(d1) << " d2=" << toString(d2)
1961 << "\n"; */
1962 bool flip = false;
1963 if (d1 == LinkDirection::LEFT || d1 == LinkDirection::PARTLEFT) {
1964 // check for leftTurnConflicht
1965 flip = !flip;
1967 // assume that the left-turning bicycle goes straight at first
1968 // and thus gets precedence over a right turning vehicle
1969 return false;
1970 }
1971 }
1972 if ((!flip && fromLane <= prohibitorFromLane) ||
1973 (flip && fromLane >= prohibitorFromLane)) {
1974 return false;
1975 }
1976 const double toAngleAtNode = fmod(to->getStartAngle() + 180, (double)360.0);
1977 const double prohibitorToAngleAtNode = fmod(prohibitorTo->getStartAngle() + 180, (double)360.0);
1978 return (flip != (GeomHelper::getCWAngleDiff(from->getEndAngle(), toAngleAtNode) <
1979 GeomHelper::getCWAngleDiff(from->getEndAngle(), prohibitorToAngleAtNode)));
1980 }
1981}
1982
1983bool
1984NBNode::mergeConflictYields(const NBEdge* from, int fromLane, int fromLaneFoe, NBEdge* to, int toLane) const {
1985 if (myRequest == nullptr) {
1986 return false;
1987 }
1988 const NBEdge::Connection& con = from->getConnection(fromLane, to, toLane);
1989 const NBEdge::Connection& prohibitorCon = from->getConnection(fromLaneFoe, to, toLane);
1990 return myRequest->mergeConflict(from, con, from, prohibitorCon, false);
1991}
1992
1993
1994bool
1996 const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
1997 if (myRequest == nullptr) {
1998 return false;
1999 }
2000 return myRequest->mergeConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2001}
2002
2003bool
2004NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
2005 const NBEdge* from2, const NBEdge* to2, int fromLane2,
2006 bool lefthand) const {
2007 UNUSED_PARAMETER(lefthand);
2008 if (from != from2 || to == to2 || fromLane == fromLane2) {
2009 return false;
2010 }
2011 if (from->isTurningDirectionAt(to)
2012 || from2->isTurningDirectionAt(to2)) {
2013 // XXX should warn if there are any non-turning connections left of this
2014 return false;
2015 }
2016 bool result = false;
2017 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2018 if (fromLane < fromLane2) {
2019 // conflict if 'to' comes before 'to2' going clockwise starting at 'from'
2020 while (*it != to2) {
2021 if (*it == to) {
2022 result = true;
2023 }
2025 }
2026 } else {
2027 // conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
2028 while (*it != to2) {
2029 if (*it == to) {
2030 result = true;
2031 }
2033 }
2034 }
2035 /*
2036 if (result) {
2037 std::cout << "turnFoes node=" << getID()
2038 << " from=" << from->getLaneID(fromLane)
2039 << " to=" << to->getID()
2040 << " from2=" << from2->getLaneID(fromLane2)
2041 << " to2=" << to2->getID()
2042 << "\n";
2043 }
2044 */
2045 return result;
2046}
2047
2048
2049bool
2050NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
2051 // when the junction has only one incoming edge, there are no
2052 // problems caused by left blockings
2053 if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
2054 return false;
2055 }
2056 double fromAngle = from->getAngleAtNode(this);
2057 double toAngle = to->getAngleAtNode(this);
2058 double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
2059 double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
2060 std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2061 do {
2063 } while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
2064 return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
2065}
2066
2067
2068bool
2069NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
2070 const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
2071 bool regardNonSignalisedLowerPriority) const {
2072 return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
2073 possProhibitedFrom, possProhibitedTo,
2074 regardNonSignalisedLowerPriority);
2075}
2076
2077
2078bool
2079NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
2080 const NBEdge* const from2, const NBEdge* const to2) const {
2081 return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
2082}
2083
2084
2085void
2087 NBEdge* removed, const EdgeVector& incoming,
2088 const EdgeVector& outgoing) {
2089 assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
2090 bool changed = true;
2091 while (changed) {
2092 changed = false;
2093 NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
2094 NBConnectionProhibits blockedConnectionsNew;
2095 // remap in connections
2096 for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
2097 const NBConnection& blocker = (*i).first;
2098 const NBConnectionVector& blocked = (*i).second;
2099 // check the blocked connections first
2100 // check whether any of the blocked must be changed
2101 bool blockedChanged = false;
2102 NBConnectionVector newBlocked;
2103 NBConnectionVector::const_iterator j;
2104 for (j = blocked.begin(); j != blocked.end(); j++) {
2105 const NBConnection& sblocked = *j;
2106 if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
2107 blockedChanged = true;
2108 }
2109 }
2110 // adapt changes if so
2111 for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
2112 const NBConnection& sblocked = *j;
2113 if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
2114 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2115 !!! newBlocked.push_back(NBConnection(*k, *k));
2116 }*/
2117 } else if (sblocked.getFrom() == removed) {
2118 assert(sblocked.getTo() != removed);
2119 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2120 newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
2121 }
2122 } else if (sblocked.getTo() == removed) {
2123 assert(sblocked.getFrom() != removed);
2124 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2125 newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
2126 }
2127 } else {
2128 newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
2129 }
2130 }
2131 if (blockedChanged) {
2132 blockedConnectionsNew[blocker] = newBlocked;
2133 changed = true;
2134 }
2135 // if the blocked were kept
2136 else {
2137 if (blocker.getFrom() == removed && blocker.getTo() == removed) {
2138 changed = true;
2139 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2140 !!! blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
2141 }*/
2142 } else if (blocker.getFrom() == removed) {
2143 assert(blocker.getTo() != removed);
2144 changed = true;
2145 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2146 blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
2147 }
2148 } else if (blocker.getTo() == removed) {
2149 assert(blocker.getFrom() != removed);
2150 changed = true;
2151 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2152 blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
2153 }
2154 } else {
2155 blockedConnectionsNew[blocker] = blocked;
2156 }
2157 }
2158 }
2159 myBlockedConnections = blockedConnectionsNew;
2160 }
2161 // remap in traffic lights
2162 tc.remapRemoved(removed, incoming, outgoing);
2163}
2164
2165
2166NBEdge*
2167NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
2168 EdgeVector::const_iterator i = itOut;
2169 while (*i != incoming) {
2170 if (clockwise) {
2172 } else {
2174 }
2175 if ((*i)->getFromNode() != this) {
2176 // only look for outgoing edges
2177 // @note we use myAllEdges to stop at the incoming edge
2178 continue;
2179 }
2180 if (incoming->isTurningDirectionAt(*i)) {
2181 return nullptr;
2182 }
2183 if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
2184 return *i;
2185 }
2186 }
2187 return nullptr;
2188}
2189
2190
2191bool
2192NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
2193 if (candidate != nullptr) {
2194 const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
2195 // they are too similar it does not matter
2196 if (fabs(angle - candAngle) < 5.) {
2197 return false;
2198 }
2199 // the other edge is at least 5 degree straighter
2200 if (fabs(candAngle) < fabs(angle) - 5.) {
2201 return true;
2202 }
2203 if (fabs(angle) < fabs(candAngle) - 5.) {
2204 return false;
2205 }
2206 if (fabs(candAngle) < 44.) {
2207 // the lane count for the same modes is larger
2208 const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
2209 if (candModeLanes > modeLanes) {
2210 return true;
2211 }
2212 if (candModeLanes < modeLanes) {
2213 return false;
2214 }
2215 // we would create a left turn
2216 if (candAngle < 0 && angle > 0) {
2217 return true;
2218 }
2219 if (angle < 0 && candAngle > 0) {
2220 return false;
2221 }
2222 }
2223 }
2224 return false;
2225}
2226
2227
2229NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
2230 // ok, no connection at all -> dead end
2231 if (outgoing == nullptr) {
2232 return LinkDirection::NODIR;
2233 }
2234 if (incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT && outgoing->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2236 }
2237 // turning direction
2238 if (incoming->isTurningDirectionAt(outgoing)) {
2240 }
2241 // get the angle between incoming/outgoing at the junction
2242 const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
2243 // ok, should be a straight connection
2244 EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
2245 SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
2246 if (vehPerm != SVC_PEDESTRIAN) {
2247 vehPerm &= ~SVC_PEDESTRIAN;
2248 }
2249 const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
2250 if (fabs(angle) < 44.) {
2251 if (fabs(angle) > 6.) {
2252 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
2254 }
2255 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
2257 }
2258 }
2259 if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2260 return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
2261 }
2263 }
2264
2265 if (angle > 0) {
2266 // check whether any other edge goes further to the right
2267 if (angle > 90) {
2268 return LinkDirection::RIGHT;
2269 }
2270 NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
2271 if (outCW != nullptr) {
2273 } else {
2274 return LinkDirection::RIGHT;
2275 }
2276 } else {
2277 // check whether any other edge goes further to the left
2278 if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
2280 } else if (angle < -90) {
2281 return LinkDirection::LEFT;
2282 }
2283 NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
2284 if (outCCW != nullptr) {
2286 } else {
2287 return LinkDirection::LEFT;
2288 }
2289 }
2290}
2291
2292
2294NBNode::getLinkState(const NBEdge* incoming, NBEdge* outgoing, int fromlane, int toLane,
2295 bool mayDefinitelyPass, const std::string& tlID) const {
2297 return LINKSTATE_MAJOR; // the trains must run on time
2298 }
2299 if (tlID != "") {
2300 return mustBrake(incoming, outgoing, fromlane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
2301 }
2302 if (outgoing == nullptr) { // always off
2304 }
2306 && mustBrake(incoming, outgoing, fromlane, toLane, true)) {
2307 return LINKSTATE_EQUAL; // all the same
2308 }
2310 return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
2311 }
2312 if (myType == SumoXMLNodeType::ZIPPER && mustBrake(incoming, outgoing, fromlane, toLane, false)) {
2313 return LINKSTATE_ZIPPER;
2314 }
2315 if (!mayDefinitelyPass
2316 && mustBrake(incoming, outgoing, fromlane, toLane, true)
2317 // legacy mode
2318 && (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
2319 // avoid linkstate minor at pure railway nodes
2322 }
2323 // traffic lights are not regarded here
2324 return LINKSTATE_MAJOR;
2325}
2326
2327bool
2329 std::string reason;
2330 return checkIsRemovableReporting(reason);
2331}
2332
2333bool
2334NBNode::checkIsRemovableReporting(std::string& reason) const {
2335 if (getEdges().empty()) {
2336 return true;
2337 }
2338 // check whether this node is included in a traffic light or crossing
2339 if (myTrafficLights.size() != 0) {
2340 reason = "TLS";
2341 return false;
2342 }
2344 reason = "rail_signal";
2345 return false;
2346 }
2347 if (myCrossings.size() != 0) {
2348 reason = "crossing";
2349 return false;
2350 }
2351 EdgeVector::const_iterator i;
2352 // one in, one out -> just a geometry ...
2353 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2354 // ... if types match ...
2355 if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2356 reason = "edges incompatible: " + reason;
2357 return false;
2358 }
2359 if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
2360 reason = "turnaround";
2361 return false;
2362 }
2363 return true;
2364 }
2365 // two in, two out -> may be something else
2366 if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
2367 // check whether the origin nodes of the incoming edges differ
2368 std::set<NBNode*> origSet;
2369 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2370 origSet.insert((*i)->getFromNode());
2371 }
2372 if (origSet.size() < 2) {
2373 // overlapping case
2374 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2375 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2376 return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
2377 myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
2378 || (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
2379 myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
2380 }
2381 }
2382 // check whether this node is an intermediate node of
2383 // a two-directional street
2384 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2385 // each of the edges must have an opposite direction edge
2386 NBEdge* opposite = (*i)->getTurnDestination(true);
2387 if (opposite != nullptr) {
2388 // the other outgoing edges must be the continuation of the current
2389 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2390 // check whether the types allow joining
2391 if (!(*i)->expandableBy(continuation, reason)) {
2392 reason = "edges incompatible: " + reason;
2393 return false;
2394 }
2395 } else {
2396 // ok, at least one outgoing edge is not an opposite
2397 // of an incoming one
2398 reason = "not opposites";
2399 return false;
2400 }
2401 }
2402 return true;
2403 }
2404 // ok, a real node
2405 reason = "intersection";
2406 return false;
2407}
2408
2409
2410std::vector<std::pair<NBEdge*, NBEdge*> >
2412 assert(checkIsRemovable());
2413 std::vector<std::pair<NBEdge*, NBEdge*> > ret;
2414 // one in, one out-case
2415 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2416 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2417 return ret;
2418 }
2419 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
2420 // two in, two out-case
2421 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2422 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2423 // overlapping edges
2424 std::string reason;
2425 if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2426 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2427 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
2428 } else {
2429 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
2430 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
2431 }
2432 return ret;
2433 }
2434 }
2435 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2436 // join with the edge that is not a turning direction
2437 NBEdge* opposite = (*i)->getTurnDestination(true);
2438 assert(opposite != 0);
2439 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2440 ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
2441 }
2442 return ret;
2443}
2444
2445
2446const PositionVector&
2448 return myPoly;
2449}
2450
2451
2452void
2454 myPoly = shape;
2455 myHaveCustomPoly = (myPoly.size() > 1);
2456 if (myHaveCustomPoly) {
2457 for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
2458 (*i)->resetNodeBorder(this);
2459 }
2460 }
2461}
2462
2463
2464NBEdge*
2466 for (NBEdge* e : myOutgoingEdges) {
2467 if (e->getToNode() == n && e->getPermissions() != 0) {
2468 return e;
2469 }
2470 }
2471 return nullptr;
2472}
2473
2474
2475bool
2477 if (isDistrict()) {
2478 return false;
2479 }
2480 for (const NBEdge* const t : getEdges()) {
2481 const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
2482 for (const NBEdge* const k : other->getEdges()) {
2483 if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
2484 return true;
2485 }
2486 }
2487 }
2488 return false;
2489}
2490
2491
2492bool
2495}
2496
2497
2498int
2500#ifdef DEBUG_PED_STRUCTURES
2502#endif
2503 int numGuessed = 0;
2504 if (myCrossings.size() > 0 || myDiscardAllCrossings) {
2505 // user supplied crossings, do not guess
2506 return numGuessed;
2507 }
2508 if (gDebugFlag1) {
2509 std::cout << "guess crossings for " << getID() << "\n";
2510 }
2512 // check for pedestrial lanes going clockwise around the node
2513 std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
2514 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2515 NBEdge* edge = *it;
2516 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2517 if (edge->getFromNode() == this) {
2518 for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2519 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2520 }
2521 } else {
2522 for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2523 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2524 }
2525 }
2526 }
2527 // do we even have a pedestrian lane?
2528 int firstSidewalk = -1;
2529 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2530 if (normalizedLanes[i].second) {
2531 firstSidewalk = i;
2532 break;
2533 }
2534 }
2535 int hadCandidates = 0;
2536 std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
2537 if (firstSidewalk != -1) {
2538 // rotate lanes to ensure that the first one allows pedestrians
2539 std::vector<std::pair<NBEdge*, bool> > tmp;
2540 copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
2541 copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
2542 normalizedLanes = tmp;
2543 // find candidates
2544 EdgeVector candidates;
2545 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2546 NBEdge* edge = normalizedLanes[i].first;
2547 const bool allowsPed = normalizedLanes[i].second;
2548 if (gDebugFlag1) {
2549 std::cout << " cands=" << toString(candidates) << " edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n";
2550 }
2551 if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
2552 candidates.push_back(edge);
2553 } else if (allowsPed) {
2554 if (candidates.size() > 0) {
2555 if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
2556 hadCandidates++;
2557 const int n = checkCrossing(candidates);
2558 numGuessed += n;
2559 if (n > 0) {
2560 connectedCandidates.push_back(n);
2561 }
2562 }
2563 candidates.clear();
2564 }
2565 }
2566 }
2567 if (hadCandidates > 0 && candidates.size() > 0) {
2568 // avoid wrapping around to the same sidewalk
2569 hadCandidates++;
2570 const int n = checkCrossing(candidates);
2571 numGuessed += n;
2572 if (n > 0) {
2573 connectedCandidates.push_back(n);
2574 }
2575 }
2576 }
2577 // Avoid duplicate crossing between the same pair of walkingareas
2578 if (gDebugFlag1) {
2579 std::cout << " hadCandidates=" << hadCandidates << " connectedCandidates=" << toString(connectedCandidates) << "\n";
2580 }
2581 if (hadCandidates == 2 && connectedCandidates.size() == 2) {
2582 // One or both of them might be split: remove the one with less splits
2583 if (connectedCandidates.back() <= connectedCandidates.front()) {
2584 numGuessed -= connectedCandidates.back();
2585 myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
2586 } else {
2587 numGuessed -= connectedCandidates.front();
2588 myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
2589 }
2590 }
2592 if (gDebugFlag1) {
2593 std::cout << "guessedCrossings:\n";
2594 for (auto& crossing : myCrossings) {
2595 std::cout << " edges=" << toString(crossing->edges) << "\n";
2596 }
2597 }
2598 if (numGuessed > 0 && isSimpleContinuation(true, true)) {
2599 // avoid narrow node shape when there is a crossing
2600 computeNodeShape(-1);
2601 for (NBEdge* e : myAllEdges) {
2602 e->computeEdgeShape();
2603 }
2604 }
2605 return numGuessed;
2606}
2607
2608
2609int
2611 if (gDebugFlag1) {
2612 std::cout << "checkCrossing candidates=" << toString(candidates) << "\n";
2613 }
2614 if (candidates.size() == 0) {
2615 if (gDebugFlag1) {
2616 std::cout << "no crossing added (numCandidates=" << candidates.size() << ")\n";
2617 }
2618 return 0;
2619 } else {
2620 // check whether the edges may be part of a common crossing due to having similar angle
2621 double prevAngle = -100000; // dummy
2622 for (int i = 0; i < (int)candidates.size(); ++i) {
2623 NBEdge* edge = candidates[i];
2624 double angle = edge->getCrossingAngle(this);
2625 // edges should be sorted by angle but this only holds true approximately
2626 if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
2627 if (gDebugFlag1) {
2628 std::cout << "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n";
2629 }
2630 return 0;
2631 }
2632 if (!isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
2633 if (gDebugFlag1) {
2634 std::cout << "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n";
2635 }
2636 return 0;
2637 }
2638 prevAngle = angle;
2639 }
2640 if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
2642 if (gDebugFlag1) {
2643 std::cout << "adding crossing: " << toString(candidates) << "\n";
2644 }
2645 return 1;
2646 } else {
2647 // check for intermediate walking areas
2648 prevAngle = -100000; // dummy
2649 for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
2650 double angle = (*it)->getCrossingAngle(this);
2651 if (it != candidates.begin()) {
2652 NBEdge* prev = *(it - 1);
2653 NBEdge* curr = *it;
2654 Position prevPos, currPos;
2655 int laneI;
2656 // compute distance between candiate edges
2657 double intermediateWidth = 0;
2658 if (prev->getToNode() == this) {
2659 laneI = prev->getNumLanes() - 1;
2660 prevPos = prev->getLanes()[laneI].shape[-1];
2661 } else {
2662 laneI = 0;
2663 prevPos = prev->getLanes()[laneI].shape[0];
2664 }
2665 intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
2666 if (curr->getFromNode() == this) {
2667 laneI = curr->getNumLanes() - 1;
2668 currPos = curr->getLanes()[laneI].shape[0];
2669 } else {
2670 laneI = 0;
2671 currPos = curr->getLanes()[laneI].shape[-1];
2672 }
2673 intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
2674 intermediateWidth += currPos.distanceTo2D(prevPos);
2675 if (gDebugFlag1) {
2676 std::cout
2677 << " prevAngle=" << prevAngle
2678 << " angle=" << angle
2679 << " intermediateWidth=" << intermediateWidth
2680 << "\n";
2681 }
2682 if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
2683 || (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
2684 return checkCrossing(EdgeVector(candidates.begin(), it))
2685 + checkCrossing(EdgeVector(it, candidates.end()));
2686 }
2687 }
2688 prevAngle = angle;
2689 }
2691 if (gDebugFlag1) {
2692 std::cout << "adding crossing: " << toString(candidates) << "\n";
2693 }
2694 return 1;
2695 }
2696 }
2697}
2698
2699
2700bool
2702 // sort edge vector
2703 std::sort(edges.begin(), edges.end());
2704 // iterate over crossing to find a crossing with the same edges
2705 for (auto& crossing : myCrossings) {
2706 // sort edges of crossing before compare
2707 EdgeVector edgesOfCrossing = crossing->edges;
2708 std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
2709 if (edgesOfCrossing == edges) {
2710 return true;
2711 }
2712 }
2713 return false;
2714}
2715
2716
2717bool
2718NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
2719 for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
2720 if (!normalizedLanes[i].second) {
2721 return true;
2722 }
2723 }
2724 return false;
2725}
2726
2727
2728void
2731 buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
2732 OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
2733 // ensure that all crossings are properly connected
2734 bool recheck = myCrossings.size() > 0;
2735 while (recheck) {
2736 recheck = false;
2737 std::set<std::string> waIDs;
2738 int numSidewalks = 0;
2739 for (WalkingArea& wa : myWalkingAreas) {
2740 waIDs.insert(wa.id);
2741 numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
2742 }
2743 if (numSidewalks < 2) {
2744 // all crossings are invalid if there are fewer than 2 sidewalks involved
2745 waIDs.clear();
2746 }
2747 for (auto& crossing : myCrossings) {
2748 if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
2749 if (crossing->valid) {
2750 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
2751 crossing->id, getID(), toString(crossing->edges));
2752 recheck = true;
2753 }
2754 for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
2755 WalkingArea& wa = *waIt;
2756 std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
2757 if (it_nc != wa.nextCrossings.end()) {
2758 wa.nextCrossings.erase(it_nc);
2759 }
2760 if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
2761 waIt = myWalkingAreas.erase(waIt);
2762 recheck = true;
2763 } else {
2764 waIt++;
2765 }
2766 }
2767 crossing->valid = false;
2768 crossing->prevWalkingArea = "";
2769 crossing->nextWalkingArea = "";
2770 }
2771 }
2772 }
2773}
2774
2775
2776std::vector<NBNode::Crossing*>
2778 std::vector<Crossing*> result;
2779 for (auto& c : myCrossings) {
2780 if (c->valid) {
2781 result.push_back(c.get());
2782 }
2783 }
2784 //if (myCrossings.size() > 0) {
2785 // std::cout << "valid crossings at " << getID() << "\n";
2786 // for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
2787 // std::cout << " " << toString((*it)->edges) << "\n";
2788 // }
2789 //}
2790 return result;
2791}
2792
2793
2794void
2796 myCrossings.clear();
2797 // also discard all further crossings
2798 if (rejectAll) {
2799 myDiscardAllCrossings = true;
2800 }
2801}
2802
2803
2804void
2806 myWalkingAreas.clear();
2807}
2808
2809
2810double
2812 // myDisplacementError is computed during this operation. reset first
2814 // build inner edges for vehicle movements across the junction
2815 int noInternalNoSplits = 0;
2816 for (const NBEdge* const edge : myIncomingEdges) {
2817 for (const NBEdge::Connection& con : edge->getConnections()) {
2818 if (con.toEdge == nullptr) {
2819 continue;
2820 }
2821 noInternalNoSplits++;
2822 }
2823 }
2824 int lno = 0;
2825 int splitNo = 0;
2826 double maxCrossingSeconds = 0.;
2827 for (NBEdge* const edge : myIncomingEdges) {
2828 maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
2829 }
2830 return maxCrossingSeconds;
2831}
2832
2833
2834int
2836#ifdef DEBUG_PED_STRUCTURES
2838#endif
2839 if (gDebugFlag1) {
2840 std::cout << "build crossings for " << getID() << ":\n";
2841 }
2843 myCrossings.clear();
2844 }
2845 int index = 0;
2846 const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
2847 for (auto& c : myCrossings) {
2848 c->valid = true;
2849 if (!isTLControlled()) {
2850 c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
2851 }
2852 c->id = ":" + getID() + "_c" + toString(index++);
2853 c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
2854 // reset fields, so repeated computation (Netedit) will sucessfully perform the checks
2855 // in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
2856 c->nextWalkingArea = "";
2857 c->prevWalkingArea = "";
2858 EdgeVector& edges = c->edges;
2859 if (gDebugFlag1) {
2860 std::cout << " crossing=" << c->id << " edges=" << toString(edges);
2861 }
2862 // sorting the edges in the right way is imperative. We want to sort
2863 // them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
2864 std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
2865 if (gDebugFlag1) {
2866 std::cout << " sortedEdges=" << toString(edges) << "\n";
2867 };
2868 // rotate the edges so that the largest relative angle difference comes at the end
2869 std::vector<double> rawAngleDiffs;
2870 double maxAngleDiff = 0;
2871 int maxAngleDiffIndex = 0; // index before maxDist
2872 for (int i = 0; i < (int) edges.size(); i++) {
2873 double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
2874 edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
2875 if (diff < 0) {
2876 diff += 360;
2877 }
2878 const double rawDiff = NBHelpers::relAngle(
2879 edges[i]->getAngleAtNodeNormalized(this),
2880 edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
2881 rawAngleDiffs.push_back(fabs(rawDiff));
2882
2883 if (gDebugFlag1) {
2884 std::cout << " i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n";
2885 }
2886 if (diff > maxAngleDiff) {
2887 maxAngleDiff = diff;
2888 maxAngleDiffIndex = i;
2889 }
2890 }
2891 if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
2892 // if the angle differences is too small, we better not rotate
2893 std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
2894 if (gDebugFlag1) {
2895 std::cout << " rotatedEdges=" << toString(edges);
2896 }
2897 }
2898 bool diagonalCrossing = false;
2899 std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
2900 if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
2901 diagonalCrossing = true;
2902 if (gDebugFlag1) {
2903 std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
2904 for (auto e : edges) {
2905 std::cout << " e=" << e->getID()
2906 << " aC=" << e->getAngleAtNodeToCenter(this)
2907 << " a=" << e->getAngleAtNode(this)
2908 << " aN=" << e->getAngleAtNodeNormalized(this)
2909 << "\n";
2910 }
2911 }
2912 }
2913 // reverse to get them in CCW order (walking direction around the node)
2914 std::reverse(edges.begin(), edges.end());
2915 // compute shape
2916 c->shape.clear();
2917 const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
2918 const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
2919 int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
2920 int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
2921 if (gDebugFlag1) {
2922 std::cout << " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n";
2923 }
2924 if (firstNonPedLane < 0 || lastNonPedLane < 0) {
2925 // invalid crossing
2926 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
2927 c->valid = false;
2928 // compute surrogate shape to make it visible in netedit
2929 firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
2930 lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
2931 }
2932 if (c->customShape.size() != 0) {
2933 c->shape = c->customShape;
2934 } else {
2935 NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
2936 NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
2937 crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
2938 crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
2939 crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
2940 crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
2941 crossingBeg.shape.extrapolate(c->width / 2);
2942 crossingEnd.shape.extrapolate(c->width / 2);
2943 // check if after all changes shape are NAN (in these case, discard)
2944 if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
2945 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
2946 c->valid = false;
2947 } else {
2948 c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
2949 c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
2950 }
2951 if (diagonalCrossing) {
2952 c->shape.move2side(-c->width);
2953 }
2954 }
2955 }
2956 return index;
2957}
2958
2959
2960void
2961NBNode::buildWalkingAreas(int cornerDetail, double joinMinDist) {
2962#ifdef DEBUG_PED_STRUCTURES
2964#endif
2965 int index = 0;
2966 myWalkingAreas.clear();
2967 if (gDebugFlag1) {
2968 std::cout << "build walkingAreas for " << getID() << ":\n";
2969 }
2970 if (myAllEdges.size() == 0) {
2971 return;
2972 }
2974 // shapes are all pointing away from the intersection
2975 std::vector<std::pair<NBEdge*, NBEdge::Lane> > normalizedLanes;
2976 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2977 NBEdge* edge = *it;
2978 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2979 if (edge->getFromNode() == this) {
2980 for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2981 NBEdge::Lane l = *it_l;
2982 l.shape = l.shape.getSubpartByIndex(0, 2);
2984 normalizedLanes.push_back(std::make_pair(edge, l));
2985 }
2986 } else {
2987 for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2988 NBEdge::Lane l = *it_l;
2989 l.shape = l.shape.reverse();
2990 l.shape = l.shape.getSubpartByIndex(0, 2);
2992 normalizedLanes.push_back(std::make_pair(edge, l));
2993 }
2994 }
2995 }
2996 //if (gDebugFlag1) std::cout << " normalizedLanes=" << normalizedLanes.size() << "\n";
2997 // collect [start,count[ indices in normalizedLanes that belong to a walkingArea
2998 std::vector<std::pair<int, int> > waIndices;
2999 int start = -1;
3000 NBEdge* prevEdge = normalizedLanes.back().first;
3001 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
3002 NBEdge* edge = normalizedLanes[i].first;
3003 NBEdge::Lane& l = normalizedLanes[i].second;
3004 if (start == -1) {
3005 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3006 start = i;
3007 }
3008 } else {
3009 if ((l.permissions & SVC_PEDESTRIAN) == 0
3010 || crossingBetween(edge, prevEdge)
3011 || alreadyConnectedPaths(edge, prevEdge, joinMinDist)
3012 || crossesFringe(edge, prevEdge)
3013 ) {
3014 waIndices.push_back(std::make_pair(start, i - start));
3015 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3016 start = i;
3017 } else {
3018 start = -1;
3019 }
3020
3021 }
3022 }
3023 if (gDebugFlag1) std::cout << " i=" << i << " edge=" << edge->getID() << " start=" << start << " ped=" << ((l.permissions & SVC_PEDESTRIAN) != 0)
3024 << " waI=" << waIndices.size() << " crossingBetween=" << crossingBetween(edge, prevEdge) << "\n";
3025 prevEdge = edge;
3026 }
3027 // deal with wrap-around issues
3028 if (start != - 1) {
3029 const int waNumLanes = (int)normalizedLanes.size() - start;
3030 if (waIndices.size() == 0) {
3031 waIndices.push_back(std::make_pair(start, waNumLanes));
3032 if (gDebugFlag1) {
3033 std::cout << " single wa, end at wrap-around\n";
3034 }
3035 } else {
3036 if (waIndices.front().first == 0) {
3037 NBEdge* edge = normalizedLanes.front().first;
3038 if (crossingBetween(edge, normalizedLanes.back().first)
3039 || crossesFringe(edge, normalizedLanes.back().first)) {
3040 // do not wrap-around (see above)
3041 waIndices.push_back(std::make_pair(start, waNumLanes));
3042 if (gDebugFlag1) {
3043 std::cout << " do not wrap around\n";
3044 }
3045 } else {
3046 // first walkingArea wraps around
3047 waIndices.front().first = start;
3048 waIndices.front().second = waNumLanes + waIndices.front().second;
3049 if (gDebugFlag1) {
3050 std::cout << " wrapping around\n";
3051 }
3052 }
3053 } else {
3054 // last walkingArea ends at the wrap-around
3055 waIndices.push_back(std::make_pair(start, waNumLanes));
3056 if (gDebugFlag1) {
3057 std::cout << " end at wrap-around\n";
3058 }
3059 }
3060 }
3061 }
3062 if (gDebugFlag1) {
3063 std::cout << " normalizedLanes=" << normalizedLanes.size() << " waIndices:\n";
3064 for (int i = 0; i < (int)waIndices.size(); ++i) {
3065 std::cout << " " << waIndices[i].first << ", " << waIndices[i].second << "\n";
3066 }
3067 }
3068 // build walking areas connected to a sidewalk
3069 for (int i = 0; i < (int)waIndices.size(); ++i) {
3070 const bool buildExtensions = waIndices[i].second != (int)normalizedLanes.size();
3071 const int startIdx = waIndices[i].first;
3072 const int prev = startIdx > 0 ? startIdx - 1 : (int)normalizedLanes.size() - 1;
3073 const int count = waIndices[i].second;
3074 const int end = (startIdx + count) % normalizedLanes.size();
3075 const int lastIdx = (startIdx + count - 1) % normalizedLanes.size();
3076
3077 WalkingArea wa(":" + getID() + "_w" + toString(index++), 1);
3078 if (gDebugFlag1) {
3079 std::cout << "build walkingArea " << wa.id << " start=" << startIdx << " end=" << end << " count=" << count << " prev=" << prev << ":\n";
3080 }
3081 double endCrossingWidth = 0;
3082 double startCrossingWidth = 0;
3083 PositionVector endCrossingShape;
3084 PositionVector startCrossingShape;
3085 // check for connected crossings
3086 bool connectsCrossing = false;
3087 std::vector<Position> connectedPoints;
3088 for (auto c : getCrossings()) {
3089 if (gDebugFlag1) {
3090 std::cout << " crossing=" << c->id << " sortedEdges=" << toString(c->edges) << "\n";
3091 }
3092 if (c->edges.back() == normalizedLanes[end].first
3093 && (normalizedLanes[end].second.permissions & SVC_PEDESTRIAN) == 0) {
3094 // crossing ends
3095 if (c->nextWalkingArea != "") {
3096 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' targets '%' and '%'."),
3097 getID(), c->id, c->nextWalkingArea, wa.id);
3098 c->valid = false;
3099 }
3100 c->nextWalkingArea = wa.id;
3101 wa.prevCrossings.push_back(c->id);
3102 if ((int)c->edges.size() < wa.minPrevCrossingEdges) {
3103 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3104 endCrossingWidth = c->width;
3105 endCrossingShape = c->shape;
3106 wa.width = MAX2(wa.width, endCrossingWidth);
3107 connectsCrossing = true;
3108 connectedPoints.push_back(c->shape[-1]);
3109 wa.minPrevCrossingEdges = (int)c->edges.size();
3110 }
3111 if (gDebugFlag1) {
3112 std::cout << " crossing " << c->id << " ends\n";
3113 }
3114 }
3115 if (c->edges.front() == normalizedLanes[prev].first
3116 && (normalizedLanes[prev].second.permissions & SVC_PEDESTRIAN) == 0) {
3117 // crossing starts
3118 if (c->prevWalkingArea != "") {
3119 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' is targeted by '%' and '%'."),
3120 getID(), c->id, c->prevWalkingArea, wa.id);
3121 c->valid = false;
3122 }
3123 c->prevWalkingArea = wa.id;
3124 wa.nextCrossings.push_back(c->id);
3125 if ((int)c->edges.size() < wa.minNextCrossingEdges) {
3126 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3127 startCrossingWidth = c->width;
3128 startCrossingShape = c->shape;
3129 wa.width = MAX2(wa.width, startCrossingWidth);
3130 connectsCrossing = true;
3131 connectedPoints.push_back(c->shape[0]);
3132 wa.minNextCrossingEdges = (int)c->edges.size();
3133 }
3134 if (gDebugFlag1) {
3135 std::cout << " crossing " << c->id << " starts\n";
3136 }
3137 }
3138 if (gDebugFlag1) std::cout << " check connections to crossing " << c->id
3139 << " cFront=" << c->edges.front()->getID() << " cBack=" << c->edges.back()->getID()
3140 << " wEnd=" << normalizedLanes[end].first->getID() << " wStart=" << normalizedLanes[startIdx].first->getID()
3141 << " wStartPrev=" << normalizedLanes[prev].first->getID()
3142 << "\n";
3143 }
3144 if (count < 2 && !connectsCrossing) {
3145 // not relevant for walking
3146 if (gDebugFlag1) {
3147 std::cout << " not relevant for walking: count=" << count << " connectsCrossing=" << connectsCrossing << "\n";
3148 }
3149 continue;
3150 }
3151 // build shape and connections
3152 std::set<NBEdge*, ComparatorIdLess> connected;
3153 for (int j = 0; j < count; ++j) {
3154 const int nlI = (startIdx + j) % normalizedLanes.size();
3155 NBEdge* edge = normalizedLanes[nlI].first;
3156 NBEdge::Lane l = normalizedLanes[nlI].second;
3157 wa.width = MAX2(wa.width, l.width);
3158 if (connected.count(edge) == 0) {
3159 if (edge->getFromNode() == this) {
3160 wa.nextSidewalks.push_back(edge->getSidewalkID());
3161 connectedPoints.push_back(edge->getLaneShape(0)[0]);
3162 } else {
3163 wa.prevSidewalks.push_back(edge->getSidewalkID());
3164 connectedPoints.push_back(edge->getLaneShape(0)[-1]);
3165 }
3166 connected.insert(edge);
3167 }
3168 l.shape.move2side(-l.width / 2);
3170 l.shape.move2side(l.width);
3171 wa.shape.push_back(l.shape[0]);
3172 }
3173 if (buildExtensions) {
3174 // extension at starting crossing
3175 if (startCrossingShape.size() > 0) {
3176 if (gDebugFlag1) {
3177 std::cout << " extension at startCrossing shape=" << startCrossingShape << "\n";
3178 }
3179 startCrossingShape.move2side(startCrossingWidth / 2);
3180 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // right corner
3181 startCrossingShape.move2side(-startCrossingWidth);
3182 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // left corner goes first
3183 }
3184 // extension at ending crossing
3185 if (endCrossingShape.size() > 0) {
3186 if (gDebugFlag1) {
3187 std::cout << " extension at endCrossing shape=" << endCrossingShape << "\n";
3188 }
3189 endCrossingShape.move2side(endCrossingWidth / 2);
3190 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3191 endCrossingShape.move2side(-endCrossingWidth);
3192 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3193 }
3194 }
3195 if (connected.size() == 2 && !connectsCrossing && wa.nextSidewalks.size() == 1 && wa.prevSidewalks.size() == 1
3196 && normalizedLanes.size() == 2) {
3197 // do not build a walkingArea since a normal connection exists
3198 NBEdge* e1 = *connected.begin();
3199 NBEdge* e2 = *(++connected.begin());
3200 if (e1->hasConnectionTo(e2, 0, 0) || e2->hasConnectionTo(e1, 0, 0)) {
3201 if (gDebugFlag1) {
3202 std::cout << " not building a walkingarea since normal connections exist\n";
3203 }
3204 continue;
3205 }
3206 }
3207 // build smooth inner curve (optional)
3208 if (cornerDetail > 0) {
3209 int smoothEnd = end;
3210 int smoothPrev = prev;
3211 // extend to green verge
3212 if (endCrossingWidth > 0 && normalizedLanes[smoothEnd].second.permissions == 0) {
3213 smoothEnd = (smoothEnd + 1) % normalizedLanes.size();
3214 }
3215 if (startCrossingWidth > 0 && normalizedLanes[smoothPrev].second.permissions == 0) {
3216 if (smoothPrev == 0) {
3217 smoothPrev = (int)normalizedLanes.size() - 1;
3218 } else {
3219 smoothPrev--;
3220 }
3221 }
3222 PositionVector begShape = normalizedLanes[smoothEnd].second.shape;
3223 begShape = begShape.reverse();
3224 PositionVector begShapeOuter = normalizedLanes[lastIdx].second.shape;
3225 begShapeOuter = begShapeOuter.reverse();
3226 //begShape.extrapolate(endCrossingWidth);
3227 begShape.move2side(normalizedLanes[smoothEnd].second.width / 2);
3228 begShapeOuter.move2side(normalizedLanes[lastIdx].second.width / 2);
3229 PositionVector endShape = normalizedLanes[smoothPrev].second.shape;
3230 PositionVector endShapeOuter = normalizedLanes[startIdx].second.shape;;
3231 endShape.move2side(normalizedLanes[smoothPrev].second.width / 2);
3232 endShapeOuter.move2side(normalizedLanes[startIdx].second.width / 2);
3233 //endShape.extrapolate(startCrossingWidth);
3234 PositionVector curve;
3235 if (count != (int)normalizedLanes.size() || count == 2) {
3236 if ((normalizedLanes[smoothEnd].first->getPermissions() & normalizedLanes[smoothPrev].first->getPermissions() &
3237 ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
3238 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25);
3239 if (curve.length2D() - begShape.back().distanceTo2D(endShape.front()) > 5) {
3240 // recompute less bulging curve
3241 //std::cout << " directLength=" << begShape.back().distanceTo2D(endShape.front()) << " curveLength=" << curve.length2D()
3242 // << " delta=" << curve.length2D() - begShape.back().distanceTo2D(endShape.front()) << "\n";
3243 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3244 }
3245 } else {
3246 const double extend = MIN2(10.0, begShape.back().distanceTo2D(endShape.front()) / 2);
3247 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3248 }
3249 if (gDebugFlag1) std::cout
3250 << " end=" << smoothEnd << " prev=" << smoothPrev
3251 << " endCrossingWidth=" << endCrossingWidth << " startCrossingWidth=" << startCrossingWidth
3252 << " begShape=" << begShape << " endShape=" << endShape << " smooth curve=" << curve
3253 << " begShapeOuter=" << begShapeOuter << " endShapeOuter=" << endShapeOuter
3254 << "\n";
3255 if (curve.size() > 2) {
3256 curve.erase(curve.begin());
3257 curve.pop_back();
3258 if (endCrossingWidth > 0) {
3259 wa.shape.pop_back();
3260 }
3261 if (startCrossingWidth > 0) {
3262 wa.shape.erase(wa.shape.begin());
3263 }
3264 if (count == (int)normalizedLanes.size()) {
3265 curve = curve.reverse();
3266 }
3267 wa.shape.append(curve, 0);
3268 }
3269 }
3270 if (curve.size() > 2 && count == 2) {
3271 const double innerDist = begShape.back().distanceTo2D(endShape[0]);
3272 const double outerDist = begShapeOuter.back().distanceTo2D(endShapeOuter[0]);
3273 if (gDebugFlag1) {
3274 std::cout << " innerDist=" << innerDist << " outerDist=" << outerDist << "\n";
3275 }
3276 if (outerDist > innerDist) {
3277 // we also need a rounded outer curve (unless we have only a single walkingarea)
3278 const double extend = MIN2(10.0, begShapeOuter.back().distanceTo2D(endShapeOuter.front()) / 2);
3279 curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3280 curve = curve.reverse();
3281 wa.shape.erase(wa.shape.begin() + 1, wa.shape.begin() + 3);
3282 wa.shape.insert(wa.shape.begin() + 1, curve.begin(), curve.end());
3283 if (gDebugFlag1) {
3284 std::cout << " outerCurve=" << curve << "\n";
3285 }
3286 }
3287 }
3288 }
3289 // apply custom shapes
3290 if (myWalkingAreaCustomShapes.size() > 0) {
3291 for (auto wacs : myWalkingAreaCustomShapes) {
3292 // every edge in wasc.edges must be part of connected
3293 if ((wacs.shape.size() != 0 || wacs.width != NBEdge::UNSPECIFIED_WIDTH) && includes(connected, wacs.edges)) {
3294 if (wacs.shape.size() != 0) {
3295 wa.shape = wacs.shape;
3296 }
3297 if (wacs.width != NBEdge::UNSPECIFIED_WIDTH) {
3298 wa.width = wacs.width;
3299 }
3300 wa.hasCustomShape = true;
3301 }
3302 }
3303 }
3304 // determine length (average of all possible connections)
3305 double lengthSum = 0;
3306 int combinations = 0;
3307 for (std::vector<Position>::const_iterator it1 = connectedPoints.begin(); it1 != connectedPoints.end(); ++it1) {
3308 for (std::vector<Position>::const_iterator it2 = connectedPoints.begin(); it2 != connectedPoints.end(); ++it2) {
3309 const Position& p1 = *it1;
3310 const Position& p2 = *it2;
3311 if (p1 != p2) {
3312 lengthSum += p1.distanceTo2D(p2);
3313 combinations += 1;
3314 }
3315 }
3316 }
3317 if (gDebugFlag1) {
3318 std::cout << " combinations=" << combinations << " connectedPoints=" << connectedPoints << "\n";
3319 }
3320 wa.length = POSITION_EPS;
3321 if (combinations > 0) {
3322 wa.length = MAX2(POSITION_EPS, lengthSum / combinations);
3323 }
3324 myWalkingAreas.push_back(wa);
3325 }
3326 // build walkingAreas between split crossings
3327 std::vector<Crossing*> validCrossings = getCrossings();
3328 for (std::vector<Crossing*>::iterator it = validCrossings.begin(); it != validCrossings.end(); ++it) {
3329 Crossing& prev = **it;
3330 Crossing& next = (it != validCrossings.begin() ? **(it - 1) :** (validCrossings.end() - 1));
3331 if (gDebugFlag1) {
3332 std::cout << " checkIntermediate: prev=" << prev.id << " next=" << next.id << " prev.nextWA=" << prev.nextWalkingArea << "\n";
3333 }
3334 if (prev.nextWalkingArea == "") {
3335 if (next.prevWalkingArea != "" || &prev == &next) {
3336 WRITE_WARNINGF(TL("Invalid pedestrian topology: crossing '%' across [%] has no target."), prev.id, toString(prev.edges));
3337 prev.valid = false;
3338 continue;
3339 }
3340 WalkingArea wa(":" + getID() + "_w" + toString(index++), prev.width);
3341 prev.nextWalkingArea = wa.id;
3342 wa.nextCrossings.push_back(next.id);
3343 next.prevWalkingArea = wa.id;
3344 // back of previous crossing
3345 PositionVector tmp = prev.shape;
3346 tmp.move2side(-prev.width / 2);
3347 wa.shape.push_back(tmp[-1]);
3348 tmp.move2side(prev.width);
3349 wa.shape.push_back(tmp[-1]);
3350 // front of next crossing
3351 tmp = next.shape;
3352 tmp.move2side(prev.width / 2);
3353 wa.shape.push_back(tmp[0]);
3354 tmp.move2side(-prev.width);
3355 wa.shape.push_back(tmp[0]);
3356 // apply custom shapes
3357 if (myWalkingAreaCustomShapes.size() > 0) {
3358 std::set<NBEdge*, ComparatorIdLess> crossed(prev.edges.begin(), prev.edges.end());
3359 crossed.insert(next.edges.begin(), next.edges.end());
3360 for (auto wacs : myWalkingAreaCustomShapes) {
3361 // every edge in wacs.edges must be part of crossed
3362 if (wacs.shape.size() != 0 && wacs.edges.size() > 1 && includes(crossed, wacs.edges)) {
3363 wa.shape = wacs.shape;
3364 wa.hasCustomShape = true;
3365 }
3366 }
3367 }
3368 // length (special case)
3369 wa.length = MAX2(POSITION_EPS, prev.shape.back().distanceTo2D(next.shape.front()));
3370 myWalkingAreas.push_back(wa);
3371 if (gDebugFlag1) {
3372 std::cout << " build wa=" << wa.id << "\n";
3373 }
3374 }
3375 }
3376}
3377
3378bool
3379NBNode::includes(const std::set<NBEdge*, ComparatorIdLess>& super,
3380 const std::set<const NBEdge*, ComparatorIdLess>& sub) {
3381 // for some reason std::include does not work reliably
3382 for (const NBEdge* e : sub) {
3383 if (super.count(const_cast<NBEdge*>(e)) == 0) {
3384 return false;
3385 }
3386 }
3387 return true;
3388}
3389
3390
3391bool
3392NBNode::crossingBetween(const NBEdge* e1, const NBEdge* e2) const {
3393 if (e1 == e2) {
3394 return false;
3395 }
3396 if (myAllEdges.size() > 3) {
3397 // pedestrian scramble
3398 return false;
3399 }
3400 for (auto c : getCrossings()) {
3401 const EdgeVector& edges = c->edges;
3402 EdgeVector::const_iterator it1 = std::find(edges.begin(), edges.end(), e1);
3403 EdgeVector::const_iterator it2 = std::find(edges.begin(), edges.end(), e2);
3404 if (it1 != edges.end() && it2 != edges.end()) {
3405 return true;
3406 }
3407 }
3408 return false;
3409}
3410
3411
3412bool
3413NBNode::alreadyConnectedPaths(const NBEdge* e1, const NBEdge* e2, double dist) const {
3414 if (e1 == e2) {
3415 return false;
3416 }
3417 if (e1->getPermissions() != SVC_PEDESTRIAN
3418 || e2->getPermissions() != SVC_PEDESTRIAN) {
3419 // no paths
3420 return false;
3421 }
3422 if (e1->getFinalLength() > dist &&
3423 e2->getFinalLength() > dist) {
3424 // too long
3425 return false;
3426 }
3427 NBNode* other1 = e1->getFromNode() == this ? e1->getToNode() : e1->getFromNode();
3428 NBNode* other2 = e2->getFromNode() == this ? e2->getToNode() : e2->getFromNode();
3429 return other1 == other2;
3430}
3431
3432
3433bool
3434NBNode::crossesFringe(const NBEdge* e1, const NBEdge* e2) const {
3436 && myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1
3437 && (e1->isTurningDirectionAt(e2) || e2->isTurningDirectionAt(e1));
3438}
3439
3440
3442NBNode::edgesBetween(const NBEdge* e1, const NBEdge* e2) const {
3443 EdgeVector result;
3444 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), e1);
3445 assert(it != myAllEdges.end());
3447 EdgeVector::const_iterator it_end = std::find(myAllEdges.begin(), myAllEdges.end(), e2);
3448 assert(it_end != myAllEdges.end());
3449 while (it != it_end) {
3450 result.push_back(*it);
3452 }
3453 return result;
3454}
3455
3456
3457void
3458NBNode::addWalkingAreaShape(EdgeVector edges, const PositionVector& shape, double width) {
3460 wacs.edges.insert(edges.begin(), edges.end());
3461 wacs.shape = shape;
3462 wacs.width = width;
3463 myWalkingAreaCustomShapes.push_back(wacs);
3464}
3465
3466
3467bool
3470}
3471
3472bool
3473NBNode::geometryLike(const EdgeVector& incoming, const EdgeVector& outgoing) const {
3474 if (incoming.size() == 1 && outgoing.size() == 1) {
3475 return true;
3476 }
3477 if (incoming.size() == 2 && outgoing.size() == 2) {
3478 // check whether the incoming and outgoing edges are pairwise (near) parallel and
3479 // thus the only cross-connections could be turn-arounds
3480 NBEdge* in0 = incoming[0];
3481 NBEdge* in1 = incoming[1];
3482 NBEdge* out0 = outgoing[0];
3483 NBEdge* out1 = outgoing[1];
3484 if ((in0->isTurningDirectionAt(out0) || in0->isTurningDirectionAt(out1))
3485 && (in1->isTurningDirectionAt(out0) || in1->isTurningDirectionAt(out1))) {
3486 return true;
3487 }
3488 if (in0->getGeometry() == in1->getGeometry() && out0->getGeometry() == out1->getGeometry()) {
3489 // overlapping edges
3490 return true;
3491 }
3492 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
3493 NBEdge* inEdge = *it;
3494 double angle0 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(this), out0->getAngleAtNode(this)));
3495 double angle1 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(this), out1->getAngleAtNode(this)));
3496 if (MAX2(angle0, angle1) <= 160) {
3497 // neither of the outgoing edges is parallel to inEdge
3498 return false;
3499 }
3500 }
3501 return true;
3502 }
3503 return false;
3504}
3505
3506void
3510 }
3511}
3512
3513bool
3515 for (NBEdge* out : myOutgoingEdges) {
3516 if (out->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
3517 return true;
3518 }
3519 }
3520 return false;
3521}
3522
3524NBNode::addCrossing(EdgeVector edges, double width, bool priority, int tlIndex, int tlIndex2,
3525 const PositionVector& customShape, bool fromSumoNet) {
3526 Crossing* c = new Crossing(this, edges, width, priority, tlIndex, tlIndex2, customShape);
3527 myCrossings.push_back(std::unique_ptr<Crossing>(c));
3528 if (fromSumoNet) {
3530 }
3531 return c;
3532}
3533
3534
3535void
3537 EdgeSet edgeSet(edges.begin(), edges.end());
3538 for (auto it = myCrossings.begin(); it != myCrossings.end();) {
3539 EdgeSet edgeSet2((*it)->edges.begin(), (*it)->edges.end());
3540 if (edgeSet == edgeSet2) {
3541 it = myCrossings.erase(it);
3542 } else {
3543 ++it;
3544 }
3545 }
3546}
3547
3548
3550NBNode::getCrossing(const std::string& id) const {
3551 for (auto& c : myCrossings) {
3552 if (c->id == id) {
3553 return c.get();
3554 }
3555 }
3556 throw ProcessError("Request for unknown crossing '" + id + "'");
3557}
3558
3559
3561NBNode::getCrossing(const EdgeVector& edges, bool hardFail) const {
3562 const EdgeSet edgeSet(edges.begin(), edges.end());
3563 for (auto& crossing : myCrossings) {
3564 const EdgeSet edgeSet2(crossing->edges.begin(), crossing->edges.end());
3565 if (edgeSet == edgeSet2) {
3566 return crossing.get();
3567 }
3568 }
3569 if (!hardFail) {
3570 return nullptr;
3571 }
3572 throw ProcessError("Request for unknown crossing for the given Edges");
3573}
3574
3575
3577NBNode::getWalkingArea(const std::string& id) {
3578 for (auto& walkingArea : myWalkingAreas) {
3579 if (walkingArea.id == id) {
3580 return walkingArea;
3581 }
3582 }
3583 throw ProcessError("Request for unknown crossing for the given Edges");
3584}
3585
3586
3587bool
3588NBNode::setCrossingTLIndices(const std::string& tlID, int startIndex) {
3589 bool usedCustom = false;
3590 for (auto c : getCrossings()) {
3591 c->tlLinkIndex = startIndex++;
3592 c->tlID = tlID;
3593 if (c->customTLIndex != -1) {
3594 usedCustom |= (c->tlLinkIndex != c->customTLIndex);
3595 c->tlLinkIndex = c->customTLIndex;
3596 }
3597 c->tlLinkIndex2 = c->customTLIndex2;
3598 }
3599 return usedCustom;
3600}
3601
3602
3603int
3605 if (myRequest == nullptr) {
3606 // could be an uncontrolled type
3607 int result = 0;
3608 for (const NBEdge* const edge : myIncomingEdges) {
3609 result += (int)edge->getConnections().size();
3610 }
3611 return result;
3612 } else {
3613 return myRequest->getSizes().second;
3614 }
3615}
3616
3617
3618int
3620 int result = 0;
3621 for (const NBEdge* const e : myIncomingEdges) {
3622 for (const NBEdge::Connection& cand : e->getConnections()) {
3623 if (e == from && cand.fromLane == con.fromLane && cand.toLane == con.toLane && cand.toEdge == con.toEdge) {
3624 return result;
3625 }
3626 result++;
3627 }
3628 }
3629 return -1;
3630}
3631
3632
3635 /* Conceptually, the center point would be identical with myPosition.
3636 * However, if the shape is influenced by custom geometry endpoints of the adjoining edges,
3637 * myPosition may fall outside the shape. In this case it is better to use
3638 * the center of the shape
3639 **/
3640 PositionVector tmp = myPoly;
3641 tmp.closePolygon();
3642 //std::cout << getID() << " around=" << tmp.around(myPosition) << " dist=" << tmp.distance2D(myPosition) << "\n";
3643 if (tmp.size() < 3 || tmp.around(myPosition) || tmp.distance2D(myPosition) < POSITION_EPS) {
3644 return myPosition;
3645 }
3646 return myPoly.getPolygonCenter();
3647}
3648
3649
3652 EdgeVector result = myAllEdges;
3653 if (gDebugFlag1) {
3654 std::cout << " angles:\n";
3655 for (EdgeVector::const_iterator it = result.begin(); it != result.end(); ++it) {
3656 std::cout << " edge=" << (*it)->getID() << " edgeAngle=" << (*it)->getAngleAtNode(this) << " angleToShape=" << (*it)->getAngleAtNodeToCenter(this) << "\n";
3657 }
3658 std::cout << " allEdges before: " << toString(result) << "\n";
3659 }
3660 sort(result.begin(), result.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3661 // let the first edge in myAllEdges remain the first
3662 if (gDebugFlag1) {
3663 std::cout << " allEdges sorted: " << toString(result) << "\n";
3664 }
3665 rotate(result.begin(), std::find(result.begin(), result.end(), *myAllEdges.begin()), result.end());
3666 if (gDebugFlag1) {
3667 std::cout << " allEdges rotated: " << toString(result) << "\n";
3668 }
3669 return result;
3670}
3671
3672
3673void
3675 // simple case: edges with LaneSpreadFunction::CENTER and a (possible) turndirection at the same node
3676 for (EdgeVector::iterator it = myIncomingEdges.begin(); it != myIncomingEdges.end(); it++) {
3677 NBEdge* edge = *it;
3678 NBEdge* turnDest = edge->getTurnDestination(true);
3679 if (turnDest != nullptr) {
3680 edge->shiftPositionAtNode(this, turnDest);
3681 turnDest->shiftPositionAtNode(this, edge);
3682 }
3683 }
3684 // @todo: edges in the same direction with sharp angles starting/ending at the same position
3685}
3686
3687
3688bool
3690 return type == SumoXMLNodeType::TRAFFIC_LIGHT
3693}
3694
3695
3696bool
3697NBNode::rightOnRedConflict(int index, int foeIndex) const {
3699 if (def->rightOnRedConflict(index, foeIndex)) {
3700 return true;
3701 }
3702 }
3703 return false;
3704}
3705
3706
3707void
3708NBNode::sortEdges(bool useNodeShape) {
3709 if (myAllEdges.size() == 0) {
3710 return;
3711 }
3712 EdgeVector allEdgesOriginal = myAllEdges;
3713 EdgeVector& allEdges = myAllEdges;
3714 EdgeVector& incoming = myIncomingEdges;
3715 EdgeVector& outgoing = myOutgoingEdges;
3716
3717 // sort the edges by angle (this is the canonical sorting)
3718 std::sort(allEdges.begin(), allEdges.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3719 std::sort(incoming.begin(), incoming.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3720 std::sort(outgoing.begin(), outgoing.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3721 std::vector<NBEdge*>::iterator j;
3722 for (j = allEdges.begin(); j != allEdges.end() - 1 && j != allEdges.end(); ++j) {
3724 }
3725 if (allEdges.size() > 1 && j != allEdges.end()) {
3726 NBNodesEdgesSorter::swapWhenReversed(this, allEdges.end() - 1, allEdges.begin());
3727 }
3728
3729 // sort again using additional geometry information
3730 NBEdge* firstOfAll = allEdges.front();
3731 NBEdge* firstOfIncoming = incoming.size() > 0 ? incoming.front() : 0;
3732 NBEdge* firstOfOutgoing = outgoing.size() > 0 ? outgoing.front() : 0;
3733 // sort by the angle between the node shape center and the point where the edge meets the node shape
3734 std::sort(allEdges.begin(), allEdges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3735 std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3736 std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3737 // let the first edge remain the first
3738 rotate(allEdges.begin(), std::find(allEdges.begin(), allEdges.end(), firstOfAll), allEdges.end());
3739 if (firstOfIncoming != nullptr) {
3740 rotate(incoming.begin(), std::find(incoming.begin(), incoming.end(), firstOfIncoming), incoming.end());
3741 }
3742 if (firstOfOutgoing != nullptr) {
3743 rotate(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), firstOfOutgoing), outgoing.end());
3744 }
3745#ifdef DEBUG_EDGE_SORTING
3746 if (DEBUGCOND) {
3747 std::cout << "sortedEdges:\n";
3748 for (NBEdge* e : allEdges) {
3749 std::cout << " " << e->getID()
3750 << " angleToCenter=" << e->getAngleAtNodeToCenter(this)
3751 << " junctionAngle=" << e->getAngleAtNode(this) << "\n";
3752 }
3753 }
3754#endif
3755
3756 // fixing some pathological all edges orderings
3757 // if every of the edges a,b,c has a turning edge a',b',c' the all edges ordering should be a,a',b,b',c,c'
3758 if (incoming.size() == outgoing.size() && incoming.front() == allEdges.front()) {
3759 std::vector<NBEdge*>::const_iterator in, out;
3760 std::vector<NBEdge*> allTmp;
3761 for (in = incoming.begin(), out = outgoing.begin(); in != incoming.end(); ++in, ++out) {
3762 if ((*in)->isTurningDirectionAt(*out)) {
3763 allTmp.push_back(*in);
3764 allTmp.push_back(*out);
3765 } else {
3766 break;
3767 }
3768 }
3769 if (allTmp.size() == allEdges.size()) {
3770 allEdges = allTmp;
3771 }
3772 }
3773 // sort the crossings
3774 std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, allEdges));
3775 //if (crossings.size() > 0) {
3776 // std::cout << " crossings at " << getID() << "\n";
3777 // for (std::vector<NBNode::Crossing*>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
3778 // std::cout << " " << toString((*it)->edges) << "\n";
3779 // }
3780 //}
3781
3782 if (useNodeShape && myAllEdges != allEdgesOriginal) {
3783 // sorting order changed after node shape was computed.
3784 computeNodeShape(-1);
3785 for (NBEdge* e : myAllEdges) {
3786 e->computeEdgeShape();
3787 }
3788 }
3789}
3790
3791std::vector<std::pair<Position, std::string> >
3793 // using a set would be nicer but we want to have some slack in position identification
3794 std::vector<std::pair<Position, std::string> >result;
3795 for (NBEdge* e : myAllEdges) {
3796 Position pos = this == e->getFromNode() ? e->getGeometry().front() : e->getGeometry().back();
3797 const std::string origID = e->getParameter(this == e->getFromNode() ? "origFrom" : "origTo");
3798 bool unique = true;
3799 for (const auto& pair : result) {
3800 if (pos.almostSame(pair.first) || (origID != "" && pair.second == origID)) {
3801 unique = false;
3802 break;
3803 }
3804 }
3805 if (unique) {
3806 result.push_back(std::make_pair(pos, origID));
3807 }
3808 }
3809 return result;
3810}
3811
3812
3813/****************************************************************************/
@ DEFAULT
default cursor
#define DEG2RAD(x)
Definition: GeomHelper.h:35
#define RAD2DEG(x)
Definition: GeomHelper.h:36
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:266
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:265
#define TL(string)
Definition: MsgHandler.h:282
std::map< NBConnection, NBConnectionVector > NBConnectionProhibits
Definition of a container for connection block dependencies Includes a list of all connections which ...
std::vector< NBConnection > NBConnectionVector
Definition of a connection vector.
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:42
@ KEEPCLEAR_FALSE
Definition: NBCont.h:59
@ KEEPCLEAR_UNSPECIFIED
Definition: NBCont.h:61
#define DEBUGCOND
Definition: NBNode.cpp:74
#define EXTEND_CROSSING_ANGLE_THRESHOLD
Definition: NBNode.cpp:60
#define MIN_WEAVE_LENGTH
Definition: NBNode.cpp:66
#define SPLIT_CROSSING_WIDTH_THRESHOLD
Definition: NBNode.cpp:62
#define SPLIT_CROSSING_ANGLE_THRESHOLD
Definition: NBNode.cpp:63
#define DEBUGCOND2(obj)
Definition: NBNode.cpp:75
const SVCPermissions SVCAll
all VClasses are allowed
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission is a railway edge.
const std::string & getVehicleClassNames(SVCPermissions permissions, bool expand)
Returns the ids of the given classes, divided using a ' '.
bool isForbidden(SVCPermissions permissions)
Returns whether an edge with the given permission is a forbidden edge.
@ SVC_IGNORING
vehicles ignoring classes
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_BICYCLE
vehicle is a bicycle
@ SVC_TRAM
vehicle is a light rail
@ SVC_PEDESTRIAN
pedestrian
@ UNKNOWN
not defined
int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
FringeType
classifying boundary nodes
LinkDirection
The different directions a link between two lanes may take (or a stream between two edges)....
@ PARTLEFT
The link is a partial left direction.
@ RIGHT
The link is a (hard) right direction.
@ TURN
The link is a 180 degree turn.
@ LEFT
The link is a (hard) left direction.
@ STRAIGHT
The link is a straight direction.
@ TURN_LEFTHAND
The link is a 180 degree turn (left-hand network)
@ PARTRIGHT
The link is a partial right direction.
@ NODIR
The link has no direction (is a dead end link)
LinkState
The right-of-way state of a link between two lanes used when constructing a NBTrafficLightLogic,...
@ LINKSTATE_ALLWAY_STOP
This is an uncontrolled, all-way stop link.
@ LINKSTATE_MAJOR
This is an uncontrolled, major link, may pass.
@ LINKSTATE_STOP
This is an uncontrolled, minor link, has to stop.
@ LINKSTATE_EQUAL
This is an uncontrolled, right-before-left link.
@ LINKSTATE_ZIPPER
This is an uncontrolled, zipper-merge link.
@ LINKSTATE_TL_OFF_BLINKING
The link is controlled by a tls which is off and blinks, has to brake.
@ LINKSTATE_MINOR
This is an uncontrolled, minor link, has to brake.
@ LINKSTATE_TL_OFF_NOSIGNAL
The link is controlled by a tls which is off, not blinking, may pass.
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
bool gDebugFlag1
global utility flags for debugging
Definition: StdDefs.cpp:33
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
#define UNUSED_PARAMETER(x)
Definition: StdDefs.h:30
T MIN3(T a, T b, T c)
Definition: StdDefs.h:84
T MIN2(T a, T b)
Definition: StdDefs.h:71
T MAX2(T a, T b)
Definition: StdDefs.h:77
#define SUMO_MAX_CONNECTIONS
the maximum number of connections across an intersection
Definition: StdDefs.h:41
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static void compute(BresenhamCallBack *callBack, const int val1, const int val2)
Definition: Bresenham.cpp:32
static double getCCWAngleDiff(double angle1, double angle2)
Returns the distance of second angle from first angle counter-clockwise.
Definition: GeomHelper.cpp:153
static double getCWAngleDiff(double angle1, double angle2)
Returns the distance of second angle from first angle clockwise.
Definition: GeomHelper.cpp:163
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
NBEdge * getFrom() const
returns the from-edge (start of the connection)
bool replaceTo(NBEdge *which, NBEdge *by)
replaces the to-edge by the one given
bool replaceFrom(NBEdge *which, NBEdge *by)
replaces the from-edge by the one given
NBEdge * getTo() const
returns the to-edge (end of the connection)
Class to sort edges by their angle in relation to the given edge.
Definition: NBContHelper.h:142
static void nextCCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
static void nextCW(const EdgeVector &edges, EdgeVector::const_iterator &from)
A container for districts.
A class representing a single district.
Definition: NBDistrict.h:62
void replaceIncoming(const EdgeVector &which, NBEdge *const by)
Replaces incoming edges from the vector (sinks) by the given edge.
Definition: NBDistrict.cpp:100
void replaceOutgoing(const EdgeVector &which, NBEdge *const by)
Replaces outgoing edges from the vector (source) by the given edge.
Definition: NBDistrict.cpp:132
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:59
void erase(NBDistrictCont &dc, NBEdge *edge)
Removes the given edge from the container (deleting it)
Definition: NBEdgeCont.cpp:404
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:4137
bool isInsideTLS() const
Returns whether this edge was marked as being within an intersection.
Definition: NBEdge.h:1150
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition: NBEdge.h:608
double getCrossingAngle(NBNode *node)
return the angle for computing pedestrian crossings at the given node
Definition: NBEdge.cpp:4289
double getLaneWidth() const
Returns the default width of lanes of this edge.
Definition: NBEdge.h:648
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:552
Lane & getLaneStruct(int lane)
Definition: NBEdge.h:1426
const Connection & getConnection(int fromLane, const NBEdge *to, int toLane) const
Returns the specified connection (unmodifiable) This method goes through "myConnections" and returns ...
Definition: NBEdge.cpp:1255
const PositionVector & getGeometry() const
Returns the geometry of the edge.
Definition: NBEdge.h:787
bool isBidiRail(bool ignoreSpread=false) const
whether this edge is part of a bidirectional railway
Definition: NBEdge.cpp:762
EdgeBuildingStep getStep() const
The building step of this edge.
Definition: NBEdge.h:641
const std::vector< NBEdge::Lane > & getLanes() const
Returns the lane definitions.
Definition: NBEdge.h:736
int getFirstNonPedestrianLaneIndex(int direction, bool exclusive=false) const
return the first lane with permissions other than SVC_PEDESTRIAN and 0
Definition: NBEdge.cpp:4197
@ LANES2LANES_DONE
Lanes to lanes - relationships are computed; no recheck is necessary/wished.
@ LANES2EDGES
Lanes to edges - relationships are computed/loaded.
@ LANES2LANES_USER
Lanes to lanes - relationships are loaded; no recheck is necessary/wished.
void remapConnections(const EdgeVector &incoming)
Remaps the connection in a way that allows the removal of it.
Definition: NBEdge.cpp:1386
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:625
const std::string & getID() const
Definition: NBEdge.h:1526
bool isTurningDirectionAt(const NBEdge *const edge) const
Returns whether the given edge is the opposite direction to this edge.
Definition: NBEdge.cpp:3430
bool isBidiEdge(bool checkPotential=false) const
whether this edge is part of a bidirectional edge pair
Definition: NBEdge.cpp:773
int getNumLanes() const
Returns the number of lanes.
Definition: NBEdge.h:526
double getTotalWidth() const
Returns the combined width of all lanes of this edge.
Definition: NBEdge.cpp:3975
bool isConnectedTo(const NBEdge *e, const bool ignoreTurnaround=false) const
Returns the information whethe a connection to the given edge has been added (or computed)
Definition: NBEdge.cpp:1285
const PositionVector & getNodeBorder(const NBNode *node) const
Definition: NBEdge.cpp:740
int getNumLanesThatAllow(SVCPermissions permissions) const
get lane indices that allow the given permissions
Definition: NBEdge.cpp:4266
std::set< SVCPermissions > getPermissionVariants(int iStart, int iEnd) const
return all permission variants within the specified lane range [iStart, iEnd[
Definition: NBEdge.cpp:4254
@ VALIDATED
The connection was computed and validated.
@ COMPUTED
The connection was computed.
static PositionVector startShapeAt(const PositionVector &laneShape, const NBNode *startNode, PositionVector nodeShape)
Definition: NBEdge.cpp:902
std::string getSidewalkID()
get the lane id for the canonical sidewalk lane
Definition: NBEdge.cpp:4314
std::vector< int > getConnectionLanes(NBEdge *currentOutgoing, bool withBikes=true) const
Returns the list of lanes that may be used to reach the given edge.
Definition: NBEdge.cpp:1360
double getStartAngle() const
Returns the angle at the start of the edge (relative to the node shape center) The angle is computed ...
Definition: NBEdge.h:561
int getSpecialLane(SVCPermissions permissions) const
return index of the first lane that allows the given permissions
Definition: NBEdge.cpp:4230
bool setConnection(int lane, NBEdge *destEdge, int destLane, Lane2LaneInfoType type, bool mayUseSameDestination=false, bool mayDefinitelyPass=false, KeepClear keepClear=KEEPCLEAR_UNSPECIFIED, double contPos=UNSPECIFIED_CONTPOS, double visibility=UNSPECIFIED_VISIBILITY_DISTANCE, double speed=UNSPECIFIED_SPEED, double friction=UNSPECIFIED_FRICTION, double length=myDefaultConnectionLength, const PositionVector &customShape=PositionVector::EMPTY, const bool uncontrolled=UNSPECIFIED_CONNECTION_UNCONTROLLED, SVCPermissions permissions=SVC_UNSPECIFIED, bool indirectLeft=false, const std::string &edgeType="", SVCPermissions changeLeft=SVC_UNSPECIFIED, SVCPermissions changeRight=SVC_UNSPECIFIED, bool postProcess=false)
Adds a connection to a certain lane of a certain edge.
Definition: NBEdge.cpp:1145
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition: NBEdge.cpp:2057
bool isOffRamp() const
Definition: NBEdge.h:1402
EdgeVector getConnectedEdges() const
Returns the list of outgoing edges unsorted.
Definition: NBEdge.cpp:1335
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:545
NBEdge * getTurnDestination(bool possibleDestination=false) const
Definition: NBEdge.cpp:3767
void shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap
Definition: NBEdge.cpp:4434
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2083
static const double UNSPECIFIED_WIDTH
unspecified lane width
Definition: NBEdge.h:357
double getEndAngle() const
Returns the angle at the end of the edge (relative to the node shape center) The angle is computed in...
Definition: NBEdge.h:570
void replaceInConnections(NBEdge *which, NBEdge *by, int laneOff)
replace in current connections of edge
Definition: NBEdge.cpp:1495
bool hasConnectionTo(NBEdge *destEdge, int destLane, int fromLane=-1) const
Retrieves info about a connection to a certain lane of a certain edge.
Definition: NBEdge.cpp:1279
double getEndOffset() const
Returns the offset to the destination node.
Definition: NBEdge.h:695
bool addLane2LaneConnections(int fromLane, NBEdge *dest, int toLane, int no, Lane2LaneInfoType type, bool invalidatePrevious=false, bool mayDefinitelyPass=false)
Builds no connections starting at the given lanes.
Definition: NBEdge.cpp:1128
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition: NBEdge.cpp:962
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition: NBEdge.cpp:4469
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1347
int getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive=false) const
return the first lane with permissions other than SVC_PEDESTRIAN, SVC_BICYCLE and 0
Definition: NBEdge.cpp:4213
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition: NBHelpers.cpp:45
static double normRelAngle(double angle1, double angle2)
ensure that reverse relAngles (>=179.999) always count as turnarounds (-180)
Definition: NBHelpers.cpp:58
A loaded (complete) traffic light logic.
Computes lane-2-lane connections.
Definition: NBNode.h:85
bool myIsBikeEdge
whether the outgoing edge is exclusively used by bikes
Definition: NBNode.h:118
ApproachingDivider(const EdgeVector &approaching, NBEdge *currentOutgoing)
Constructor.
Definition: NBNode.cpp:96
~ApproachingDivider()
Destructor.
Definition: NBNode.cpp:128
const EdgeVector & myApproaching
The list of edges that approach the current edge.
Definition: NBNode.h:109
int numAvailableLanes() const
@ get number of available lanes
Definition: NBNode.h:97
std::deque< int > * spread(const std::vector< int > &approachingLanes, int dest) const
the method that spreads the wished number of lanes from the the lane given by the bresenham-call to b...
Definition: NBNode.cpp:165
NBEdge * myCurrentOutgoing
The approached current edge.
Definition: NBNode.h:112
void execute(const int src, const int dest)
the bresenham-callback
Definition: NBNode.cpp:132
std::vector< int > myAvailableLanes
The available lanes to which connections shall be built.
Definition: NBNode.h:115
A definition of a pedestrian crossing.
Definition: NBNode.h:129
Crossing(const NBNode *_node, const EdgeVector &_edges, double _width, bool _priority, int _customTLIndex, int _customTLIndex2, const PositionVector &_customShape)
constructor
Definition: NBNode.cpp:237
std::string id
the (edge)-id of this crossing
Definition: NBNode.h:144
std::string prevWalkingArea
the lane-id of the previous walkingArea
Definition: NBNode.h:146
std::string nextWalkingArea
the lane-id of the next walkingArea
Definition: NBNode.h:148
PositionVector shape
The crossing's shape.
Definition: NBNode.h:138
EdgeVector edges
The edges being crossed.
Definition: NBNode.h:136
double width
This crossing's width.
Definition: NBNode.h:142
bool valid
whether this crossing is valid (and can be written to the net.xml). This is needed for netedit becaus...
Definition: NBNode.h:162
Represents a single node (junction) during network building.
Definition: NBNode.h:66
LinkState getLinkState(const NBEdge *incoming, NBEdge *outgoing, int fromLane, int toLane, bool mayDefinitelyPass, const std::string &tlID) const
get link state
Definition: NBNode.cpp:2294
void addIncomingEdge(NBEdge *edge)
adds an incoming edge
Definition: NBNode.cpp:459
void invalidateOutgoingConnections(bool reallowSetting=false)
invalidate outgoing connections
Definition: NBNode.cpp:1888
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:2229
static const int FOUR_CONTROL_POINTS
Definition: NBNode.h:215
static const int AVOID_INTERSECTING_LEFT_TURNS
Definition: NBNode.h:216
bool hasIncoming(const NBEdge *const e) const
Returns whether the given edge ends at this node.
Definition: NBNode.cpp:1746
void addWalkingAreaShape(EdgeVector edges, const PositionVector &shape, double width)
add custom shape for walkingArea
Definition: NBNode.cpp:3458
void avoidOverlap()
fix overlap
Definition: NBNode.cpp:3674
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition: NBNode.cpp:1819
std::vector< WalkingAreaCustomShape > myWalkingAreaCustomShapes
Vector of custom walking areas shapes.
Definition: NBNode.h:884
Position getCenter() const
Returns a position that is guaranteed to lie within the node shape.
Definition: NBNode.cpp:3634
bool mustBrake(const NBEdge *const from, const NBEdge *const to, int fromLane, int toLane, bool includePedCrossings) const
Returns the information whether the described flow must let any other flow pass.
Definition: NBNode.cpp:1896
void removeCrossing(const EdgeVector &edges)
remove a pedestrian crossing from this node (identified by its edges)
Definition: NBNode.cpp:3536
NBEdge * getNextCompatibleOutgoing(const NBEdge *incoming, SVCPermissions vehPerm, EdgeVector::const_iterator start, bool clockwise) const
Definition: NBNode.cpp:2167
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition: NBNode.cpp:479
int getConnectionIndex(const NBEdge *from, const NBEdge::Connection &con) const
return the index of the given connection
Definition: NBNode.cpp:3619
void reinit(const Position &position, SumoXMLNodeType type, bool updateEdgeGeometries=false)
Resets initial values.
Definition: NBNode.cpp:307
int numNormalConnections() const
return the number of lane-to-lane connections at this junction (excluding crossings)
Definition: NBNode.cpp:3604
bool setCrossingTLIndices(const std::string &tlID, int startIndex)
Definition: NBNode.cpp:3588
static const double UNSPECIFIED_RADIUS
unspecified lane width
Definition: NBNode.h:210
Crossing * getCrossing(const std::string &id) const
return the crossing with the given id
Definition: NBNode.cpp:3550
NBNode(const std::string &id, const Position &position, SumoXMLNodeType type)
Constructor.
Definition: NBNode.cpp:256
bool forbidsPedestriansAfter(std::vector< std::pair< NBEdge *, bool > > normalizedLanes, int startIndex)
return whether there is a non-sidewalk lane after the given index;
Definition: NBNode.cpp:2718
bool needsCont(const NBEdge *fromE, const NBEdge *otherFromE, const NBEdge::Connection &c, const NBEdge::Connection &otherC) const
whether an internal junction should be built at from and respect other
Definition: NBNode.cpp:875
void recheckVClassConnections(NBEdge *currentOutgoing)
ensure connectivity for all vClasses
Definition: NBNode.cpp:1405
void buildCrossingsAndWalkingAreas()
build crossings, and walkingareas. Also removes invalid loaded crossings if wished
Definition: NBNode.cpp:2729
static const int BACKWARD
Definition: NBNode.h:207
bool rightOnRedConflict(int index, int foeIndex) const
whether the given index must yield to the foeIndex while turing right on a red light
Definition: NBNode.cpp:3697
SumoXMLNodeType getType() const
Returns the type of this node.
Definition: NBNode.h:275
void computeLogic2(bool checkLaneFoes)
compute right-of-way logic for all lane-to-lane connections
Definition: NBNode.cpp:993
bool myTypeWasGuessed
whether the node type was guessed rather than loaded
Definition: NBNode.h:937
void setCustomShape(const PositionVector &shape)
set the junction shape
Definition: NBNode.cpp:2453
void computeNodeShape(double mismatchThreshold)
Compute the junction shape for this node.
Definition: NBNode.cpp:1077
void buildWalkingAreas(int cornerDetail, double joinMinDist)
build pedestrian walking areas and set connections from/to walkingAreas
Definition: NBNode.cpp:2961
void remapRemoved(NBTrafficLightLogicCont &tc, NBEdge *removed, const EdgeVector &incoming, const EdgeVector &outgoing)
remap removed
Definition: NBNode.cpp:2086
int buildCrossings()
build pedestrian crossings
Definition: NBNode.cpp:2835
SumoXMLNodeType myType
The type of the junction.
Definition: NBNode.h:887
EdgeVector myOutgoingEdges
Vector of outgoing edges.
Definition: NBNode.h:872
bool myKeepClear
whether the junction area must be kept clear
Definition: NBNode.h:911
static bool isTrafficLight(SumoXMLNodeType type)
return whether the given type is a traffic light
Definition: NBNode.cpp:3689
void discardWalkingareas()
discard previously built walkingareas (required for repeated computation by netedit)
Definition: NBNode.cpp:2805
void computeLogic(const NBEdgeCont &ec)
computes the node's type, logic and traffic light
Definition: NBNode.cpp:954
void invalidateIncomingConnections(bool reallowSetting=false)
invalidate incoming connections
Definition: NBNode.cpp:1880
NBRequest * myRequest
Node requests.
Definition: NBNode.h:902
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:258
void mirrorX()
mirror coordinates along the x-axis
Definition: NBNode.cpp:346
std::vector< std::pair< Position, std::string > > getEndPoints() const
return list of unique endpoint coordinates of all edges at this node
Definition: NBNode.cpp:3792
static bool rightTurnConflict(const NBEdge *from, const NBEdge *to, int fromLane, const NBEdge *prohibitorFrom, const NBEdge *prohibitorTo, int prohibitorFromLane)
return whether the given laneToLane connection is a right turn which must yield to a bicycle crossing...
Definition: NBNode.cpp:1933
std::vector< std::pair< NBEdge *, NBEdge * > > getEdgesToJoin() const
get edges to join
Definition: NBNode.cpp:2411
bool myHaveCustomPoly
whether this nodes shape was set by the user
Definition: NBNode.h:899
Position getEmptyDir() const
Returns something like the most unused direction Should only be used to add source or sink nodes.
Definition: NBNode.cpp:1855
PositionVector indirectLeftShape(const PositionVector &begShape, const PositionVector &endShape, int numPoints) const
compute shape of indirect left turn
Definition: NBNode.cpp:708
NBNode::Crossing * addCrossing(EdgeVector edges, double width, bool priority, int tlIndex=-1, int tlIndex2=-1, const PositionVector &customShape=PositionVector::EMPTY, bool fromSumoNet=false)
add a pedestrian crossing to this node
Definition: NBNode.cpp:3524
static const int AVOID_WIDE_RIGHT_TURN
flags for controlling shape generation
Definition: NBNode.h:213
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:263
int myCrossingsLoadedFromSumoNet
number of crossings loaded from a sumo net
Definition: NBNode.h:926
bool forbids(const NBEdge *const possProhibitorFrom, const NBEdge *const possProhibitorTo, const NBEdge *const possProhibitedFrom, const NBEdge *const possProhibitedTo, bool regardNonSignalisedLowerPriority) const
Returns the information whether "prohibited" flow must let "prohibitor" flow pass.
Definition: NBNode.cpp:2069
bool alreadyConnectedPaths(const NBEdge *e1, const NBEdge *e2, double dist) const
return true if the given pedestrian paths are connected at another junction within dist
Definition: NBNode.cpp:3413
bool mustBrakeForCrossing(const NBEdge *const from, const NBEdge *const to, const Crossing &crossing) const
Returns the information whether the described flow must brake for the given crossing.
Definition: NBNode.cpp:1914
bool hasConflict() const
whether there are conflicting streams of traffic at this node
Definition: NBNode.cpp:1058
void removeTrafficLights(bool setAsPriority=false)
Removes all references to traffic lights that control this tls.
Definition: NBNode.cpp:382
void replaceInConnectionProhibitions(NBEdge *which, NBEdge *by, int whichLaneOff, int byLaneOff)
replace incoming connections prohibitions
Definition: NBNode.cpp:1673
bool mergeConflictYields(const NBEdge *from, int fromLane, int fromLaneFoe, NBEdge *to, int toLane) const
whether one of multple connections from the same edge targeting the same lane must yield
Definition: NBNode.cpp:1984
void replaceOutgoing(NBEdge *which, NBEdge *by, int laneOff)
Replaces occurences of the first edge within the list of outgoing by the second Connections are remap...
Definition: NBNode.cpp:1604
void getReduction(const NBEdge *in, const NBEdge *out, int &inOffset, int &outOffset, int &reduction) const
get the reduction in driving lanes at this junction
Definition: NBNode.cpp:1510
EdgeVector myAllEdges
Vector of incoming and outgoing edges.
Definition: NBNode.h:875
void computeKeepClear()
compute keepClear status for all connections
Definition: NBNode.cpp:1000
void sortEdges(bool useNodeShape)
sort all edge containers for this node
Definition: NBNode.cpp:3708
RightOfWay myRightOfWay
how to compute right of way for this node
Definition: NBNode.h:914
bool myIsBentPriority
Definition: NBNode.h:934
std::set< NBTrafficLightDefinition * > myTrafficLights
traffic lights of node
Definition: NBNode.h:905
double myRadius
the turning radius (for all corners) at this node in m.
Definition: NBNode.h:908
static bool includes(const std::set< NBEdge *, ComparatorIdLess > &super, const std::set< const NBEdge *, ComparatorIdLess > &sub)
returns whether sub is a subset of super
Definition: NBNode.cpp:3379
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:514
bool isLeftMover(const NBEdge *const from, const NBEdge *const to) const
Computes whether the given connection is a left mover across the junction.
Definition: NBNode.cpp:2050
int removeSelfLoops(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tc)
Removes edges which are both incoming and outgoing into this node.
Definition: NBNode.cpp:426
bool checkCrossingDuplicated(EdgeVector edges)
return true if there already exist a crossing with the same edges as the input
Definition: NBNode.cpp:2701
void setRoundabout()
update the type of this node as a roundabout
Definition: NBNode.cpp:3507
bool mergeConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether multple connections from the same edge target the same lane
Definition: NBNode.cpp:1995
bool myDiscardAllCrossings
whether to discard all pedestrian crossings
Definition: NBNode.h:923
void invalidateTLS(NBTrafficLightLogicCont &tlCont, bool removedConnections, bool addedConnections)
causes the traffic light to be computed anew
Definition: NBNode.cpp:395
bool brakeForCrossingOnExit(const NBEdge *to) const
whether a connection to the given edge must brake for a crossing when leaving the intersection
Definition: NBNode.cpp:1919
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition: NBNode.cpp:2777
void addSortedLinkFoes(const NBConnection &mayDrive, const NBConnection &mustStop)
add shorted link FOES
Definition: NBNode.cpp:1776
Position myPosition
The position the node lies at.
Definition: NBNode.h:866
void replaceIncoming(NBEdge *which, NBEdge *by, int laneOff)
Replaces occurences of the first edge within the list of incoming by the second Connections are remap...
Definition: NBNode.cpp:1640
bool turnFoes(const NBEdge *from, const NBEdge *to, int fromLane, const NBEdge *from2, const NBEdge *to2, int fromLane2, bool lefthand=false) const
return whether the given laneToLane connection originate from the same edge and are in conflict due t...
Definition: NBNode.cpp:2004
void discardAllCrossings(bool rejectAll)
discard all current (and optionally future) crossings
Definition: NBNode.cpp:2795
bool hasOutgoing(const NBEdge *const e) const
Returns whether the given edge starts at this node.
Definition: NBNode.cpp:1752
bool writeLogic(OutputDevice &into) const
writes the XML-representation of the logic as a bitset-logic XML representation
Definition: NBNode.cpp:1029
NBEdge * getPossiblySplittedOutgoing(const std::string &edgeid)
get possibly splitted outgoing edge
Definition: NBNode.cpp:1806
void addOutgoingEdge(NBEdge *edge)
adds an outgoing edge
Definition: NBNode.cpp:469
bool isConstantWidthTransition() const
detects whether a given junction splits or merges lanes while keeping constant road width
Definition: NBNode.cpp:818
std::vector< std::unique_ptr< Crossing > > myCrossings
Vector of crossings.
Definition: NBNode.h:878
bool isStraighter(const NBEdge *const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge *const candidate) const
check whether the candidate edge is more likely to be the straight continuation
Definition: NBNode.cpp:2192
void removeJoinedTrafficLights()
remove all traffic light definitions that are part of a joined tls
Definition: NBNode.cpp:939
bool crossingBetween(const NBEdge *e1, const NBEdge *e2) const
return true if the given edges are connected by a crossing
Definition: NBNode.cpp:3392
bool isDistrict() const
check if node is a district
Definition: NBNode.cpp:2493
NBDistrict * myDistrict
The district the node is the centre of.
Definition: NBNode.h:893
void computeLanes2Lanes()
computes the connections of lanes to edges
Definition: NBNode.cpp:1116
void reshiftPosition(double xoff, double yoff)
Applies an offset to the node.
Definition: NBNode.cpp:333
double myDisplacementError
geometry error after computation of internal lane shapes
Definition: NBNode.h:929
static const int AVOID_WIDE_LEFT_TURN
Definition: NBNode.h:214
void removeTrafficLight(NBTrafficLightDefinition *tlDef)
Removes the given traffic light from this node.
Definition: NBNode.cpp:375
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:268
const std::string getResponse(int linkIndex) const
get the 'response' string (right-of-way bit set) of the right-of-way logic
Definition: NBNode.cpp:1049
static bool isLongEnough(NBEdge *out, double minLength)
check if is long enough
Definition: NBNode.cpp:1565
bool tlsContConflict(const NBEdge *from, const NBEdge::Connection &c, const NBEdge *foeFrom, const NBEdge::Connection &foe) const
whether the connection must yield if the foe remains on the intersection after its phase ends
Definition: NBNode.cpp:929
const PositionVector & getShape() const
retrieve the junction shape
Definition: NBNode.cpp:2447
std::vector< WalkingArea > myWalkingAreas
Vector of walking areas.
Definition: NBNode.h:881
NBConnectionProhibits myBlockedConnections
The container for connection block dependencies.
Definition: NBNode.h:890
void updateSurroundingGeometry()
update geometry of node and surrounding edges
Definition: NBNode.cpp:1067
int addedLanesRight(NBEdge *out, int addedLanes) const
check whether this edge has extra lanes on the right side
Definition: NBNode.cpp:1518
FringeType myFringeType
fringe type of this node
Definition: NBNode.h:917
bool checkIsRemovable() const
check if node is removable
Definition: NBNode.cpp:2328
bool isRoundabout() const
return whether this node is part of a roundabout
Definition: NBNode.cpp:3514
static const int FORWARD
edge directions (for pedestrian related stuff)
Definition: NBNode.h:206
bool checkIsRemovableReporting(std::string &reason) const
check if node is removable and return reason if not
Definition: NBNode.cpp:2334
void displaceShapeAtWidthChange(const NBEdge *from, const NBEdge::Connection &con, PositionVector &fromShape, PositionVector &toShape) const
displace lane shapes to account for change in lane width at this node
Definition: NBNode.cpp:826
bool foes(const NBEdge *const from1, const NBEdge *const to1, const NBEdge *const from2, const NBEdge *const to2) const
Returns the information whether the given flows cross.
Definition: NBNode.cpp:2079
void removeDoubleEdges()
remove duble edges
Definition: NBNode.cpp:1708
double buildInnerEdges()
build internal lanes, pedestrian crossings and walking areas
Definition: NBNode.cpp:2811
PositionVector myPoly
the (outer) shape of the junction
Definition: NBNode.h:896
NBEdge * getConnectionTo(NBNode *n) const
get connection to certain node
Definition: NBNode.cpp:2465
bool crossesFringe(const NBEdge *e1, const NBEdge *e2) const
return true if the given sidewalks are separated by a fringe road
Definition: NBNode.cpp:3434
void getEdgesThatApproach(NBEdge *currentOutgoing, EdgeVector &approaching)
returns a list of edges which are connected to the given outgoing edge
Definition: NBNode.cpp:1582
EdgeVector getEdgesSortedByAngleAtNodeCenter() const
returns the list of all edges sorted clockwise by getAngleAtNodeToCenter
Definition: NBNode.cpp:3651
EdgeVector edgesBetween(const NBEdge *e1, const NBEdge *e2) const
return all edges that lie clockwise between the given edges
Definition: NBNode.cpp:3442
PositionVector computeInternalLaneShape(const NBEdge *fromE, const NBEdge::Connection &con, int numPoints, NBNode *recordError=0, int shapeFlag=0) const
Compute the shape for an internal lane.
Definition: NBNode.cpp:733
~NBNode()
Destructor.
Definition: NBNode.cpp:301
NBEdge * getPossiblySplittedIncoming(const std::string &edgeid)
get possibly splitted incoming edge
Definition: NBNode.cpp:1793
void shiftTLConnectionLaneIndex(NBEdge *edge, int offset, int threshold=-1)
patches loaded signal plans by modifying lane indices above threshold by the given offset
Definition: NBNode.cpp:418
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3468
bool isNearDistrict() const
@chech if node is near district
Definition: NBNode.cpp:2476
static const int INDIRECT_LEFT
Definition: NBNode.h:218
EdgeVector myIncomingEdges
Vector of incoming edges.
Definition: NBNode.h:869
int checkCrossing(EdgeVector candidates)
Definition: NBNode.cpp:2610
WalkingArea & getWalkingArea(const std::string &id)
return the walkingArea with the given ID
Definition: NBNode.cpp:3577
void addTrafficLight(NBTrafficLightDefinition *tlDef)
Adds a traffic light to the list of traffic lights that control this node.
Definition: NBNode.cpp:365
int guessCrossings()
guess pedestrian crossings and return how many were guessed
Definition: NBNode.cpp:2499
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition: NBNode.h:321
static const int SCURVE_IGNORE
Definition: NBNode.h:217
const std::string getFoes(int linkIndex) const
get the 'foes' string (conflict bit set) of the right-of-way logic
Definition: NBNode.cpp:1039
NBEdge * getOppositeIncoming(NBEdge *e) const
returns the opposite incoming edge of certain edge
Definition: NBNode.cpp:1758
static PositionVector bezierControlPoints(const PositionVector &begShape, const PositionVector &endShape, bool isTurnaround, double extrapolateBeg, double extrapolateEnd, bool &ok, NBNode *recordError=0, double straightThresh=DEG2RAD(5), int shapeFlag=0)
get bezier control points
Definition: NBNode.cpp:544
This class computes shapes of junctions.
double getRadius() const
get computed radius for node
const PositionVector compute()
Computes the shape of the assigned junction.
static bool isRailwayNode(const NBNode *n)
whether the given node only has rail edges
Sorts crossings by minimum clockwise clockwise edge angle. Use the ordering found in myAllEdges of th...
Definition: NBAlgorithms.h:110
Sorts incoming and outgoing edges clockwise around the given node.
Definition: NBAlgorithms.h:152
static void swapWhenReversed(const NBNode *const n, const std::vector< NBEdge * >::iterator &i1, const std::vector< NBEdge * >::iterator &i2)
Assures correct order for same-angle opposite-direction edges.
A traffic light logics which must be computed (only nodes/edges are given)
Definition: NBOwnTLDef.h:44
bool forbids(const NBEdge *const possProhibitorFrom, const NBEdge *const possProhibitorTo, const NBEdge *const possProhibitedFrom, const NBEdge *const possProhibitedTo, bool regardNonSignalisedLowerPriority) const
Returns the information whether "prohibited" flow must let "prohibitor" flow pass.
Definition: NBRequest.cpp:539
bool hasConflictAtLink(int linkIndex) const
whether there are conflicting streams of traffic for the given link index
Definition: NBRequest.cpp:1116
const std::string & getFoes(int linkIndex) const
Definition: NBRequest.cpp:383
bool hasConflict() const
whether there are conflicting streams of traffic at this node
Definition: NBRequest.cpp:1106
void buildBitfieldLogic()
builds the bitset-representation of the logic
Definition: NBRequest.cpp:145
bool indirectLeftTurnConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether straight and indirect left turn are in conflict
Definition: NBRequest.cpp:854
static bool mustBrakeForCrossing(const NBNode *node, const NBEdge *const from, const NBEdge *const to, const NBNode::Crossing &crossing)
Returns the information whether the described flow must brake for the given crossing.
Definition: NBRequest.cpp:1040
bool mergeConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether multple connections from the same edge target the same lane
Definition: NBRequest.cpp:774
void writeLogic(OutputDevice &into) const
Definition: NBRequest.cpp:399
void computeLogic(const bool checkLaneFoes)
writes the XML-representation of the logic as a bitset-logic XML representation
Definition: NBRequest.cpp:421
std::pair< int, int > getSizes() const
returns the number of the junction's lanes and the number of the junction's links in respect.
Definition: NBRequest.cpp:502
bool mustBrake(const NBEdge *const possProhibitorFrom, const NBEdge *const possProhibitorTo, const NBEdge *const possProhibitedFrom, const NBEdge *const possProhibitedTo) const
Returns the information whether "prohibited" flow must let "prohibitor" flow pass.
Definition: NBRequest.cpp:1056
const std::string & getResponse(int linkIndex) const
Definition: NBRequest.cpp:391
bool foes(const NBEdge *const from1, const NBEdge *const to1, const NBEdge *const from2, const NBEdge *const to2) const
Returns the information whether the given flows cross.
Definition: NBRequest.cpp:520
The base class for traffic light logic definitions.
const std::vector< NBNode * > & getNodes() const
Returns the list of controlled nodes.
TrafficLightType getType() const
get the algorithm type (static etc..)
virtual void removeNode(NBNode *node)
Removes the given node from the list of controlled nodes.
virtual void addNode(NBNode *node)
Adds a node to the traffic light logic.
SUMOTime getOffset()
Returns the offset.
A container for traffic light definitions and built programs.
void remapRemoved(NBEdge *removed, const EdgeVector &incoming, const EdgeVector &outgoing)
Replaces occurences of the removed edge in incoming/outgoing edges of all definitions.
bool removeFully(const std::string id)
Removes a logic definition (and all programs) from the dictionary.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
static void computeTurnDirectionsForNode(NBNode *node, bool warn)
Computes turnaround destinations for all incoming edges of the given nodes (if any)
Base class for objects which have an id.
Definition: Named.h:54
std::string myID
The name of the object.
Definition: Named.h:125
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:59
Static storage of an output device and its base (abstract) implementation.
Definition: OutputDevice.h:61
An upper class for objects with additional parameters.
Definition: Parameterised.h:41
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
void norm2d()
Definition: Position.h:165
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:298
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:252
void sub(double dx, double dy)
Substracts the given position from this one.
Definition: Position.h:145
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 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 angleTo2D(const Position &other) const
returns the angle in the plane of the vector pointing from here to the other position
Definition: Position.h:262
bool almostSame(const Position &p2, double maxDiv=POSITION_EPS) const
check if two position is almost the sme as other
Definition: Position.h:237
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 getPolygonCenter() const
Returns the arithmetic of all corner points.
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
bool isNAN() const
check if PositionVector is NAN
void add(double xoff, double yoff, double zoff)
void closePolygon()
ensures that the last position equals the first
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
PositionVector getOrthogonal(const Position &p, double extend, bool before, double length=1.0, double deg=90) const
return orthogonal through p (extending this vector if necessary)
void move2side(double amount, double maxExtension=100)
move position vector to side using certain ammount
PositionVector smoothedZFront(double dist=std::numeric_limits< double >::max()) const
returned vector that is smoothed at the front (within dist)
double angleAt2D(int pos) const
get angle in certain position of position vector
void extrapolate(const double val, const bool onlyFirst=false, const bool onlyLast=false)
extrapolate position vector
PositionVector bezier(int numPoints)
return a bezier interpolation
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
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
bool around(const Position &p, double offset=0) const
Returns the information whether the position vector describes a polygon lying around the given point.
class for maintaining associations between enums and xml-strings
static bool isValidNetID(const std::string &value)
whether the given string is a valid id for a network element
Some static methods for string processing.
Definition: StringUtils.h:41
auto get(const nlohmann::detail::iteration_proxy_value< IteratorType > &i) -> decltype(i.key())
Definition: json.hpp:4451
NLOHMANN_BASIC_JSON_TPL_DECLARATION void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL &j1, nlohmann::NLOHMANN_BASIC_JSON_TPL &j2) noexcept(//NOLINT(readability-inconsistent-declaration-parameter-name) is_nothrow_move_constructible< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value &&//NOLINT(misc-redundant-expression) is_nothrow_move_assignable< nlohmann::NLOHMANN_BASIC_JSON_TPL >::value)
exchanges the values of two JSON objects
Definition: json.hpp:21884
#define M_PI
Definition: odrSpiral.cpp:45
A structure which describes a connection between edges or lanes.
Definition: NBEdge.h:201
bool indirectLeft
Whether this connection is an indirect left turn.
Definition: NBEdge.h:278
const std::string & getID() const
Definition: NBEdge.h:328
int fromLane
The lane the connections starts at.
Definition: NBEdge.h:227
int toLane
The lane the connections yields in.
Definition: NBEdge.h:233
NBEdge * toEdge
The edge the connections yields in.
Definition: NBEdge.h:230
PositionVector customShape
custom shape for connection
Definition: NBEdge.h:266
std::string getDescription(const NBEdge *parent) const
get string describing this connection
Definition: NBEdge.cpp:95
std::string tlID
The id of the traffic light that controls this connection.
Definition: NBEdge.h:236
bool haveVia
check if Connection have a Via
Definition: NBEdge.h:293
int tlLinkIndex
The index of this connection within the controlling traffic light.
Definition: NBEdge.h:239
An (internal) definition of a single lane of an edge.
Definition: NBEdge.h:143
double width
This lane's width.
Definition: NBEdge.h:176
double endOffset
This lane's offset to the intersection begin.
Definition: NBEdge.h:169
SVCPermissions changeRight
List of vehicle types that are allowed to change right from this lane.
Definition: NBEdge.h:166
SVCPermissions changeLeft
List of vehicle types that are allowed to change Left from this lane.
Definition: NBEdge.h:163
SVCPermissions permissions
List of vehicle types that are allowed on this lane.
Definition: NBEdge.h:157
PositionVector shape
The lane's shape.
Definition: NBEdge.h:148
std::set< const NBEdge *, ComparatorIdLess > edges
Definition: NBNode.h:200
A definition of a pedestrian walking area.
Definition: NBNode.h:169
int minPrevCrossingEdges
minimum number of edges crossed by incoming crossings
Definition: NBNode.h:196
std::vector< std::string > nextSidewalks
the lane-id of the next sidewalk lane or ""
Definition: NBNode.h:188
std::vector< std::string > prevSidewalks
the lane-id of the previous sidewalk lane or ""
Definition: NBNode.h:190
std::string id
the (edge)-id of this walkingArea
Definition: NBNode.h:176
bool hasCustomShape
whether this walkingArea has a custom shape
Definition: NBNode.h:192
double width
This lane's width.
Definition: NBNode.h:178
std::vector< std::string > nextCrossings
the lane-id of the next crossing(s)
Definition: NBNode.h:184
std::vector< std::string > prevCrossings
the lane-id of the previous crossing(s)
Definition: NBNode.h:186
PositionVector shape
The polygonal shape.
Definition: NBNode.h:182
double length
This lane's width.
Definition: NBNode.h:180
int minNextCrossingEdges
minimum number of edges crossed by nextCrossings
Definition: NBNode.h:194