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