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