Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NBNode.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2023 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
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(OptionsCont::getOptions().getBool("junctions.minimal-shape"));
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
2072 const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2073 if (myRequest == nullptr) {
2074 return false;
2075 }
2076 return myRequest->bidiConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2077}
2078
2079bool
2080NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
2081 const NBEdge* from2, const NBEdge* to2, int fromLane2,
2082 bool lefthand) const {
2083 UNUSED_PARAMETER(lefthand);
2084 if (from != from2 || to == to2 || fromLane == fromLane2) {
2085 return false;
2086 }
2087 if (from->isTurningDirectionAt(to)
2088 || from2->isTurningDirectionAt(to2)) {
2089 // XXX should warn if there are any non-turning connections left of this
2090 return false;
2091 }
2092 bool result = false;
2093 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2094 if (fromLane < fromLane2) {
2095 // conflict if 'to' comes before 'to2' going clockwise starting at 'from'
2096 while (*it != to2) {
2097 if (*it == to) {
2098 result = true;
2099 }
2101 }
2102 } else {
2103 // conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
2104 while (*it != to2) {
2105 if (*it == to) {
2106 result = true;
2107 }
2109 }
2110 }
2111 /*
2112 if (result) {
2113 std::cout << "turnFoes node=" << getID()
2114 << " from=" << from->getLaneID(fromLane)
2115 << " to=" << to->getID()
2116 << " from2=" << from2->getLaneID(fromLane2)
2117 << " to2=" << to2->getID()
2118 << "\n";
2119 }
2120 */
2121 return result;
2122}
2123
2124
2125bool
2126NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
2127 // when the junction has only one incoming edge, there are no
2128 // problems caused by left blockings
2129 if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
2130 return false;
2131 }
2132 double fromAngle = from->getAngleAtNode(this);
2133 double toAngle = to->getAngleAtNode(this);
2134 double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
2135 double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
2136 std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2137 do {
2139 } while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
2140 return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
2141}
2142
2143
2144bool
2145NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
2146 const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
2147 bool regardNonSignalisedLowerPriority) const {
2148 return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
2149 possProhibitedFrom, possProhibitedTo,
2150 regardNonSignalisedLowerPriority);
2151}
2152
2153
2154bool
2155NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
2156 const NBEdge* const from2, const NBEdge* const to2) const {
2157 return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
2158}
2159
2160
2161void
2163 NBEdge* removed, const EdgeVector& incoming,
2164 const EdgeVector& outgoing) {
2165 assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
2166 bool changed = true;
2167 while (changed) {
2168 changed = false;
2169 NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
2170 NBConnectionProhibits blockedConnectionsNew;
2171 // remap in connections
2172 for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
2173 const NBConnection& blocker = (*i).first;
2174 const NBConnectionVector& blocked = (*i).second;
2175 // check the blocked connections first
2176 // check whether any of the blocked must be changed
2177 bool blockedChanged = false;
2178 NBConnectionVector newBlocked;
2179 NBConnectionVector::const_iterator j;
2180 for (j = blocked.begin(); j != blocked.end(); j++) {
2181 const NBConnection& sblocked = *j;
2182 if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
2183 blockedChanged = true;
2184 }
2185 }
2186 // adapt changes if so
2187 for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
2188 const NBConnection& sblocked = *j;
2189 if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
2190 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2191 !!! newBlocked.push_back(NBConnection(*k, *k));
2192 }*/
2193 } else if (sblocked.getFrom() == removed) {
2194 assert(sblocked.getTo() != removed);
2195 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2196 newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
2197 }
2198 } else if (sblocked.getTo() == removed) {
2199 assert(sblocked.getFrom() != removed);
2200 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2201 newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
2202 }
2203 } else {
2204 newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
2205 }
2206 }
2207 if (blockedChanged) {
2208 blockedConnectionsNew[blocker] = newBlocked;
2209 changed = true;
2210 }
2211 // if the blocked were kept
2212 else {
2213 if (blocker.getFrom() == removed && blocker.getTo() == removed) {
2214 changed = true;
2215 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2216 !!! blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
2217 }*/
2218 } else if (blocker.getFrom() == removed) {
2219 assert(blocker.getTo() != removed);
2220 changed = true;
2221 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2222 blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
2223 }
2224 } else if (blocker.getTo() == removed) {
2225 assert(blocker.getFrom() != removed);
2226 changed = true;
2227 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2228 blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
2229 }
2230 } else {
2231 blockedConnectionsNew[blocker] = blocked;
2232 }
2233 }
2234 }
2235 myBlockedConnections = blockedConnectionsNew;
2236 }
2237 // remap in traffic lights
2238 tc.remapRemoved(removed, incoming, outgoing);
2239}
2240
2241
2242NBEdge*
2243NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
2244 EdgeVector::const_iterator i = itOut;
2245 while (*i != incoming) {
2246 if (clockwise) {
2248 } else {
2250 }
2251 if ((*i)->getFromNode() != this) {
2252 // only look for outgoing edges
2253 // @note we use myAllEdges to stop at the incoming edge
2254 continue;
2255 }
2256 if (incoming->isTurningDirectionAt(*i)) {
2257 return nullptr;
2258 }
2259 if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
2260 return *i;
2261 }
2262 }
2263 return nullptr;
2264}
2265
2266
2267bool
2268NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
2269 if (candidate != nullptr) {
2270 const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
2271 // they are too similar it does not matter
2272 if (fabs(angle - candAngle) < 5.) {
2273 return false;
2274 }
2275 // the other edge is at least 5 degree straighter
2276 if (fabs(candAngle) < fabs(angle) - 5.) {
2277 return true;
2278 }
2279 if (fabs(angle) < fabs(candAngle) - 5.) {
2280 return false;
2281 }
2282 if (fabs(candAngle) < 44.) {
2283 // the lane count for the same modes is larger
2284 const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
2285 if (candModeLanes > modeLanes) {
2286 return true;
2287 }
2288 if (candModeLanes < modeLanes) {
2289 return false;
2290 }
2291 // we would create a left turn
2292 if (candAngle < 0 && angle > 0) {
2293 return true;
2294 }
2295 if (angle < 0 && candAngle > 0) {
2296 return false;
2297 }
2298 }
2299 }
2300 return false;
2301}
2302
2304NBNode::getPassengerEdges(bool incoming) const {
2305 EdgeVector result;
2306 for (NBEdge* e : (incoming ? myIncomingEdges : myOutgoingEdges)) {
2307 if ((e->getPermissions() & SVC_PASSENGER) != 0) {
2308 result.push_back(e);
2309 }
2310 }
2311 return result;
2312}
2313
2315NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
2316 // ok, no connection at all -> dead end
2317 if (outgoing == nullptr) {
2318 return LinkDirection::NODIR;
2319 }
2322 }
2323 // turning direction
2324 if (incoming->isTurningDirectionAt(outgoing)) {
2325 if (isExplicitRailNoBidi(incoming, outgoing)) {
2327 }
2329 }
2330 // get the angle between incoming/outgoing at the junction
2331 const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
2332 // ok, should be a straight connection
2333 EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
2334 SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
2335 if (vehPerm != SVC_PEDESTRIAN) {
2336 vehPerm &= ~SVC_PEDESTRIAN;
2337 }
2338 const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
2339 if (fabs(angle) < 44.) {
2340 if (fabs(angle) > 6.) {
2341 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
2343 }
2344 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
2346 }
2347 }
2348 if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2349 return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
2350 }
2352 }
2353
2354 if (angle > 0) {
2355 // check whether any other edge goes further to the right
2356 if (angle > 90) {
2357 return LinkDirection::RIGHT;
2358 }
2359 NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
2360 if (outCW != nullptr) {
2362 } else {
2363 return LinkDirection::RIGHT;
2364 }
2365 } else {
2366 // check whether any other edge goes further to the left
2367 if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
2368 if (isExplicitRailNoBidi(incoming, outgoing)) {
2370 }
2372 } else if (angle < -90) {
2373 return LinkDirection::LEFT;
2374 }
2375 NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
2376 if (outCCW != nullptr) {
2378 } else {
2379 return LinkDirection::LEFT;
2380 }
2381 }
2382}
2383
2384
2385bool
2386NBNode::isExplicitRailNoBidi(const NBEdge* incoming, const NBEdge* outgoing) {
2387 // assume explicit connections at sharp turn-arounds are either for reversal or due to a geometry glitch
2388 // (but should not have been guessed)
2389 // @note this function is also called from NBAlgorithms when there aren't any connections ready
2391 && isRailway(incoming->getPermissions())
2392 && isRailway(outgoing->getPermissions())
2393 && incoming->getBidiEdge() != outgoing);
2394}
2395
2396
2398NBNode::getLinkState(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane,
2399 bool mayDefinitelyPass, const std::string& tlID) const {
2401 return LINKSTATE_MAJOR; // the trains must run on time
2402 }
2403 if (tlID != "") {
2405 return LINKSTATE_ALLWAY_STOP;
2406 }
2407 return mustBrake(incoming, outgoing, fromLane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
2408 }
2409 if (outgoing == nullptr) { // always off
2411 }
2413 && mustBrake(incoming, outgoing, fromLane, toLane, true)) {
2414 return LINKSTATE_EQUAL; // all the same
2415 }
2417 return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
2418 }
2419 if (myType == SumoXMLNodeType::ZIPPER && zipperConflict(incoming, outgoing, fromLane, toLane)) {
2420 return LINKSTATE_ZIPPER;
2421 }
2422 if (!mayDefinitelyPass
2423 && mustBrake(incoming, outgoing, fromLane, toLane, true)
2424 // legacy mode
2425 && (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
2426 // avoid linkstate minor at pure railway nodes
2429 }
2430 // traffic lights are not regarded here
2431 return LINKSTATE_MAJOR;
2432}
2433
2434
2435bool
2436NBNode::zipperConflict(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane) const {
2437 if (mustBrake(incoming, outgoing, fromLane, toLane, false)) {
2438 // there should be another connection with the same target (not just some intersecting trajectories)
2439 for (const NBEdge* in : getIncomingEdges()) {
2440 for (const NBEdge::Connection& c : in->getConnections()) {
2441 if ((in != incoming || c.fromLane != fromLane) && c.toEdge == outgoing && c.toLane == toLane) {
2442 return true;
2443 }
2444 }
2445 }
2446 }
2447 return false;
2448}
2449
2450
2451bool
2453 std::string reason;
2454 return checkIsRemovableReporting(reason);
2455}
2456
2457bool
2458NBNode::checkIsRemovableReporting(std::string& reason) const {
2459 if (getEdges().empty()) {
2460 return true;
2461 }
2462 // check whether this node is included in a traffic light or crossing
2463 if (myTrafficLights.size() != 0) {
2464 reason = "TLS";
2465 return false;
2466 }
2468 reason = "rail_signal";
2469 return false;
2470 }
2471 if (myCrossings.size() != 0) {
2472 reason = "crossing";
2473 return false;
2474 }
2475 EdgeVector::const_iterator i;
2476 // one in, one out -> just a geometry ...
2477 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2478 // ... if types match ...
2479 if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2480 reason = "edges incompatible: " + reason;
2481 return false;
2482 }
2483 if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
2484 reason = "turnaround";
2485 return false;
2486 }
2487 return true;
2488 }
2489 // two in, two out -> may be something else
2490 if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
2491 // check whether the origin nodes of the incoming edges differ
2492 std::set<NBNode*> origSet;
2493 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2494 origSet.insert((*i)->getFromNode());
2495 }
2496 if (origSet.size() < 2) {
2497 // overlapping case
2498 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2499 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2500 return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
2501 myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
2502 || (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
2503 myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
2504 }
2505 }
2506 // check whether this node is an intermediate node of
2507 // a two-directional street
2508 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2509 // each of the edges must have an opposite direction edge
2510 NBEdge* opposite = (*i)->getTurnDestination(true);
2511 if (opposite != nullptr) {
2512 // the other outgoing edges must be the continuation of the current
2513 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2514 // check whether the types allow joining
2515 if (!(*i)->expandableBy(continuation, reason)) {
2516 reason = "edges incompatible: " + reason;
2517 return false;
2518 }
2519 } else {
2520 // ok, at least one outgoing edge is not an opposite
2521 // of an incoming one
2522 reason = "not opposites";
2523 return false;
2524 }
2525 }
2526 return true;
2527 }
2528 // ok, a real node
2529 reason = "intersection";
2530 return false;
2531}
2532
2533
2534std::vector<std::pair<NBEdge*, NBEdge*> >
2536 assert(checkIsRemovable());
2537 std::vector<std::pair<NBEdge*, NBEdge*> > ret;
2538 // one in, one out-case
2539 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2540 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2541 return ret;
2542 }
2543 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
2544 // two in, two out-case
2545 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2546 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2547 // overlapping edges
2548 std::string reason;
2549 if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2550 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2551 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
2552 } else {
2553 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
2554 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
2555 }
2556 return ret;
2557 }
2558 }
2559 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2560 // join with the edge that is not a turning direction
2561 NBEdge* opposite = (*i)->getTurnDestination(true);
2562 assert(opposite != 0);
2563 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2564 ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
2565 }
2566 return ret;
2567}
2568
2569
2570const PositionVector&
2572 return myPoly;
2573}
2574
2575
2576void
2578 myPoly = shape;
2579 myHaveCustomPoly = (myPoly.size() > 1);
2580 if (myHaveCustomPoly) {
2581 for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
2582 (*i)->resetNodeBorder(this);
2583 }
2584 }
2585}
2586
2587
2588NBEdge*
2590 for (NBEdge* e : myOutgoingEdges) {
2591 if (e->getToNode() == n && e->getPermissions() != 0) {
2592 return e;
2593 }
2594 }
2595 return nullptr;
2596}
2597
2598
2599bool
2601 if (isDistrict()) {
2602 return false;
2603 }
2604 for (const NBEdge* const t : getEdges()) {
2605 const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
2606 for (const NBEdge* const k : other->getEdges()) {
2607 if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
2608 return true;
2609 }
2610 }
2611 }
2612 return false;
2613}
2614
2615
2616bool
2620
2621
2622int
2624#ifdef DEBUG_PED_STRUCTURES
2626#endif
2627 int numGuessed = 0;
2628 if (myCrossings.size() > 0 || myDiscardAllCrossings) {
2629 // user supplied crossings, do not guess
2630 return numGuessed;
2631 }
2632 if (gDebugFlag1) {
2633 std::cout << "guess crossings for " << getID() << "\n";
2634 }
2636 // check for pedestrial lanes going clockwise around the node
2637 std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
2638 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2639 NBEdge* edge = *it;
2640 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2641 if (edge->getFromNode() == this) {
2642 for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2643 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2644 }
2645 } else {
2646 for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2647 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2648 }
2649 }
2650 }
2651 // do we even have a pedestrian lane?
2652 int firstSidewalk = -1;
2653 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2654 if (normalizedLanes[i].second) {
2655 firstSidewalk = i;
2656 break;
2657 }
2658 }
2659 int hadCandidates = 0;
2660 std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
2661 if (firstSidewalk != -1) {
2662 // rotate lanes to ensure that the first one allows pedestrians
2663 std::vector<std::pair<NBEdge*, bool> > tmp;
2664 copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
2665 copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
2666 normalizedLanes = tmp;
2667 // find candidates
2668 EdgeVector candidates;
2669 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2670 NBEdge* edge = normalizedLanes[i].first;
2671 const bool allowsPed = normalizedLanes[i].second;
2672 if (gDebugFlag1) {
2673 std::cout << " cands=" << toString(candidates) << " edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n";
2674 }
2675 if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
2676 candidates.push_back(edge);
2677 } else if (allowsPed) {
2678 if (candidates.size() > 0) {
2679 if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
2680 hadCandidates++;
2681 const int n = checkCrossing(candidates);
2682 numGuessed += n;
2683 if (n > 0) {
2684 connectedCandidates.push_back(n);
2685 }
2686 }
2687 candidates.clear();
2688 }
2689 }
2690 }
2691 if (hadCandidates > 0 && candidates.size() > 0) {
2692 // avoid wrapping around to the same sidewalk
2693 hadCandidates++;
2694 const int n = checkCrossing(candidates);
2695 numGuessed += n;
2696 if (n > 0) {
2697 connectedCandidates.push_back(n);
2698 }
2699 }
2700 }
2701 // Avoid duplicate crossing between the same pair of walkingareas
2702 if (gDebugFlag1) {
2703 std::cout << " hadCandidates=" << hadCandidates << " connectedCandidates=" << toString(connectedCandidates) << "\n";
2704 }
2705 if (hadCandidates == 2 && connectedCandidates.size() == 2) {
2706 // One or both of them might be split: remove the one with less splits
2707 if (connectedCandidates.back() <= connectedCandidates.front()) {
2708 numGuessed -= connectedCandidates.back();
2709 myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
2710 } else {
2711 numGuessed -= connectedCandidates.front();
2712 myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
2713 }
2714 }
2716 if (gDebugFlag1) {
2717 std::cout << "guessedCrossings:\n";
2718 for (auto& crossing : myCrossings) {
2719 std::cout << " edges=" << toString(crossing->edges) << "\n";
2720 }
2721 }
2722 if (numGuessed > 0 && isSimpleContinuation(true, true)) {
2723 // avoid narrow node shape when there is a crossing
2724 computeNodeShape(-1);
2725 for (NBEdge* e : myAllEdges) {
2726 e->computeEdgeShape();
2727 }
2728 }
2729 return numGuessed;
2730}
2731
2732
2733int
2734NBNode::checkCrossing(EdgeVector candidates, bool checkOnly) {
2735 if (gDebugFlag1) {
2736 std::cout << "checkCrossing candidates=" << toString(candidates) << "\n";
2737 }
2738 if (candidates.size() == 0) {
2739 if (gDebugFlag1) {
2740 std::cout << "no crossing added (numCandidates=" << candidates.size() << ")\n";
2741 }
2742 return 0;
2743 } else {
2744 // check whether the edges may be part of a common crossing due to having similar angle
2745 double prevAngle = -100000; // dummy
2746 for (int i = 0; i < (int)candidates.size(); ++i) {
2747 NBEdge* edge = candidates[i];
2748 double angle = edge->getCrossingAngle(this);
2749 // edges should be sorted by angle but this only holds true approximately
2750 if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
2751 if (gDebugFlag1) {
2752 std::cout << "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n";
2753 }
2754 return 0;
2755 }
2756 if (!checkOnly && !isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
2757 if (gDebugFlag1) {
2758 std::cout << "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n";
2759 }
2760 return 0;
2761 }
2762 prevAngle = angle;
2763 }
2764 if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
2765 if (!checkOnly) {
2767 if (gDebugFlag1) {
2768 std::cout << "adding crossing: " << toString(candidates) << "\n";
2769 }
2770 }
2771 return 1;
2772 } else {
2773 // check for intermediate walking areas
2774 prevAngle = -100000; // dummy
2775 for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
2776 double angle = (*it)->getCrossingAngle(this);
2777 if (it != candidates.begin()) {
2778 NBEdge* prev = *(it - 1);
2779 NBEdge* curr = *it;
2780 Position prevPos, currPos;
2781 int laneI;
2782 // compute distance between candiate edges
2783 double intermediateWidth = 0;
2784 if (prev->getToNode() == this) {
2785 laneI = prev->getNumLanes() - 1;
2786 prevPos = prev->getLanes()[laneI].shape[-1];
2787 } else {
2788 laneI = 0;
2789 prevPos = prev->getLanes()[laneI].shape[0];
2790 }
2791 intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
2792 if (curr->getFromNode() == this) {
2793 laneI = curr->getNumLanes() - 1;
2794 currPos = curr->getLanes()[laneI].shape[0];
2795 } else {
2796 laneI = 0;
2797 currPos = curr->getLanes()[laneI].shape[-1];
2798 }
2799 intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
2800 intermediateWidth += currPos.distanceTo2D(prevPos);
2801 if (gDebugFlag1) {
2802 std::cout
2803 << " prevAngle=" << prevAngle
2804 << " angle=" << angle
2805 << " intermediateWidth=" << intermediateWidth
2806 << "\n";
2807 }
2808 if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
2809 || (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
2810 return checkCrossing(EdgeVector(candidates.begin(), it), checkOnly)
2811 + checkCrossing(EdgeVector(it, candidates.end()), checkOnly);
2812 }
2813 }
2814 prevAngle = angle;
2815 }
2816 if (!checkOnly) {
2818 if (gDebugFlag1) {
2819 std::cout << "adding crossing: " << toString(candidates) << "\n";
2820 }
2821 }
2822 return 1;
2823 }
2824 }
2825}
2826
2827
2828bool
2830 // sort edge vector
2831 std::sort(edges.begin(), edges.end());
2832 // iterate over crossing to find a crossing with the same edges
2833 for (auto& crossing : myCrossings) {
2834 // sort edges of crossing before compare
2835 EdgeVector edgesOfCrossing = crossing->edges;
2836 std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
2837 if (edgesOfCrossing == edges) {
2838 return true;
2839 }
2840 }
2841 return false;
2842}
2843
2844
2845bool
2846NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
2847 for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
2848 if (!normalizedLanes[i].second) {
2849 return true;
2850 }
2851 }
2852 return false;
2853}
2854
2855
2856void
2859 buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
2860 OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
2861 // ensure that all crossings are properly connected
2862 bool recheck = myCrossings.size() > 0;
2863 while (recheck) {
2864 recheck = false;
2865 std::set<std::string> waIDs;
2866 int numSidewalks = 0;
2867 for (WalkingArea& wa : myWalkingAreas) {
2868 waIDs.insert(wa.id);
2869 numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
2870 }
2871 if (numSidewalks < 2) {
2872 // all crossings are invalid if there are fewer than 2 sidewalks involved
2873 waIDs.clear();
2874 }
2875 for (auto& crossing : myCrossings) {
2876 if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
2877 if (crossing->valid) {
2878 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
2879 crossing->id, getID(), toString(crossing->edges));
2880 recheck = true;
2881 }
2882 for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
2883 WalkingArea& wa = *waIt;
2884 std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
2885 if (it_nc != wa.nextCrossings.end()) {
2886 wa.nextCrossings.erase(it_nc);
2887 }
2888 if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
2889 waIt = myWalkingAreas.erase(waIt);
2890 recheck = true;
2891 } else {
2892 waIt++;
2893 }
2894 }
2895 crossing->valid = false;
2896 crossing->prevWalkingArea = "";
2897 crossing->nextWalkingArea = "";
2898 }
2899 }
2900 }
2901}
2902
2903
2904std::vector<NBNode::Crossing*>
2906 std::vector<Crossing*> result;
2907 for (auto& c : myCrossings) {
2908 if (c->valid) {
2909 result.push_back(c.get());
2910 }
2911 }
2912 //if (myCrossings.size() > 0) {
2913 // std::cout << "valid crossings at " << getID() << "\n";
2914 // for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
2915 // std::cout << " " << toString((*it)->edges) << "\n";
2916 // }
2917 //}
2918 return result;
2919}
2920
2921
2922void
2924 myCrossings.clear();
2925 // also discard all further crossings
2926 if (rejectAll) {
2927 myDiscardAllCrossings = true;
2928 }
2929}
2930
2931
2932void
2936
2937
2938double
2940 // myDisplacementError is computed during this operation. reset first
2942 // build inner edges for vehicle movements across the junction
2943 int noInternalNoSplits = 0;
2944 for (const NBEdge* const edge : myIncomingEdges) {
2945 for (const NBEdge::Connection& con : edge->getConnections()) {
2946 if (con.toEdge == nullptr) {
2947 continue;
2948 }
2949 noInternalNoSplits++;
2950 }
2951 }
2952 int lno = 0;
2953 int splitNo = 0;
2954 double maxCrossingSeconds = 0.;
2955 for (NBEdge* const edge : myIncomingEdges) {
2956 maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
2957 }
2958 return maxCrossingSeconds;
2959}
2960
2961
2962int
2964#ifdef DEBUG_PED_STRUCTURES
2966#endif
2967 if (gDebugFlag1) {
2968 std::cout << "build crossings for " << getID() << ":\n";
2969 }
2971 myCrossings.clear();
2972 }
2973 int index = 0;
2974 const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
2975 for (auto& c : myCrossings) {
2976 c->valid = true;
2977 if (!isTLControlled()) {
2978 c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
2979 }
2980 c->id = ":" + getID() + "_c" + toString(index++);
2981 c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
2982 // reset fields, so repeated computation (Netedit) will sucessfully perform the checks
2983 // in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
2984 c->nextWalkingArea = "";
2985 c->prevWalkingArea = "";
2986 EdgeVector& edges = c->edges;
2987 if (gDebugFlag1) {
2988 std::cout << " crossing=" << c->id << " edges=" << toString(edges);
2989 }
2990 // sorting the edges in the right way is imperative. We want to sort
2991 // them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
2992 std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
2993 if (gDebugFlag1) {
2994 std::cout << " sortedEdges=" << toString(edges) << "\n";
2995 };
2996 // rotate the edges so that the largest relative angle difference comes at the end
2997 std::vector<double> rawAngleDiffs;
2998 double maxAngleDiff = 0;
2999 int maxAngleDiffIndex = 0; // index before maxDist
3000 for (int i = 0; i < (int) edges.size(); i++) {
3001 double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
3002 edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
3003 if (diff < 0) {
3004 diff += 360;
3005 }
3006 const double rawDiff = NBHelpers::relAngle(
3007 edges[i]->getAngleAtNodeNormalized(this),
3008 edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
3009 rawAngleDiffs.push_back(fabs(rawDiff));
3010
3011 if (gDebugFlag1) {
3012 std::cout << " i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n";
3013 }
3014 if (diff > maxAngleDiff) {
3015 maxAngleDiff = diff;
3016 maxAngleDiffIndex = i;
3017 }
3018 }
3019 if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
3020 // if the angle differences is too small, we better not rotate
3021 std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
3022 if (gDebugFlag1) {
3023 std::cout << " rotatedEdges=" << toString(edges);
3024 }
3025 }
3026 bool diagonalCrossing = false;
3027 std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
3028 if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
3029 diagonalCrossing = true;
3030 if (gDebugFlag1) {
3031 std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
3032 for (auto e : edges) {
3033 std::cout << " e=" << e->getID()
3034 << " aC=" << e->getAngleAtNodeToCenter(this)
3035 << " a=" << e->getAngleAtNode(this)
3036 << " aN=" << e->getAngleAtNodeNormalized(this)
3037 << "\n";
3038 }
3039 }
3040 }
3041 // reverse to get them in CCW order (walking direction around the node)
3042 std::reverse(edges.begin(), edges.end());
3043 // compute shape
3044 c->shape.clear();
3045 const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
3046 const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
3047 int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
3048 int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
3049 if (gDebugFlag1) {
3050 std::cout << " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n";
3051 }
3052 if (firstNonPedLane < 0 || lastNonPedLane < 0) {
3053 // invalid crossing
3054 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
3055 c->valid = false;
3056 // compute surrogate shape to make it visible in netedit
3057 firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
3058 lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
3059 }
3060 if (c->customShape.size() != 0) {
3061 c->shape = c->customShape;
3062 } else {
3063 NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
3064 NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
3065 crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
3066 crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
3067 crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
3068 crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
3069 crossingBeg.shape.extrapolate(c->width / 2);
3070 crossingEnd.shape.extrapolate(c->width / 2);
3071 // check if after all changes shape are NAN (in these case, discard)
3072 if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
3073 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
3074 c->valid = false;
3075 } else {
3076 c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
3077 c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
3078 }
3079 if (diagonalCrossing) {
3080 c->shape.move2side(-c->width);
3081 }
3082 }
3083 }
3084 return index;
3085}
3086
3087
3088void
3089NBNode::buildWalkingAreas(int cornerDetail, double joinMinDist) {
3090#ifdef DEBUG_PED_STRUCTURES
3092#endif
3093 int index = 0;
3094 myWalkingAreas.clear();
3095 if (gDebugFlag1) {
3096 std::cout << "build walkingAreas for " << getID() << ":\n";
3097 }
3098 if (myAllEdges.size() == 0) {
3099 return;
3100 }
3102 // shapes are all pointing away from the intersection
3103 std::vector<std::pair<NBEdge*, NBEdge::Lane> > normalizedLanes;
3104 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
3105 NBEdge* edge = *it;
3106 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
3107 if (edge->getFromNode() == this) {
3108 for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
3109 NBEdge::Lane l = *it_l;
3110 l.shape = l.shape.getSubpartByIndex(0, 2);
3112 normalizedLanes.push_back(std::make_pair(edge, l));
3113 }
3114 } else {
3115 for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
3116 NBEdge::Lane l = *it_l;
3117 l.shape = l.shape.reverse();
3118 l.shape = l.shape.getSubpartByIndex(0, 2);
3120 normalizedLanes.push_back(std::make_pair(edge, l));
3121 }
3122 }
3123 }
3124 //if (gDebugFlag1) std::cout << " normalizedLanes=" << normalizedLanes.size() << "\n";
3125 // collect [start,count[ indices in normalizedLanes that belong to a walkingArea
3126 std::vector<std::pair<int, int> > waIndices;
3127 int start = -1;
3128 NBEdge* prevEdge = normalizedLanes.back().first;
3129 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
3130 NBEdge* edge = normalizedLanes[i].first;
3131 NBEdge::Lane& l = normalizedLanes[i].second;
3132 if (start == -1) {
3133 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3134 start = i;
3135 }
3136 } else {
3137 if ((l.permissions & SVC_PEDESTRIAN) == 0
3138 || crossingBetween(edge, prevEdge)
3139 || alreadyConnectedPaths(edge, prevEdge, joinMinDist)
3140 || crossesFringe(edge, prevEdge)
3141 ) {
3142 waIndices.push_back(std::make_pair(start, i - start));
3143 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3144 start = i;
3145 } else {
3146 start = -1;
3147 }
3148
3149 }
3150 }
3151 if (gDebugFlag1) std::cout << " i=" << i << " edge=" << edge->getID() << " start=" << start << " ped=" << ((l.permissions & SVC_PEDESTRIAN) != 0)
3152 << " waI=" << waIndices.size() << " crossingBetween=" << crossingBetween(edge, prevEdge) << "\n";
3153 prevEdge = edge;
3154 }
3155 // deal with wrap-around issues
3156 if (start != - 1) {
3157 const int waNumLanes = (int)normalizedLanes.size() - start;
3158 if (waIndices.size() == 0) {
3159 waIndices.push_back(std::make_pair(start, waNumLanes));
3160 if (gDebugFlag1) {
3161 std::cout << " single wa, end at wrap-around\n";
3162 }
3163 } else {
3164 if (waIndices.front().first == 0) {
3165 NBEdge* edge = normalizedLanes.front().first;
3166 if (crossingBetween(edge, normalizedLanes.back().first)
3167 || crossesFringe(edge, normalizedLanes.back().first)) {
3168 // do not wrap-around (see above)
3169 waIndices.push_back(std::make_pair(start, waNumLanes));
3170 if (gDebugFlag1) {
3171 std::cout << " do not wrap around\n";
3172 }
3173 } else {
3174 // first walkingArea wraps around
3175 waIndices.front().first = start;
3176 waIndices.front().second = waNumLanes + waIndices.front().second;
3177 if (gDebugFlag1) {
3178 std::cout << " wrapping around\n";
3179 }
3180 }
3181 } else {
3182 // last walkingArea ends at the wrap-around
3183 waIndices.push_back(std::make_pair(start, waNumLanes));
3184 if (gDebugFlag1) {
3185 std::cout << " end at wrap-around\n";
3186 }
3187 }
3188 }
3189 }
3190 if (gDebugFlag1) {
3191 std::cout << " normalizedLanes=" << normalizedLanes.size() << " waIndices:\n";
3192 for (int i = 0; i < (int)waIndices.size(); ++i) {
3193 std::cout << " " << waIndices[i].first << ", " << waIndices[i].second << "\n";
3194 }
3195 }
3196 // build walking areas connected to a sidewalk
3197 for (int i = 0; i < (int)waIndices.size(); ++i) {
3198 const bool buildExtensions = waIndices[i].second != (int)normalizedLanes.size();
3199 const int startIdx = waIndices[i].first;
3200 const int prev = startIdx > 0 ? startIdx - 1 : (int)normalizedLanes.size() - 1;
3201 const int count = waIndices[i].second;
3202 const int end = (startIdx + count) % normalizedLanes.size();
3203 const int lastIdx = (startIdx + count - 1) % normalizedLanes.size();
3204
3205 WalkingArea wa(":" + getID() + "_w" + toString(index++), 1);
3206 if (gDebugFlag1) {
3207 std::cout << "build walkingArea " << wa.id << " start=" << startIdx << " end=" << end << " count=" << count << " prev=" << prev << ":\n";
3208 }
3209 double endCrossingWidth = 0;
3210 double startCrossingWidth = 0;
3211 PositionVector endCrossingShape;
3212 PositionVector startCrossingShape;
3213 // check for connected crossings
3214 bool connectsCrossing = false;
3215 std::vector<Position> connectedPoints;
3216 for (auto c : getCrossings()) {
3217 if (gDebugFlag1) {
3218 std::cout << " crossing=" << c->id << " sortedEdges=" << toString(c->edges) << "\n";
3219 }
3220 if (c->edges.back() == normalizedLanes[end].first
3221 && (normalizedLanes[end].second.permissions & SVC_PEDESTRIAN) == 0) {
3222 // crossing ends
3223 if (c->nextWalkingArea != "") {
3224 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' targets '%' and '%'."),
3225 getID(), c->id, c->nextWalkingArea, wa.id);
3226 c->valid = false;
3227 }
3228 c->nextWalkingArea = wa.id;
3229 wa.prevCrossings.push_back(c->id);
3230 if ((int)c->edges.size() < wa.minPrevCrossingEdges) {
3231 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3232 endCrossingWidth = c->width;
3233 endCrossingShape = c->shape;
3234 wa.width = MAX2(wa.width, endCrossingWidth);
3235 connectsCrossing = true;
3236 connectedPoints.push_back(c->shape[-1]);
3237 wa.minPrevCrossingEdges = (int)c->edges.size();
3238 }
3239 if (gDebugFlag1) {
3240 std::cout << " crossing " << c->id << " ends\n";
3241 }
3242 }
3243 if (c->edges.front() == normalizedLanes[prev].first
3244 && (normalizedLanes[prev].second.permissions & SVC_PEDESTRIAN) == 0) {
3245 // crossing starts
3246 if (c->prevWalkingArea != "") {
3247 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' is targeted by '%' and '%'."),
3248 getID(), c->id, c->prevWalkingArea, wa.id);
3249 c->valid = false;
3250 }
3251 if (c->valid && std::find(wa.prevCrossings.begin(), wa.prevCrossings.end(), c->id) != wa.prevCrossings.end()) {
3252 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' starts and ends at walkingarea '%'."),
3253 getID(), c->id, wa.id);
3254 c->valid = false;
3255 }
3256 c->prevWalkingArea = wa.id;
3257 wa.nextCrossings.push_back(c->id);
3258 if ((int)c->edges.size() < wa.minNextCrossingEdges) {
3259 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3260 startCrossingWidth = c->width;
3261 startCrossingShape = c->shape;
3262 wa.width = MAX2(wa.width, startCrossingWidth);
3263 connectsCrossing = true;
3264 connectedPoints.push_back(c->shape[0]);
3265 wa.minNextCrossingEdges = (int)c->edges.size();
3266 }
3267 if (gDebugFlag1) {
3268 std::cout << " crossing " << c->id << " starts\n";
3269 }
3270 }
3271 if (gDebugFlag1) std::cout << " check connections to crossing " << c->id
3272 << " cFront=" << c->edges.front()->getID() << " cBack=" << c->edges.back()->getID()
3273 << " wEnd=" << normalizedLanes[end].first->getID() << " wStart=" << normalizedLanes[startIdx].first->getID()
3274 << " wStartPrev=" << normalizedLanes[prev].first->getID()
3275 << "\n";
3276 }
3277 if (count < 2 && !connectsCrossing) {
3278 // not relevant for walking
3279 if (gDebugFlag1) {
3280 std::cout << " not relevant for walking: count=" << count << " connectsCrossing=" << connectsCrossing << "\n";
3281 }
3282 continue;
3283 }
3284 // build shape and connections
3285 std::set<const NBEdge*, ComparatorIdLess>& connected = wa.refEdges;
3286 for (int j = 0; j < count; ++j) {
3287 const int nlI = (startIdx + j) % normalizedLanes.size();
3288 NBEdge* edge = normalizedLanes[nlI].first;
3289 NBEdge::Lane l = normalizedLanes[nlI].second;
3290 wa.width = MAX2(wa.width, l.width);
3291 if (connected.count(edge) == 0) {
3292 if (edge->getFromNode() == this) {
3293 wa.nextSidewalks.push_back(edge->getSidewalkID());
3294 connectedPoints.push_back(edge->getLaneShape(0)[0]);
3295 } else {
3296 wa.prevSidewalks.push_back(edge->getSidewalkID());
3297 connectedPoints.push_back(edge->getLaneShape(0)[-1]);
3298 }
3299 connected.insert(edge);
3300 }
3301 l.shape.move2side(-l.width / 2);
3303 l.shape.move2side(l.width);
3304 wa.shape.push_back(l.shape[0]);
3305 }
3306 if (buildExtensions) {
3307 // extension at starting crossing
3308 if (startCrossingShape.size() > 0) {
3309 if (gDebugFlag1) {
3310 std::cout << " extension at startCrossing shape=" << startCrossingShape << "\n";
3311 }
3312 startCrossingShape.move2side(startCrossingWidth / 2);
3313 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // right corner
3314 startCrossingShape.move2side(-startCrossingWidth);
3315 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // left corner goes first
3316 }
3317 // extension at ending crossing
3318 if (endCrossingShape.size() > 0) {
3319 if (gDebugFlag1) {
3320 std::cout << " extension at endCrossing shape=" << endCrossingShape << "\n";
3321 }
3322 endCrossingShape.move2side(endCrossingWidth / 2);
3323 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3324 endCrossingShape.move2side(-endCrossingWidth);
3325 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3326 }
3327 }
3328 if (connected.size() == 2 && !connectsCrossing && wa.nextSidewalks.size() == 1 && wa.prevSidewalks.size() == 1
3329 && normalizedLanes.size() == 2) {
3330 // do not build a walkingArea since a normal connection exists
3331 const NBEdge* e1 = *connected.begin();
3332 const NBEdge* e2 = *(++connected.begin());
3333 if (e1->hasConnectionTo(e2, 0, 0) || e2->hasConnectionTo(e1, 0, 0)) {
3334 if (gDebugFlag1) {
3335 std::cout << " not building a walkingarea since normal connections exist\n";
3336 }
3337 continue;
3338 }
3339 }
3340 if (count == (int)normalizedLanes.size()) {
3341 // junction is covered by the whole walkingarea
3342 wa.shape = myPoly;
3343 } else if (cornerDetail > 0) {
3344 // build smooth inner curve (optional)
3345 int smoothEnd = end;
3346 int smoothPrev = prev;
3347 // extend to green verge
3348 if (endCrossingWidth > 0 && normalizedLanes[smoothEnd].second.permissions == 0) {
3349 smoothEnd = (smoothEnd + 1) % normalizedLanes.size();
3350 }
3351 if (startCrossingWidth > 0 && normalizedLanes[smoothPrev].second.permissions == 0) {
3352 if (smoothPrev == 0) {
3353 smoothPrev = (int)normalizedLanes.size() - 1;
3354 } else {
3355 smoothPrev--;
3356 }
3357 }
3358 PositionVector begShape = normalizedLanes[smoothEnd].second.shape;
3359 begShape = begShape.reverse();
3360 PositionVector begShapeOuter = normalizedLanes[lastIdx].second.shape;
3361 begShapeOuter = begShapeOuter.reverse();
3362 //begShape.extrapolate(endCrossingWidth);
3363 begShape.move2side(normalizedLanes[smoothEnd].second.width / 2);
3364 begShapeOuter.move2side(normalizedLanes[lastIdx].second.width / 2);
3365 PositionVector endShape = normalizedLanes[smoothPrev].second.shape;
3366 PositionVector endShapeOuter = normalizedLanes[startIdx].second.shape;;
3367 endShape.move2side(normalizedLanes[smoothPrev].second.width / 2);
3368 endShapeOuter.move2side(normalizedLanes[startIdx].second.width / 2);
3369 //endShape.extrapolate(startCrossingWidth);
3370 PositionVector curve;
3371 if (count != (int)normalizedLanes.size() || count == 2) {
3372 if ((normalizedLanes[smoothEnd].first->getPermissions() & normalizedLanes[smoothPrev].first->getPermissions() &
3373 ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
3374 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25);
3375 if (curve.length2D() - begShape.back().distanceTo2D(endShape.front()) > 5) {
3376 // recompute less bulging curve
3377 //std::cout << " directLength=" << begShape.back().distanceTo2D(endShape.front()) << " curveLength=" << curve.length2D()
3378 // << " delta=" << curve.length2D() - begShape.back().distanceTo2D(endShape.front()) << "\n";
3379 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3380 }
3381 } else {
3382 const double extend = MIN2(10.0, begShape.back().distanceTo2D(endShape.front()) / 2);
3383 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3384 }
3385 if (gDebugFlag1) std::cout
3386 << " end=" << smoothEnd << " prev=" << smoothPrev
3387 << " endCrossingWidth=" << endCrossingWidth << " startCrossingWidth=" << startCrossingWidth
3388 << " begShape=" << begShape << " endShape=" << endShape << " smooth curve=" << curve
3389 << " begShapeOuter=" << begShapeOuter << " endShapeOuter=" << endShapeOuter
3390 << "\n";
3391 if (curve.size() > 2) {
3392 curve.erase(curve.begin());
3393 curve.pop_back();
3394 if (endCrossingWidth > 0) {
3395 wa.shape.pop_back();
3396 }
3397 if (startCrossingWidth > 0) {
3398 wa.shape.erase(wa.shape.begin());
3399 }
3400 if (count == (int)normalizedLanes.size()) {
3401 curve = curve.reverse();
3402 }
3403 wa.shape.append(curve, 0);
3404 }
3405 }
3406 if (curve.size() > 2 && count == 2) {
3407 const double innerDist = begShape.back().distanceTo2D(endShape[0]);
3408 const double outerDist = begShapeOuter.back().distanceTo2D(endShapeOuter[0]);
3409 if (gDebugFlag1) {
3410 std::cout << " innerDist=" << innerDist << " outerDist=" << outerDist << "\n";
3411 }
3412 if (outerDist > innerDist) {
3413 // we also need a rounded outer curve (unless we have only a single walkingarea)
3414 const double extend = MIN2(10.0, begShapeOuter.back().distanceTo2D(endShapeOuter.front()) / 2);
3415 curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3416 curve = curve.reverse();
3417 wa.shape.erase(wa.shape.begin() + 1, wa.shape.begin() + 3);
3418 wa.shape.insert(wa.shape.begin() + 1, curve.begin(), curve.end());
3419 if (gDebugFlag1) {
3420 std::cout << " outerCurve=" << curve << "\n";
3421 }
3422 }
3423 }
3424 }
3425 // apply custom shapes
3426 if (myWalkingAreaCustomShapes.size() > 0) {
3427 for (auto wacs : myWalkingAreaCustomShapes) {
3428 // every edge in wasc.edges must be part of connected
3429 if ((wacs.shape.size() != 0 || wacs.width != NBEdge::UNSPECIFIED_WIDTH) && includes(connected, wacs.edges)) {
3430 if (wacs.shape.size() != 0) {
3431 wa.shape = wacs.shape;
3432 }
3433 if (wacs.width != NBEdge::UNSPECIFIED_WIDTH) {
3434 wa.width = wacs.width;
3435 }
3436 wa.hasCustomShape = true;
3437 }
3438 }
3439 }
3440 // determine length (average of all possible connections)
3441 double lengthSum = 0;
3442 int combinations = 0;
3443 for (std::vector<Position>::const_iterator it1 = connectedPoints.begin(); it1 != connectedPoints.end(); ++it1) {
3444 for (std::vector<Position>::const_iterator it2 = connectedPoints.begin(); it2 != connectedPoints.end(); ++it2) {
3445 const Position& p1 = *it1;
3446 const Position& p2 = *it2;
3447 if (p1 != p2) {
3448 lengthSum += p1.distanceTo2D(p2);
3449 combinations += 1;
3450 }
3451 }
3452 }
3453 if (gDebugFlag1) {
3454 std::cout << " combinations=" << combinations << " connectedPoints=" << connectedPoints << "\n";
3455 }
3456 wa.length = POSITION_EPS;
3457 if (combinations > 0) {
3458 wa.length = MAX2(POSITION_EPS, lengthSum / combinations);
3459 }
3460 myWalkingAreas.push_back(wa);
3461 }
3462 // build walkingAreas between split crossings
3463 std::vector<Crossing*> validCrossings = getCrossings();
3464 for (std::vector<Crossing*>::iterator it = validCrossings.begin(); it != validCrossings.end(); ++it) {
3465 Crossing& prev = **it;
3466 Crossing& next = (it != validCrossings.begin() ? **(it - 1) :** (validCrossings.end() - 1));
3467 if (gDebugFlag1) {
3468 std::cout << " checkIntermediate: prev=" << prev.id << " next=" << next.id << " prev.nextWA=" << prev.nextWalkingArea << "\n";
3469 }
3470 if (prev.nextWalkingArea == "") {
3471 if (next.prevWalkingArea != "" || &prev == &next) {
3472 WRITE_WARNINGF(TL("Invalid pedestrian topology: crossing '%' across [%] has no target."), prev.id, toString(prev.edges));
3473 prev.valid = false;
3474 continue;
3475 }
3476 WalkingArea wa(":" + getID() + "_w" + toString(index++), prev.width);
3477 prev.nextWalkingArea = wa.id;
3478 wa.nextCrossings.push_back(next.id);
3479 next.prevWalkingArea = wa.id;
3480 // back of previous crossing
3481 PositionVector tmp = prev.shape;
3482 tmp.move2side(-prev.width / 2);
3483 wa.shape.push_back(tmp[-1]);
3484 tmp.move2side(prev.width);
3485 wa.shape.push_back(tmp[-1]);
3486 // front of next crossing
3487 tmp = next.shape;
3488 tmp.move2side(prev.width / 2);
3489 wa.shape.push_back(tmp[0]);
3490 tmp.move2side(-prev.width);
3491 wa.shape.push_back(tmp[0]);
3492 wa.refEdges.insert(prev.edges.begin(), prev.edges.end());
3493 wa.refEdges.insert(next.edges.begin(), next.edges.end());
3494 // apply custom shapes
3495 if (myWalkingAreaCustomShapes.size() > 0) {
3496 for (auto wacs : myWalkingAreaCustomShapes) {
3497 // every edge in wacs.edges must be part of crossed
3498 if (wacs.shape.size() != 0 && wacs.edges.size() > 1 && includes(wa.refEdges, wacs.edges)) {
3499 wa.shape = wacs.shape;
3500 wa.hasCustomShape = true;
3501 }
3502 }
3503 }
3504 // length (special case)
3505 wa.length = MAX2(POSITION_EPS, prev.shape.back().distanceTo2D(next.shape.front()));
3506 myWalkingAreas.push_back(wa);
3507 if (gDebugFlag1) {
3508 std::cout << " build wa=" << wa.id << "\n";
3509 }
3510 }
3511 }
3512}
3513
3514bool
3515NBNode::includes(const std::set<const NBEdge*, ComparatorIdLess>& super,
3516 const std::set<const NBEdge*, ComparatorIdLess>& sub) {
3517 // for some reason std::include does not work reliably
3518 for (const NBEdge* e : sub) {
3519 if (super.count(const_cast<NBEdge*>(e)) == 0) {
3520 return false;
3521 }
3522 }
3523 return true;
3524}
3525
3526
3527bool
3528NBNode::crossingBetween(const NBEdge* e1, const NBEdge* e2) const {
3529 if (e1 == e2) {
3530 return false;
3531 }
3532 if (myAllEdges.size() > 3) {
3533 // pedestrian scramble
3534 return false;
3535 }
3536 for (auto c : getCrossings()) {
3537 const EdgeVector& edges = c->edges;
3538 EdgeVector::const_iterator it1 = std::find(edges.begin(), edges.end(), e1);
3539 EdgeVector::const_iterator it2 = std::find(edges.begin(), edges.end(), e2);
3540 if (it1 != edges.end() && it2 != edges.end()) {
3541 return true;
3542 }
3543 }
3544 return false;
3545}
3546
3547
3548bool
3549NBNode::alreadyConnectedPaths(const NBEdge* e1, const NBEdge* e2, double dist) const {
3550 if (e1 == e2) {
3551 return false;
3552 }
3553 if (e1->getPermissions() != SVC_PEDESTRIAN
3554 || e2->getPermissions() != SVC_PEDESTRIAN) {
3555 // no paths
3556 return false;
3557 }
3558 if (e1->getFinalLength() > dist &&
3559 e2->getFinalLength() > dist) {
3560 // too long
3561 return false;
3562 }
3563 NBNode* other1 = e1->getFromNode() == this ? e1->getToNode() : e1->getFromNode();
3564 NBNode* other2 = e2->getFromNode() == this ? e2->getToNode() : e2->getFromNode();
3565 return other1 == other2;
3566}
3567
3568
3569bool
3570NBNode::crossesFringe(const NBEdge* e1, const NBEdge* e2) const {
3572 && myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1
3573 && (e1->isTurningDirectionAt(e2) || e2->isTurningDirectionAt(e1));
3574}
3575
3576
3578NBNode::edgesBetween(const NBEdge* e1, const NBEdge* e2) const {
3579 EdgeVector result;
3580 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), e1);
3581 assert(it != myAllEdges.end());
3583 EdgeVector::const_iterator it_end = std::find(myAllEdges.begin(), myAllEdges.end(), e2);
3584 assert(it_end != myAllEdges.end());
3585 while (it != it_end) {
3586 result.push_back(*it);
3588 }
3589 return result;
3590}
3591
3592
3593void
3594NBNode::addWalkingAreaShape(EdgeVector edges, const PositionVector& shape, double width) {
3596 wacs.edges.insert(edges.begin(), edges.end());
3597 wacs.shape = shape;
3598 wacs.width = width;
3599 myWalkingAreaCustomShapes.push_back(wacs);
3600}
3601
3602
3603bool
3607
3608bool
3609NBNode::geometryLike(const EdgeVector& incoming, const EdgeVector& outgoing) const {
3610 if (incoming.size() == 1 && outgoing.size() == 1) {
3611 return true;
3612 }
3613 if (incoming.size() == 2 && outgoing.size() == 2) {
3614 // check whether the incoming and outgoing edges are pairwise (near) parallel and
3615 // thus the only cross-connections could be turn-arounds
3616 NBEdge* in0 = incoming[0];
3617 NBEdge* in1 = incoming[1];
3618 NBEdge* out0 = outgoing[0];
3619 NBEdge* out1 = outgoing[1];
3620 if ((in0->isTurningDirectionAt(out0) || in0->isTurningDirectionAt(out1))
3621 && (in1->isTurningDirectionAt(out0) || in1->isTurningDirectionAt(out1))) {
3622 return true;
3623 }
3624 if (in0->getGeometry() == in1->getGeometry() && out0->getGeometry() == out1->getGeometry()) {
3625 // overlapping edges
3626 return true;
3627 }
3628 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
3629 NBEdge* inEdge = *it;
3630 double angle0 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(this), out0->getAngleAtNode(this)));
3631 double angle1 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(this), out1->getAngleAtNode(this)));
3632 if (MAX2(angle0, angle1) <= 160) {
3633 // neither of the outgoing edges is parallel to inEdge
3634 return false;
3635 }
3636 }
3637 return true;
3638 }
3639 return false;
3640}
3641
3642void
3648
3649bool
3651 for (NBEdge* out : myOutgoingEdges) {
3652 if (out->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
3653 return true;
3654 }
3655 }
3656 return false;
3657}
3658
3660NBNode::addCrossing(EdgeVector edges, double width, bool priority, int tlIndex, int tlIndex2,
3661 const PositionVector& customShape, bool fromSumoNet) {
3662 Crossing* c = new Crossing(this, edges, width, priority, tlIndex, tlIndex2, customShape);
3663 myCrossings.push_back(std::unique_ptr<Crossing>(c));
3664 if (fromSumoNet) {
3666 }
3667 return c;
3668}
3669
3670
3671void
3673 EdgeSet edgeSet(edges.begin(), edges.end());
3674 for (auto it = myCrossings.begin(); it != myCrossings.end();) {
3675 EdgeSet edgeSet2((*it)->edges.begin(), (*it)->edges.end());
3676 if (edgeSet == edgeSet2) {
3677 it = myCrossings.erase(it);
3678 } else {
3679 ++it;
3680 }
3681 }
3682}
3683
3684
3686NBNode::getCrossing(const std::string& id) const {
3687 for (auto& c : myCrossings) {
3688 if (c->id == id) {
3689 return c.get();
3690 }
3691 }
3692 throw ProcessError(TLF("Request for unknown crossing '%'", id));
3693}
3694
3695
3697NBNode::getCrossing(const EdgeVector& edges, bool hardFail) const {
3698 const EdgeSet edgeSet(edges.begin(), edges.end());
3699 for (auto& crossing : myCrossings) {
3700 const EdgeSet edgeSet2(crossing->edges.begin(), crossing->edges.end());
3701 if (edgeSet == edgeSet2) {
3702 return crossing.get();
3703 }
3704 }
3705 if (!hardFail) {
3706 return nullptr;
3707 }
3708 throw ProcessError(TL("Request for unknown crossing for the given Edges"));
3709}
3710
3711
3713NBNode::getWalkingArea(const std::string& id) {
3714 for (auto& walkingArea : myWalkingAreas) {
3715 if (walkingArea.id == id) {
3716 return walkingArea;
3717 }
3718 }
3719 throw ProcessError(TL("Request for unknown crossing for the given Edges"));
3720}
3721
3722
3723bool
3724NBNode::setCrossingTLIndices(const std::string& tlID, int startIndex) {
3725 bool usedCustom = false;
3726 for (auto c : getCrossings()) {
3727 c->tlLinkIndex = startIndex++;
3728 c->tlID = tlID;
3729 if (c->customTLIndex != -1) {
3730 usedCustom |= (c->tlLinkIndex != c->customTLIndex);
3731 c->tlLinkIndex = c->customTLIndex;
3732 }
3733 c->tlLinkIndex2 = c->customTLIndex2;
3734 }
3735 return usedCustom;
3736}
3737
3738
3739int
3741 if (myRequest == nullptr) {
3742 // could be an uncontrolled type
3743 int result = 0;
3744 for (const NBEdge* const edge : myIncomingEdges) {
3745 result += (int)edge->getConnections().size();
3746 }
3747 return result;
3748 } else {
3749 return myRequest->getSizes().second;
3750 }
3751}
3752
3753
3754int
3756 int result = 0;
3757 for (const NBEdge* const e : myIncomingEdges) {
3758 for (const NBEdge::Connection& cand : e->getConnections()) {
3759 if (e == from && cand.fromLane == con.fromLane && cand.toLane == con.toLane && cand.toEdge == con.toEdge) {
3760 return result;
3761 }
3762 result++;
3763 }
3764 }
3765 return -1;
3766}
3767
3768
3771 /* Conceptually, the center point would be identical with myPosition.
3772 * However, if the shape is influenced by custom geometry endpoints of the adjoining edges,
3773 * myPosition may fall outside the shape. In this case it is better to use
3774 * the center of the shape
3775 **/
3776 PositionVector tmp = myPoly;
3777 tmp.closePolygon();
3778 //std::cout << getID() << " around=" << tmp.around(myPosition) << " dist=" << tmp.distance2D(myPosition) << "\n";
3779 if (tmp.size() < 3 || tmp.around(myPosition) || tmp.distance2D(myPosition) < POSITION_EPS) {
3780 return myPosition;
3781 }
3782 return myPoly.getPolygonCenter();
3783}
3784
3785
3788 EdgeVector result = myAllEdges;
3789 if (gDebugFlag1) {
3790 std::cout << " angles:\n";
3791 for (EdgeVector::const_iterator it = result.begin(); it != result.end(); ++it) {
3792 std::cout << " edge=" << (*it)->getID() << " edgeAngle=" << (*it)->getAngleAtNode(this) << " angleToShape=" << (*it)->getAngleAtNodeToCenter(this) << "\n";
3793 }
3794 std::cout << " allEdges before: " << toString(result) << "\n";
3795 }
3796 sort(result.begin(), result.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3797 // let the first edge in myAllEdges remain the first
3798 if (gDebugFlag1) {
3799 std::cout << " allEdges sorted: " << toString(result) << "\n";
3800 }
3801 rotate(result.begin(), std::find(result.begin(), result.end(), *myAllEdges.begin()), result.end());
3802 if (gDebugFlag1) {
3803 std::cout << " allEdges rotated: " << toString(result) << "\n";
3804 }
3805 return result;
3806}
3807
3808
3809void
3811 // simple case: edges with LaneSpreadFunction::CENTER and a (possible) turndirection at the same node
3812 for (EdgeVector::iterator it = myIncomingEdges.begin(); it != myIncomingEdges.end(); it++) {
3813 NBEdge* edge = *it;
3814 NBEdge* turnDest = edge->getTurnDestination(true);
3815 if (turnDest != nullptr) {
3816 edge->shiftPositionAtNode(this, turnDest);
3817 turnDest->shiftPositionAtNode(this, edge);
3818 }
3819 }
3820 // @todo: edges in the same direction with sharp angles starting/ending at the same position
3821}
3822
3823
3824bool
3830
3831
3832bool
3833NBNode::rightOnRedConflict(int index, int foeIndex) const {
3835 if (def->rightOnRedConflict(index, foeIndex)) {
3836 return true;
3837 }
3838 }
3839 return false;
3840}
3841
3842
3843void
3844NBNode::sortEdges(bool useNodeShape) {
3845 if (myAllEdges.size() == 0) {
3846 return;
3847 }
3848 EdgeVector allEdgesOriginal = myAllEdges;
3849 EdgeVector& allEdges = myAllEdges;
3850 EdgeVector& incoming = myIncomingEdges;
3851 EdgeVector& outgoing = myOutgoingEdges;
3852
3853 // sort the edges by angle (this is the canonical sorting)
3854 std::sort(allEdges.begin(), allEdges.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3855 std::sort(incoming.begin(), incoming.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3856 std::sort(outgoing.begin(), outgoing.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
3857 std::vector<NBEdge*>::iterator j;
3858 for (j = allEdges.begin(); j != allEdges.end() - 1 && j != allEdges.end(); ++j) {
3860 }
3861 if (allEdges.size() > 1 && j != allEdges.end()) {
3862 NBNodesEdgesSorter::swapWhenReversed(this, allEdges.end() - 1, allEdges.begin());
3863 }
3864
3865 // sort again using additional geometry information
3866 NBEdge* firstOfAll = allEdges.front();
3867 NBEdge* firstOfIncoming = incoming.size() > 0 ? incoming.front() : 0;
3868 NBEdge* firstOfOutgoing = outgoing.size() > 0 ? outgoing.front() : 0;
3869 // sort by the angle between the node shape center and the point where the edge meets the node shape
3870 std::sort(allEdges.begin(), allEdges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3871 std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3872 std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3873 // let the first edge remain the first
3874 rotate(allEdges.begin(), std::find(allEdges.begin(), allEdges.end(), firstOfAll), allEdges.end());
3875 if (firstOfIncoming != nullptr) {
3876 rotate(incoming.begin(), std::find(incoming.begin(), incoming.end(), firstOfIncoming), incoming.end());
3877 }
3878 if (firstOfOutgoing != nullptr) {
3879 rotate(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), firstOfOutgoing), outgoing.end());
3880 }
3881#ifdef DEBUG_EDGE_SORTING
3882 if (DEBUGCOND) {
3883 std::cout << "sortedEdges (useNodeShape=" << useNodeShape << "):\n";
3884 for (NBEdge* e : allEdges) {
3885 std::cout << " " << e->getID()
3886 << " angleToCenter=" << e->getAngleAtNodeToCenter(this)
3887 << " junctionAngle=" << e->getAngleAtNode(this) << "\n";
3888 }
3889 }
3890#endif
3891
3892 // fixing some pathological all edges orderings
3893 // 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'
3894 if (incoming.size() == outgoing.size() && incoming.front() == allEdges.front()) {
3895 std::vector<NBEdge*>::const_iterator in, out;
3896 std::vector<NBEdge*> allTmp;
3897 for (in = incoming.begin(), out = outgoing.begin(); in != incoming.end(); ++in, ++out) {
3898 if ((*in)->isTurningDirectionAt(*out)) {
3899 allTmp.push_back(*in);
3900 allTmp.push_back(*out);
3901 } else {
3902 break;
3903 }
3904 }
3905 if (allTmp.size() == allEdges.size()) {
3906 allEdges = allTmp;
3907 }
3908 }
3909 // sort the crossings
3910 std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, allEdges));
3911 //if (crossings.size() > 0) {
3912 // std::cout << " crossings at " << getID() << "\n";
3913 // for (std::vector<NBNode::Crossing*>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
3914 // std::cout << " " << toString((*it)->edges) << "\n";
3915 // }
3916 //}
3917
3918 if (useNodeShape && myAllEdges != allEdgesOriginal) {
3919 // sorting order changed after node shape was computed.
3920 computeNodeShape(-1);
3921 for (NBEdge* e : myAllEdges) {
3922 e->computeEdgeShape();
3923 }
3924 }
3925}
3926
3927std::vector<std::pair<Position, std::string> >
3929 // using a set would be nicer but we want to have some slack in position identification
3930 std::vector<std::pair<Position, std::string> >result;
3931 for (NBEdge* e : myAllEdges) {
3932 Position pos = this == e->getFromNode() ? e->getGeometry().front() : e->getGeometry().back();
3933 const std::string origID = e->getParameter(this == e->getFromNode() ? "origFrom" : "origTo");
3934 bool unique = true;
3935 for (const auto& pair : result) {
3936 if (pos.almostSame(pair.first) || (origID != "" && pair.second == origID)) {
3937 unique = false;
3938 break;
3939 }
3940 }
3941 if (unique) {
3942 result.push_back(std::make_pair(pos, origID));
3943 }
3944 }
3945 return result;
3946}
3947
3948
3949/****************************************************************************/
@ DEFAULT
default cursor
#define DEG2RAD(x)
Definition GeomHelper.h:35
#define RAD2DEG(x)
Definition GeomHelper.h:36
#define DEBUGCOND(PEDID)
#define DEBUGCOND2(LANE)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:271
#define WRITE_WARNING(msg)
Definition MsgHandler.h:270
#define TL(string)
Definition MsgHandler.h:287
#define TLF(string,...)
Definition MsgHandler.h:288
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 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
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.
static double getCWAngleDiff(double angle1, double angle2)
Returns the distance of second angle from first angle clockwise.
static double angleDiff(const double angle1, const double angle2)
Returns the difference of the second angle to the first angle in radiants.
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.
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.
void replaceOutgoing(const EdgeVector &which, NBEdge *const by)
Replaces outgoing edges from the vector (source) by the given edge.
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)
The representation of a single edge during network building.
Definition NBEdge.h:92
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4232
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
@ ROUNDABOUT
Definition NBEdge.h:377
@ 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:4384
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:1235
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:4292
@ 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:1366
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:3516
bool isBidiEdge(bool checkPotential=false) const
whether this edge is part of a bidirectional edge pair
Definition NBEdge.cpp:741
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:4070
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:1265
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:4361
std::set< SVCPermissions > getPermissionVariants(int iStart, int iEnd) const
return all permission variants within the specified lane range [iStart, iEnd[
Definition NBEdge.cpp:4349
@ COMPUTED
The connection was computed.
static PositionVector startShapeAt(const PositionVector &laneShape, const NBNode *startNode, PositionVector nodeShape)
Definition NBEdge.cpp:876
std::string getSidewalkID()
get the lane id for the canonical sidewalk lane
Definition NBEdge.cpp:4409
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:1340
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:4325
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:1120
int getJunctionPriority(const NBNode *const node) const
Returns the junction priority (normalised for the node currently build)
Definition NBEdge.cpp:2051
bool isOffRamp() const
Definition NBEdge.h:1386
EdgeVector getConnectedEdges() const
Returns the list of outgoing edges unsorted.
Definition NBEdge.cpp:1315
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:3861
void shiftPositionAtNode(NBNode *node, NBEdge *opposite)
shift geometry at the given node to avoid overlap
Definition NBEdge.cpp:4529
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition NBEdge.cpp:2077
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:1475
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:1103
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:1259
const PositionVector & getLaneShape(int i) const
Returns the shape of the nth lane.
Definition NBEdge.cpp:936
double getFinalLength() const
get length that will be assigned to the lanes in the final network
Definition NBEdge.cpp:4564
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition NBEdge.cpp:1327
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:4308
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:2315
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:3594
void avoidOverlap()
fix overlap
Definition NBNode.cpp:3810
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:913
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:3770
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:3672
NBEdge * getNextCompatibleOutgoing(const NBEdge *incoming, SVCPermissions vehPerm, EdgeVector::const_iterator start, bool clockwise) const
Definition NBNode.cpp:2243
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:2398
int getConnectionIndex(const NBEdge *from, const NBEdge::Connection &con) const
return the index of the given connection
Definition NBNode.cpp:3755
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:3740
bool setCrossingTLIndices(const std::string &tlID, int startIndex)
Definition NBNode.cpp:3724
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:3686
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:2846
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:2436
void buildCrossingsAndWalkingAreas()
build crossings, and walkingareas. Also removes invalid loaded crossings if wished
Definition NBNode.cpp:2857
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:2386
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:3833
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:966
void setCustomShape(const PositionVector &shape)
set the junction shape
Definition NBNode.cpp:2577
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:3089
void remapRemoved(NBTrafficLightLogicCont &tc, NBEdge *removed, const EdgeVector &incoming, const EdgeVector &outgoing)
remap removed
Definition NBNode.cpp:2162
int buildCrossings()
build pedestrian crossings
Definition NBNode.cpp:2963
SumoXMLNodeType myType
The type of the junction.
Definition NBNode.h:916
EdgeVector myOutgoingEdges
Vector of outgoing edges.
Definition NBNode.h:901
bool myKeepClear
whether the junction area must be kept clear
Definition NBNode.h:940
static bool isTrafficLight(SumoXMLNodeType type)
return whether the given type is a traffic light
Definition NBNode.cpp:3825
void discardWalkingareas()
discard previously built walkingareas (required for repeated computation by netedit)
Definition NBNode.cpp:2933
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:931
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:3928
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:2535
int checkCrossing(EdgeVector candidates, bool checkOnly=false)
Definition NBNode.cpp:2734
bool myHaveCustomPoly
whether this nodes shape was set by the user
Definition NBNode.h:928
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:3660
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:955
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:2145
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:3549
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:904
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:3844
RightOfWay myRightOfWay
how to compute right of way for this node
Definition NBNode.h:943
bool myIsBentPriority
Definition NBNode.h:963
std::set< NBTrafficLightDefinition * > myTrafficLights
traffic lights of node
Definition NBNode.h:934
double myRadius
the turning radius (for all corners) at this node in m.
Definition NBNode.h:937
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:3515
bool bidiConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether the foe connections is oncoming on the same lane
Definition NBNode.cpp:2071
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:2126
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:2829
void setRoundabout()
update the type of this node as a roundabout
Definition NBNode.cpp:3643
bool mergeConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether multiple connections from the same edge target the same lane
Definition NBNode.cpp:2062
bool myDiscardAllCrossings
whether to discard all pedestrian crossings
Definition NBNode.h:952
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:2905
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:895
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:2080
void discardAllCrossings(bool rejectAll)
discard all current (and optionally future) crossings
Definition NBNode.cpp:2923
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:2304
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:907
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:2268
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:3528
bool isDistrict() const
check if node is a district
Definition NBNode.cpp:2617
NBDistrict * myDistrict
The district the node is the centre of.
Definition NBNode.h:922
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:958
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:2571
std::vector< WalkingArea > myWalkingAreas
Vector of walking areas.
Definition NBNode.h:910
NBConnectionProhibits myBlockedConnections
The container for connection block dependencies.
Definition NBNode.h:919
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:946
bool checkIsRemovable() const
check if node is removable
Definition NBNode.cpp:2452
bool isRoundabout() const
return whether this node is part of a roundabout
Definition NBNode.cpp:3650
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:2458
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:2155
void removeDoubleEdges()
remove duble edges
Definition NBNode.cpp:1775
double buildInnerEdges()
build internal lanes, pedestrian crossings and walking areas
Definition NBNode.cpp:2939
PositionVector myPoly
the (outer) shape of the junction
Definition NBNode.h:925
NBEdge * getConnectionTo(NBNode *n) const
get connection to certain node
Definition NBNode.cpp:2589
bool crossesFringe(const NBEdge *e1, const NBEdge *e2) const
return true if the given sidewalks are separated by a fringe road
Definition NBNode.cpp:3570
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:3787
EdgeVector edgesBetween(const NBEdge *e1, const NBEdge *e2) const
return all edges that lie clockwise between the given edges
Definition NBNode.cpp:3578
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:3604
bool isNearDistrict() const
@chech if node is near district
Definition NBNode.cpp:2600
static const int INDIRECT_LEFT
Definition NBNode.h:226
EdgeVector myIncomingEdges
Vector of incoming edges.
Definition NBNode.h:898
WalkingArea & getWalkingArea(const std::string &id)
return the walkingArea with the given ID
Definition NBNode.cpp:3713
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:2623
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(bool forceSmall)
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...
Sorts incoming and outgoing edges clockwise around the given node.
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 bidiConflict(const NBEdge *from, const NBEdge::Connection &con, const NBEdge *prohibitorFrom, const NBEdge::Connection &prohibitorCon, bool foes) const
whether straight connections are in conflict via bidirectional lane use
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.
bool hasConflictAtLink(int linkIndex) const
whether there are conflicting streams of traffic for the given link index
const std::string & getFoes(int linkIndex) const
bool hasConflict() const
whether there are conflicting streams of traffic at this node
void buildBitfieldLogic()
builds the bitset-representation of the logic
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
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.
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
void writeLogic(OutputDevice &into) const
void computeLogic(const bool checkLaneFoes)
writes the XML-representation of the logic as a bitset-logic XML representation
std::pair< int, int > getSizes() const
returns the number of the junction's lanes and the number of the junction's links in respect.
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.
const std::string & getResponse(int linkIndex) const
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.
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.
Static storage of an output device and its base (abstract) implementation.
An upper class for objects with additional parameters.
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
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