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-2025 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) {
987 return false;
988 }
989 LinkDirection d1 = getDirection(fromE, toE);
990 const bool thisRight = (d1 == LinkDirection::RIGHT || d1 == LinkDirection::PARTRIGHT);
991 const bool rightTurnConflict = (thisRight &&
992 NBNode::rightTurnConflict(fromE, toE, c.fromLane, otherFromE, otherToE, otherC.fromLane));
993 if (thisRight && !rightTurnConflict) {
994 return false;
995 }
996 if (myRequest && myRequest->indirectLeftTurnConflict(fromE, c, otherFromE, otherC, false)) {
997 return true;
998 }
999 if (!(foes(otherFromE, otherToE, fromE, toE) || myRequest == nullptr || rightTurnConflict)) {
1000 // if they do not cross, no waiting place is needed
1001 return false;
1002 }
1003 LinkDirection d2 = getDirection(otherFromE, otherToE);
1004 if (d2 == LinkDirection::TURN) {
1005 return false;
1006 }
1007 if (fromE == otherFromE && !thisRight) {
1008 // ignore same edge links except for right-turns
1009 return false;
1010 }
1011 if (thisRight && d2 != LinkDirection::STRAIGHT) {
1012 return false;
1013 }
1014 }
1015 if (c.tlID != "") {
1017 for (std::set<NBTrafficLightDefinition*>::const_iterator it = myTrafficLights.begin(); it != myTrafficLights.end(); ++it) {
1018 if ((*it)->needsCont(fromE, toE, otherFromE, otherToE)) {
1019 return true;
1020 }
1021 }
1022 return false;
1023 }
1024 if (fromE->getJunctionPriority(this) > 0 && otherFromE->getJunctionPriority(this) > 0) {
1025 return mustBrake(fromE, toE, c.fromLane, c.toLane, false);
1026 }
1027 return false;
1028}
1029
1030bool
1032 const NBEdge* foeFrom, const NBEdge::Connection& foe) const {
1033 return (foe.haveVia && isTLControlled() && c.tlLinkIndex >= 0 && foe.tlLinkIndex >= 0
1034 && !foeFrom->isTurningDirectionAt(foe.toEdge)
1035 && foes(from, c.toEdge, foeFrom, foe.toEdge)
1036 && !needsCont(foeFrom, from, foe, c, true));
1037}
1038
1039
1040void
1042 std::set<NBTrafficLightDefinition*> trafficLights = myTrafficLights; // make a copy because we will modify the original
1043 for (std::set<NBTrafficLightDefinition*>::const_iterator i = trafficLights.begin(); i != trafficLights.end(); ++i) {
1044 // if this is the only controlled node we keep the tlDef as it is to generate a warning later
1045 if ((*i)->getNodes().size() > 1) {
1046 myTrafficLights.erase(*i);
1047 (*i)->removeNode(this);
1048 (*i)->setParticipantsInformation();
1049 (*i)->setTLControllingInformation();
1050 }
1051 }
1052}
1053
1054
1055void
1057 delete myRequest; // possibly recomputation step
1058 myRequest = nullptr;
1059 if (myIncomingEdges.size() == 0 || myOutgoingEdges.size() == 0) {
1060 // no logic if nothing happens here
1063 return;
1064 }
1065 // compute the logic if necessary or split the junction
1067 // build the request
1069 // check whether it is not too large
1070 int numConnections = numNormalConnections();
1071 if (numConnections >= SUMO_MAX_CONNECTIONS) {
1072 // yep -> make it untcontrolled, warn
1073 delete myRequest;
1074 myRequest = nullptr;
1077 } else {
1079 }
1080 WRITE_WARNINGF(TL("Junction '%' is too complicated (% connections, max %); will be set to %."),
1081 getID(), numConnections, SUMO_MAX_CONNECTIONS, toString(myType));
1082 } else if (numConnections == 0) {
1083 delete myRequest;
1084 myRequest = nullptr;
1087 } else {
1089 }
1090 }
1091}
1092
1093
1094void
1095NBNode::computeLogic2(bool checkLaneFoes) {
1096 if (myRequest != nullptr) {
1097 myRequest->computeLogic(checkLaneFoes);
1098 }
1099}
1100
1101void
1103 if (hasConflict()) {
1104 if (!myKeepClear) {
1105 for (NBEdge* incoming : myIncomingEdges) {
1106 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1107 for (NBEdge::Connection& c : connections) {
1108 c.keepClear = KEEPCLEAR_FALSE;
1109 }
1110 }
1111 } else if (geometryLike() && myCrossings.size() == 0 && !isTLControlled()) {
1112 int linkIndex = 0;
1113 for (NBEdge* incoming : myIncomingEdges) {
1114 std::vector<NBEdge::Connection>& connections = incoming->getConnections();
1115 for (NBEdge::Connection& c : connections) {
1116 if (c.keepClear == KEEPCLEAR_UNSPECIFIED && myRequest->hasConflictAtLink(linkIndex)) {
1117 const LinkState linkState = getLinkState(incoming, c.toEdge, c.fromLane, c.toLane, c.mayDefinitelyPass, c.tlID);
1118 if (linkState == LINKSTATE_MAJOR) {
1119 c.keepClear = KEEPCLEAR_FALSE;
1120 }
1121 }
1122 }
1123 linkIndex++;
1124 }
1125 }
1126 }
1127}
1128
1129
1130bool
1132 if (myRequest) {
1133 myRequest->writeLogic(into);
1134 return true;
1135 }
1136 return false;
1137}
1138
1139
1140const std::string
1141NBNode::getFoes(int linkIndex) const {
1142 if (myRequest == nullptr) {
1143 return "";
1144 } else {
1145 return myRequest->getFoes(linkIndex);
1146 }
1147}
1148
1149
1150const std::string
1151NBNode::getResponse(int linkIndex) const {
1152 if (myRequest == nullptr) {
1153 return "";
1154 } else {
1155 return myRequest->getResponse(linkIndex);
1156 }
1157}
1158
1159bool
1161 if (myRequest == nullptr) {
1162 return false;
1163 } else {
1164 return myRequest->hasConflict();
1165 }
1166}
1167
1168
1169bool
1171 if (myRequest == nullptr) {
1172 return false;
1173 }
1174 for (const auto& con : e->getConnections()) {
1175 const int index = getConnectionIndex(e, con);
1176 if (myRequest->hasConflictAtLink(index)) {
1177 return true;
1178 }
1179 }
1180 return false;
1181}
1182
1183
1184void
1187 sortEdges(false);
1188 computeNodeShape(-1);
1189 for (NBEdge* edge : myAllEdges) {
1190 edge->computeEdgeShape();
1191 }
1192}
1193
1194void
1195NBNode::computeNodeShape(double mismatchThreshold) {
1196 if (myHaveCustomPoly) {
1197 return;
1198 }
1199 if (myIncomingEdges.size() == 0 && myOutgoingEdges.size() == 0) {
1200 // may be an intermediate step during network editing
1201 myPoly.clear();
1202 myPoly.push_back(myPosition);
1203 return;
1204 }
1205 if (OptionsCont::getOptions().getFloat("default.junctions.radius") < 0) {
1206 // skip shape computation by option
1207 return;
1208 }
1209 try {
1210 NBNodeShapeComputer computer(*this);
1211 myPoly = computer.compute(OptionsCont::getOptions().getBool("junctions.minimal-shape"));
1212 if (myRadius == UNSPECIFIED_RADIUS && !OptionsCont::getOptions().isDefault("default.junctions.radius")) {
1213 myRadius = computer.getRadius();
1214 }
1215 if (myPoly.size() > 0) {
1216 PositionVector tmp = myPoly;
1217 tmp.push_back_noDoublePos(tmp[0]); // need closed shape
1218 if (mismatchThreshold >= 0
1219 && !tmp.around(myPosition)
1220 && tmp.distance2D(myPosition) > mismatchThreshold) {
1221 WRITE_WARNINGF(TL("Shape for junction '%' has distance % to its given position."), myID, tmp.distance2D(myPosition));
1222 }
1223 }
1224 } catch (InvalidArgument&) {
1225 WRITE_WARNINGF(TL("For junction '%': could not compute shape."), myID);
1226 // make sure our shape is not empty because our XML schema forbids empty attributes
1227 myPoly.clear();
1228 myPoly.push_back(myPosition);
1229 }
1230}
1231
1232
1233void
1235 // special case a):
1236 // one in, one out, the outgoing has more lanes
1237 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1238 NBEdge* in = myIncomingEdges[0];
1239 NBEdge* out = myOutgoingEdges[0];
1240 // check if it's not the turnaround
1241 if (in->getTurnDestination() == out) {
1242 // will be added later or not...
1243 return;
1244 }
1245 int inOffset, inEnd, outOffset, outEnd, addedLanes;
1246 getReduction(out, in, outOffset, outEnd, inOffset, inEnd, addedLanes);
1248 && addedLanes > 0
1249 && in->isConnectedTo(out)) {
1250 const int addedRight = addedLanesRight(out, addedLanes);
1251 const int addedLeft = addedLanes - addedRight;
1252#ifdef DEBUG_CONNECTION_GUESSING
1253 if (DEBUGCOND) {
1254 std::cout << "l2l node=" << getID() << " specialCase a. addedRight=" << addedRight << " addedLeft=" << addedLeft << " inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << "\n";
1255 }
1256#endif
1257 // "straight" connections
1258 for (int i = inOffset; i < inEnd; ++i) {
1259 in->setConnection(i, out, i - inOffset + outOffset + addedRight, NBEdge::Lane2LaneInfoType::COMPUTED);
1260 }
1261 // connect extra lane on the right
1262 for (int i = 0; i < addedRight; ++i) {
1263 in->setConnection(inOffset, out, outOffset + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1264 }
1265 // connect extra lane on the left
1266 const int inLeftMost = inEnd - 1;;
1267 const int outOffset2 = outOffset + addedRight + inEnd - inOffset;
1268 for (int i = 0; i < addedLeft; ++i) {
1269 in->setConnection(inLeftMost, out, outOffset2 + i, NBEdge::Lane2LaneInfoType::COMPUTED);
1270 }
1271 if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1273 }
1274 return;
1275 }
1276 }
1277 // special case b):
1278 // two in, one out, the outgoing has the same number of lanes as the sum of the incoming
1279 // --> highway on-ramp
1280 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 1) {
1281 NBEdge* const out = myOutgoingEdges[0];
1282 NBEdge* in1 = myIncomingEdges[0];
1283 NBEdge* in2 = myIncomingEdges[1];
1284 const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1285 int in1Offset = MAX2(0, in1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1286 int in2Offset = MAX2(0, in2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1287 if (in1->getNumLanes() + in2->getNumLanes() - in1Offset - in2Offset == out->getNumLanes() - outOffset
1290 && in1 != out
1291 && in2 != out
1292 && in1->isConnectedTo(out)
1293 && in2->isConnectedTo(out)
1294 && in1->getSpecialLane(SVC_BICYCLE) == -1
1295 && in2->getSpecialLane(SVC_BICYCLE) == -1
1296 && out->getSpecialLane(SVC_BICYCLE) == -1
1297 && in1->getSpecialLane(SVC_TRAM) == -1
1298 && in2->getSpecialLane(SVC_TRAM) == -1
1299 && out->getSpecialLane(SVC_TRAM) == -1
1300 && isLongEnough(out, MIN_WEAVE_LENGTH)) {
1301#ifdef DEBUG_CONNECTION_GUESSING
1302 if (DEBUGCOND) {
1303 std::cout << "l2l node=" << getID() << " specialCase b\n";
1304 }
1305#endif
1306 // for internal: check which one is the rightmost
1307 double a1 = in1->getAngleAtNode(this);
1308 double a2 = in2->getAngleAtNode(this);
1309 double ccw = GeomHelper::getCCWAngleDiff(a1, a2);
1310 double cw = GeomHelper::getCWAngleDiff(a1, a2);
1311 if (ccw > cw) {
1312 std::swap(in1, in2);
1313 std::swap(in1Offset, in2Offset);
1314 }
1315 in1->addLane2LaneConnections(in1Offset, out, outOffset, in1->getNumLanes() - in1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1316 in2->addLane2LaneConnections(in2Offset, out, in1->getNumLanes() + outOffset - in1Offset, in2->getNumLanes() - in2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1317 if (out->getSpecialLane(SVC_BICYCLE) >= 0) {
1319 }
1320 return;
1321 }
1322 }
1323 // special case c):
1324 // one in, two out, the incoming has the same number of lanes or only 1 lane less than the sum of the outgoing lanes
1325 // --> highway off-ramp
1326 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 2) {
1327 NBEdge* in = myIncomingEdges[0];
1328 NBEdge* out1 = myOutgoingEdges[0];
1329 NBEdge* out2 = myOutgoingEdges[1];
1330 const int inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1331 int out1Offset = MAX2(0, out1->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1332 int out2Offset = MAX2(0, out2->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1333 const int deltaLaneSum = (out2->getNumLanes() + out1->getNumLanes() - out1Offset - out2Offset) - (in->getNumLanes() - inOffset);
1334 if ((deltaLaneSum == 0 || (deltaLaneSum == 1 && in->getPermissionVariants(inOffset, in->getNumLanes()).size() == 1))
1336 && in != out1
1337 && in != out2
1338 && in->isConnectedTo(out1)
1339 && in->isConnectedTo(out2)
1340 && !in->isTurningDirectionAt(out1)
1341 && !in->isTurningDirectionAt(out2)
1342 ) {
1343#ifdef DEBUG_CONNECTION_GUESSING
1344 if (DEBUGCOND) {
1345 std::cout << "l2l node=" << getID() << " specialCase c\n";
1346 }
1347#endif
1348 // for internal: check which one is the rightmost
1349 if (NBContHelper::relative_outgoing_edge_sorter(in)(out2, out1)) {
1350 std::swap(out1, out2);
1351 std::swap(out1Offset, out2Offset);
1352 }
1353 in->addLane2LaneConnections(inOffset, out1, out1Offset, out1->getNumLanes() - out1Offset, NBEdge::Lane2LaneInfoType::COMPUTED, true);
1354 in->addLane2LaneConnections(out1->getNumLanes() + inOffset - out1Offset - deltaLaneSum, out2, out2Offset, out2->getNumLanes() - out2Offset, NBEdge::Lane2LaneInfoType::COMPUTED, false);
1355 if (in->getSpecialLane(SVC_BICYCLE) >= 0) {
1358 }
1359 return;
1360 }
1361 }
1362 // special case d):
1363 // one in, one out, the outgoing has one lane less and node has type 'zipper'
1364 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1 && myType == SumoXMLNodeType::ZIPPER) {
1365 NBEdge* in = myIncomingEdges[0];
1366 NBEdge* out = myOutgoingEdges[0];
1367 // check if it's not the turnaround
1368 if (in->getTurnDestination() == out) {
1369 // will be added later or not...
1370 return;
1371 }
1372#ifdef DEBUG_CONNECTION_GUESSING
1373 if (DEBUGCOND) {
1374 std::cout << "l2l node=" << getID() << " specialCase d\n";
1375 }
1376#endif
1377 const int inOffset = MAX2(0, in->getFirstNonPedestrianLaneIndex(FORWARD, true));
1378 const int outOffset = MAX2(0, out->getFirstNonPedestrianLaneIndex(FORWARD, true));
1380 && in->getNumLanes() - inOffset == out->getNumLanes() - outOffset + 1
1381 && in != out
1382 && in->isConnectedTo(out)) {
1383 for (int i = inOffset; i < in->getNumLanes(); ++i) {
1384 in->setConnection(i, out, MIN2(outOffset + i, out->getNumLanes() - 1), NBEdge::Lane2LaneInfoType::COMPUTED, true);
1385 }
1386 return;
1387 }
1388 }
1389 // special case f):
1390 // one in, one out, out has reduced or same number of lanes
1391 if (myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1) {
1392 NBEdge* in = myIncomingEdges[0];
1393 NBEdge* out = myOutgoingEdges[0];
1394 // check if it's not the turnaround
1395 if (in->getTurnDestination() == out) {
1396 // will be added later or not...
1397 return;
1398 }
1399 int inOffset, inEnd, outOffset, outEnd, reduction;
1400 getReduction(in, out, inOffset, inEnd, outOffset, outEnd, reduction);
1402 && reduction >= 0
1403 && in != out
1404 && in->isConnectedTo(out)) {
1405#ifdef DEBUG_CONNECTION_GUESSING
1406 if (DEBUGCOND) {
1407 std::cout << "l2l node=" << getID() << " specialCase f inOff=" << inOffset << " outOff=" << outOffset << " inEnd=" << inEnd << " outEnd=" << outEnd << " reduction=" << reduction << "\n";
1408 }
1409#endif
1410 // in case of reduced lane number, let the rightmost lanes end
1411 inOffset += reduction;
1412 for (int i = outOffset; i < outEnd; ++i) {
1413 in->setConnection(i + inOffset - outOffset, out, i, NBEdge::Lane2LaneInfoType::COMPUTED);
1414 }
1415 //std::cout << " special case f at node=" << getID() << " inOffset=" << inOffset << " outOffset=" << outOffset << "\n";
1417 return;
1418 }
1419 }
1420
1421 // go through this node's outgoing edges
1422 // for every outgoing edge, compute the distribution of the node's
1423 // incoming edges on this edge when approaching this edge
1424 // the incoming edges' steps will then also be marked as LANE2LANE_RECHECK...
1425 EdgeVector approaching;
1426 for (NBEdge* currentOutgoing : myOutgoingEdges) {
1427 // get the information about edges that do approach this edge
1428 getEdgesThatApproach(currentOutgoing, approaching);
1429 const int numApproaching = (int)approaching.size();
1430 if (numApproaching != 0) {
1431 ApproachingDivider divider(approaching, currentOutgoing);
1432 Bresenham::compute(&divider, numApproaching, divider.numAvailableLanes());
1433 }
1434#ifdef DEBUG_CONNECTION_GUESSING
1435 if (DEBUGCOND) {
1436 std::cout << "l2l node=" << getID() << " outgoing=" << currentOutgoing->getID() << " bresenham:\n";
1437 for (NBEdge* e : myIncomingEdges) {
1438 const std::vector<NBEdge::Connection>& elv = e->getConnections();
1439 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1440 std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1441 }
1442 }
1443 }
1444#endif
1445 recheckVClassConnections(currentOutgoing);
1446
1447 // in case of lane change restrictions on the outgoing edge, ensure that
1448 // all its lanes can be reached from each connected incoming edge
1449 bool targetProhibitsChange = false;
1450 for (int i = 0; i < currentOutgoing->getNumLanes(); i++) {
1451 const NBEdge::Lane& lane = currentOutgoing->getLanes()[i];
1452 if ((lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && i + 1 < currentOutgoing->getNumLanes())
1453 || (lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && i > 0)) {
1454 targetProhibitsChange = true;
1455 break;
1456 }
1457 }
1458 if (targetProhibitsChange) {
1459 //std::cout << " node=" << getID() << " outgoing=" << currentOutgoing->getID() << " targetProhibitsChange\n";
1460 for (NBEdge* incoming : myIncomingEdges) {
1461 if (incoming->getStep() < NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1462 std::map<int, int> outToIn;
1463 for (const NBEdge::Connection& c : incoming->getConnections()) {
1464 if (c.toEdge == currentOutgoing) {
1465 outToIn[c.toLane] = c.fromLane;
1466 }
1467 }
1468 for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); toLane++) {
1469 if (outToIn.count(toLane) == 0) {
1470 bool added = false;
1471 // find incoming lane for neighboring outgoing
1472 for (int i = 0; i < toLane; i++) {
1473 if (outToIn.count(i) != 0) {
1474#ifdef DEBUG_CONNECTION_GUESSING
1475 if (DEBUGCOND) {
1476 std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, secondTarget)\n";
1477 }
1478#endif
1479 incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1480 added = true;
1481 break;
1482 }
1483 }
1484 if (!added) {
1485 for (int i = toLane; i < currentOutgoing->getNumLanes(); i++) {
1486 if (outToIn.count(i) != 0) {
1487#ifdef DEBUG_CONNECTION_GUESSING
1488 if (DEBUGCOND) {
1489 std::cout << "l2l node=" << getID() << " from=" << incoming->getID() << " to " << currentOutgoing->getLaneID(toLane) << " (changeProhibited, newTarget)\n";
1490 }
1491#endif
1492 incoming->setConnection(outToIn[i], currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED);
1493 added = true;
1494 break;
1495 }
1496 }
1497 }
1498 }
1499 }
1500 }
1501 }
1502 }
1503 }
1504 // special case e): rail_crossing
1505 // there should only be straight connections here
1507 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
1508 const std::vector<NBEdge::Connection> cons = (*i)->getConnections();
1509 for (std::vector<NBEdge::Connection>::const_iterator k = cons.begin(); k != cons.end(); ++k) {
1510 if (getDirection(*i, (*k).toEdge) == LinkDirection::TURN) {
1511 (*i)->removeFromConnections((*k).toEdge);
1512 }
1513 }
1514 }
1515 }
1516
1517 // ... but we may have the case that there are no outgoing edges
1518 // In this case, we have to mark the incoming edges as being in state
1519 // LANE2LANE( not RECHECK) by hand
1520 if (myOutgoingEdges.size() == 0) {
1521 for (NBEdge* incoming : myIncomingEdges) {
1522 incoming->markAsInLane2LaneState();
1523 }
1524 }
1525
1526#ifdef DEBUG_CONNECTION_GUESSING
1527 if (DEBUGCOND) {
1528 std::cout << "final connections at " << getID() << "\n";
1529 for (NBEdge* e : myIncomingEdges) {
1530 const std::vector<NBEdge::Connection>& elv = e->getConnections();
1531 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1532 std::cout << " " << e->getID() << "_" << (*k).fromLane << " -> " << Named::getIDSecure((*k).toEdge) << "_" << (*k).toLane << "\n";
1533 }
1534 }
1535 }
1536#endif
1537}
1538
1539void
1541 // ensure that all modes have a connection if possible
1542 for (NBEdge* incoming : myIncomingEdges) {
1543 if (incoming->getConnectionLanes(currentOutgoing).size() > 0 && incoming->getStep() <= NBEdge::EdgeBuildingStep::LANES2LANES_DONE) {
1544 // no connections are needed for pedestrians during this step
1545 // no satisfaction is possible if the outgoing edge disallows
1546 SVCPermissions unsatisfied = incoming->getPermissions() & currentOutgoing->getPermissions() & ~SVC_PEDESTRIAN;
1547 //std::cout << "initial unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1548 const std::vector<NBEdge::Connection>& elv = incoming->getConnections();
1549 for (std::vector<NBEdge::Connection>::const_iterator k = elv.begin(); k != elv.end(); ++k) {
1550 const NBEdge::Connection& c = *k;
1551 if (c.toEdge == currentOutgoing && c.toLane >= 0) {
1552 const SVCPermissions satisfied = (incoming->getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane));
1553 //std::cout << " from=" << incoming->getID() << "_" << c.fromLane << " to=" << c.toEdge->getID() << "_" << c.toLane << " satisfied=" << getVehicleClassNames(satisfied) << "\n";
1554 unsatisfied &= ~satisfied;
1555 }
1556 }
1557 if (unsatisfied != 0) {
1558#ifdef DEBUG_CONNECTION_GUESSING
1559 if (DEBUGCOND) {
1560 std::cout << " unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1561 }
1562#endif
1563 int fromLane = 0;
1564 // first attempt: try to use a dedicated fromLane
1565 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1566 if (incoming->getPermissions(fromLane) == unsatisfied) {
1567 unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1568 }
1569 fromLane++;
1570 }
1571 // second attempt: try to re-use a fromLane that already connects to currentOutgoing
1572 // (because we don't wont to create extra turn lanes)
1573 fromLane = 0;
1574 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1575 if ((incoming->getPermissions(fromLane) & unsatisfied) != 0
1576 && incoming->getConnectionsFromLane(fromLane, currentOutgoing, -1).size() > 0) {
1577 unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1578 }
1579 fromLane++;
1580 }
1581 // third attempt: use any possible fromLane
1582 fromLane = 0;
1583 while (unsatisfied != 0 && fromLane < incoming->getNumLanes()) {
1584 if ((incoming->getPermissions(fromLane) & unsatisfied) != 0) {
1585 unsatisfied = findToLaneForPermissions(currentOutgoing, fromLane, incoming, unsatisfied);
1586 }
1587 fromLane++;
1588 }
1589#ifdef DEBUG_CONNECTION_GUESSING
1590 if (DEBUGCOND) {
1591 if (unsatisfied != 0) {
1592 std::cout << " still unsatisfied modes from edge=" << incoming->getID() << " toEdge=" << currentOutgoing->getID() << " deadModes=" << getVehicleClassNames(unsatisfied) << "\n";
1593 }
1594 }
1595#endif
1596 }
1597 }
1598 // prevent dead-end bus and bicycle lanes (they were excluded by the ApproachingDivider)
1599 // and the bus/bicycle class might already be satisfied by other lanes
1600 recheckSpecialConnections(incoming, currentOutgoing, SVC_BUS);
1601 recheckSpecialConnections(incoming, currentOutgoing, SVC_BICYCLE);
1602 }
1603}
1604
1605
1606void
1607NBNode::recheckSpecialConnections(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial) {
1608 // assume that left-turns and turn-arounds are better satisfied from lanes to the left
1609 const int specialTarget = currentOutgoing->getSpecialLane(svcSpecial);
1610 const LinkDirection dir = getDirection(incoming, currentOutgoing);
1612 && ((specialTarget >= 0 && dir != LinkDirection::TURN)
1614 bool builtConnection = false;
1615 for (int i = 0; i < (int)incoming->getNumLanes(); i++) {
1616 if (incoming->getPermissions(i) == svcSpecial
1617 && incoming->getConnectionsFromLane(i, currentOutgoing).size() == 0) {
1618 // find a dedicated bike lane as target
1619 if (specialTarget >= 0) {
1620 incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1621#ifdef DEBUG_CONNECTION_GUESSING
1622 if (DEBUGCOND) {
1623 std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (dedicated) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1624 }
1625#endif
1626 builtConnection = true;
1627 } else {
1628 // do not create turns that create a conflict with neighboring lanes
1629 if (avoidConfict(incoming, currentOutgoing, svcSpecial, dir, i)) {
1630 continue;
1631 }
1632 // use any lane that allows the special class
1633 for (int i2 = 0; i2 < (int)currentOutgoing->getNumLanes(); i2++) {
1634 if ((currentOutgoing->getPermissions(i2) & svcSpecial) != 0) {
1635 // possibly a double-connection
1636 const bool allowDouble = (incoming->getPermissions(i) == svcSpecial
1638 incoming->setConnection(i, currentOutgoing, i2, NBEdge::Lane2LaneInfoType::COMPUTED, allowDouble);
1639#ifdef DEBUG_CONNECTION_GUESSING
1640 if (DEBUGCOND) {
1641 std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " to=" << currentOutgoing->getLaneID(i2) << "\n";
1642 }
1643#endif
1644 builtConnection = true;
1645 break;
1646 }
1647 }
1648 }
1649 }
1650 }
1651 if (!builtConnection && specialTarget >= 0
1652 && incoming->getConnectionsFromLane(-1, currentOutgoing, specialTarget).size() == 0) {
1653 // find origin lane that allows bicycles
1654 int start = 0;
1655 int end = incoming->getNumLanes();
1656 int inc = 1;
1657 if (dir == LinkDirection::TURN || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) {
1658 std::swap(start, end);
1659 inc = -1;
1660 }
1661 for (int i = start; i < end; i += inc) {
1662 if ((incoming->getPermissions(i) & svcSpecial) != 0) {
1663 incoming->setConnection(i, currentOutgoing, specialTarget, NBEdge::Lane2LaneInfoType::COMPUTED);
1664#ifdef DEBUG_CONNECTION_GUESSING
1665 if (DEBUGCOND) {
1666 std::cout << " extra " << getVehicleClassNames(svcSpecial) << " connection from=" << incoming->getLaneID(i) << " (final) to=" << currentOutgoing->getLaneID(specialTarget) << "\n";
1667 }
1668#endif
1669 break;
1670 }
1671 }
1672 }
1673 }
1674}
1675
1676
1677bool
1678NBNode::avoidConfict(NBEdge* incoming, NBEdge* currentOutgoing, SVCPermissions svcSpecial, LinkDirection dir, int i) {
1679 for (const auto& c : incoming->getConnections()) {
1680 if (incoming->getPermissions(c.fromLane) == svcSpecial && c.toEdge == currentOutgoing) {
1681 return true;
1682 }
1683 }
1684 if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1685 for (const auto& c : incoming->getConnections()) {
1686 if (c.fromLane < i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1687 return true;
1688 }
1689 }
1690 } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1691 for (const auto& c : incoming->getConnections()) {
1692 if (c.fromLane > i && (c.toEdge != currentOutgoing || incoming->getPermissions(c.fromLane) == svcSpecial)) {
1693 return true;
1694 }
1695 }
1696 } else if (svcSpecial != SVC_BICYCLE && dir == LinkDirection::STRAIGHT) {
1697 for (const auto& c : incoming->getConnections()) {
1698 const LinkDirection dir2 = getDirection(incoming, c.toEdge);
1699 if (c.fromLane < i && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT)) {
1700 return true;
1701 } else if (c.fromLane > i && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)) {
1702 return true;
1703 }
1704 }
1705 }
1706 return false;
1707}
1708
1709
1710void
1711NBNode::getReduction(const NBEdge* in, const NBEdge* out, int& inOffset, int& inEnd, int& outOffset, int& outEnd, int& reduction) const {
1712 inOffset = MAX2(0, in->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1713 outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1714 inEnd = in->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1715 outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1716 reduction = (inEnd - inOffset) - (outEnd - outOffset);
1717}
1718
1719
1721NBNode::findToLaneForPermissions(NBEdge* currentOutgoing, int fromLane, NBEdge* incoming, SVCPermissions unsatisfied) {
1722 for (int toLane = 0; toLane < currentOutgoing->getNumLanes(); ++toLane) {
1723 const SVCPermissions satisfied = incoming->getPermissions(fromLane) & currentOutgoing->getPermissions(toLane) & unsatisfied;
1724 if (satisfied != 0 && !incoming->getLaneStruct(fromLane).connectionsDone) {
1725 if (incoming->hasConnectionTo(currentOutgoing, toLane)
1726 && unsatisfied == SVC_TRAM
1727 && incoming->getPermissions(fromLane) == currentOutgoing->getPermissions(toLane)) {
1728 // avoid double tram connection by shifting an existing connection
1729 for (auto con : incoming->getConnections()) {
1730 if (con.toEdge == currentOutgoing && con.toLane == toLane) {
1731#ifdef DEBUG_CONNECTION_GUESSING
1732 if (DEBUGCOND) {
1733 std::cout << " shifting connection from=" << con.fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << ": newFromLane=" << fromLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1734 }
1735#endif
1736 incoming->getConnectionRef(con.fromLane, con.toEdge, toLane).fromLane = fromLane;
1737 unsatisfied &= ~satisfied;
1738 break;
1739 }
1740 }
1741 } else {
1742 // other modes (i.e. bus) can fix lane permissions NBPTLineCont::fixPermissions but do not wish to create parallel tram tracks here
1743 bool mayUseSameDestination = unsatisfied == SVC_TRAM || (unsatisfied & SVC_PASSENGER) != 0;
1744 incoming->setConnection((int)fromLane, currentOutgoing, toLane, NBEdge::Lane2LaneInfoType::COMPUTED, mayUseSameDestination);
1745#ifdef DEBUG_CONNECTION_GUESSING
1746 if (DEBUGCOND) {
1747 std::cout << " new connection from=" << fromLane << " to=" << currentOutgoing->getID() << "_" << toLane << " satisfies=" << getVehicleClassNames(satisfied) << "\n";
1748 }
1749#endif
1750 unsatisfied &= ~satisfied;
1751 }
1752 }
1753 }
1754 return unsatisfied;
1755}
1756
1757
1758int
1759NBNode::addedLanesRight(NBEdge* out, int addedLanes) const {
1760 if (out->isOffRamp()) {
1761 return addedLanes;
1762 }
1763 NBNode* to = out->getToNode();
1764 // check whether a right lane ends
1765 if (to->getIncomingEdges().size() == 1
1766 && to->getOutgoingEdges().size() == 1) {
1767 int inOffset, inEnd, outOffset, outEnd, reduction;
1768 to->getReduction(out, to->getOutgoingEdges()[0], inOffset, inEnd, outOffset, outEnd, reduction);
1769
1770 if (reduction > 0) {
1771 return reduction;
1772 }
1773 }
1774 // check for the presence of right and left turns at the next intersection
1775 int outLanesRight = 0;
1776 int outLanesLeft = 0;
1777 int outLanesStraight = 0;
1778 for (NBEdge* succ : to->getOutgoingEdges()) {
1779 if (out->isConnectedTo(succ)) {
1780 const int outOffset = MAX2(0, succ->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1781 const int usableLanes = succ->getNumLanes() - outOffset;
1782 LinkDirection dir = to->getDirection(out, succ);
1783 if (dir == LinkDirection::STRAIGHT) {
1784 outLanesStraight += usableLanes;
1785 } else if (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT) {
1786 outLanesRight += usableLanes;
1787 } else {
1788 outLanesLeft += usableLanes;
1789 }
1790 }
1791 }
1792 const int outOffset = MAX2(0, out->getFirstNonPedestrianNonBicycleLaneIndex(FORWARD, true));
1793 const int outEnd = out->getFirstNonPedestrianLaneIndex(BACKWARD, true) + 1;
1794 const int usableLanes = outEnd - outOffset;
1795 int addedTurnLanes = MIN3(
1796 addedLanes,
1797 MAX2(0, usableLanes - outLanesStraight),
1798 outLanesRight + outLanesLeft);
1799#ifdef DEBUG_CONNECTION_GUESSING
1800 if (DEBUGCOND) {
1801 std::cout << "out=" << out->getID() << " usableLanes=" << usableLanes << " addedTurnLanes=" << addedTurnLanes << " addedLanes=" << addedLanes << " outLanesStraight=" << outLanesStraight << " outLanesLeft=" << outLanesLeft << " outLanesRight=" << outLanesRight << "\n";
1802 }
1803#endif
1804 if (outLanesLeft == 0) {
1805 return addedTurnLanes;
1806 } else {
1807 return MIN2(addedTurnLanes / 2, outLanesRight);
1808 }
1809}
1810
1811
1812bool
1813NBNode::isLongEnough(NBEdge* out, double minLength) {
1814 double seen = out->getLoadedLength();
1815 while (seen < minLength) {
1816 // advance along trivial continuations
1817 if (out->getToNode()->getOutgoingEdges().size() != 1
1818 || out->getToNode()->getIncomingEdges().size() != 1) {
1819 return false;
1820 } else {
1821 out = out->getToNode()->getOutgoingEdges()[0];
1822 seen += out->getLoadedLength();
1823 }
1824 }
1825 return true;
1826}
1827
1828
1829void
1830NBNode::getEdgesThatApproach(NBEdge* currentOutgoing, EdgeVector& approaching) {
1831 // get the position of the node to get the approaching nodes of
1832 EdgeVector::const_iterator i = std::find(myAllEdges.begin(),
1833 myAllEdges.end(), currentOutgoing);
1834 // get the first possible approaching edge
1836 // go through the list of edges clockwise and add the edges
1837 approaching.clear();
1838 for (; *i != currentOutgoing;) {
1839 // check only incoming edges
1840 if ((*i)->getToNode() == this && (*i)->getTurnDestination() != currentOutgoing) {
1841 std::vector<int> connLanes = (*i)->getConnectionLanes(currentOutgoing);
1842 if (connLanes.size() != 0) {
1843 approaching.push_back(*i);
1844 }
1845 }
1847 }
1848}
1849
1850
1851void
1852NBNode::replaceOutgoing(NBEdge* which, NBEdge* by, int laneOff) {
1853 // replace the edge in the list of outgoing nodes
1854 EdgeVector::iterator i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), which);
1855 if (i != myOutgoingEdges.end()) {
1856 (*i) = by;
1857 i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1858 (*i) = by;
1859 }
1860 // replace the edge in connections of incoming edges
1861 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); ++i) {
1862 (*i)->replaceInConnections(which, by, laneOff);
1863 }
1864 // replace within the connetion prohibition dependencies
1865 replaceInConnectionProhibitions(which, by, 0, laneOff);
1866}
1867
1868
1869void
1871 // replace edges
1872 int laneOff = 0;
1873 for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1874 replaceOutgoing(*i, by, laneOff);
1875 laneOff += (*i)->getNumLanes();
1876 }
1877 // removed double occurrences
1879 // check whether this node belongs to a district and the edges
1880 // must here be also remapped
1881 if (myDistrict != nullptr) {
1882 myDistrict->replaceOutgoing(which, by);
1883 }
1884}
1885
1886
1887void
1888NBNode::replaceIncoming(NBEdge* which, NBEdge* by, int laneOff) {
1889 // replace the edge in the list of incoming nodes
1890 EdgeVector::iterator i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), which);
1891 if (i != myIncomingEdges.end()) {
1892 (*i) = by;
1893 i = std::find(myAllEdges.begin(), myAllEdges.end(), which);
1894 (*i) = by;
1895 }
1896 // replace within the connetion prohibition dependencies
1897 replaceInConnectionProhibitions(which, by, laneOff, 0);
1898}
1899
1900
1901void
1903 // replace edges
1904 int laneOff = 0;
1905 for (EdgeVector::const_iterator i = which.begin(); i != which.end(); i++) {
1906 replaceIncoming(*i, by, laneOff);
1907 laneOff += (*i)->getNumLanes();
1908 }
1909 // removed double occurrences
1911 // check whether this node belongs to a district and the edges
1912 // must here be also remapped
1913 if (myDistrict != nullptr) {
1914 myDistrict->replaceIncoming(which, by);
1915 }
1916}
1917
1918
1919
1920void
1922 int whichLaneOff, int byLaneOff) {
1923 // replace in keys
1924 NBConnectionProhibits::iterator j = myBlockedConnections.begin();
1925 while (j != myBlockedConnections.end()) {
1926 bool changed = false;
1927 NBConnection c = (*j).first;
1928 if (c.replaceFrom(which, whichLaneOff, by, byLaneOff)) {
1929 changed = true;
1930 }
1931 if (c.replaceTo(which, whichLaneOff, by, byLaneOff)) {
1932 changed = true;
1933 }
1934 if (changed) {
1935 myBlockedConnections[c] = (*j).second;
1936 myBlockedConnections.erase(j);
1937 j = myBlockedConnections.begin();
1938 } else {
1939 j++;
1940 }
1941 }
1942 // replace in values
1943 for (j = myBlockedConnections.begin(); j != myBlockedConnections.end(); j++) {
1944 NBConnectionVector& prohibiting = (*j).second;
1945 for (NBConnectionVector::iterator k = prohibiting.begin(); k != prohibiting.end(); k++) {
1946 NBConnection& sprohibiting = *k;
1947 sprohibiting.replaceFrom(which, whichLaneOff, by, byLaneOff);
1948 sprohibiting.replaceTo(which, whichLaneOff, by, byLaneOff);
1949 }
1950 }
1951}
1952
1953
1954
1955void
1957 // check incoming
1958 for (int i = 0; myIncomingEdges.size() > 0 && i < (int)myIncomingEdges.size() - 1; i++) {
1959 int j = i + 1;
1960 while (j < (int)myIncomingEdges.size()) {
1961 if (myIncomingEdges[i] == myIncomingEdges[j]) {
1962 myIncomingEdges.erase(myIncomingEdges.begin() + j);
1963 } else {
1964 j++;
1965 }
1966 }
1967 }
1968 // check outgoing
1969 for (int i = 0; myOutgoingEdges.size() > 0 && i < (int)myOutgoingEdges.size() - 1; i++) {
1970 int j = i + 1;
1971 while (j < (int)myOutgoingEdges.size()) {
1972 if (myOutgoingEdges[i] == myOutgoingEdges[j]) {
1973 myOutgoingEdges.erase(myOutgoingEdges.begin() + j);
1974 } else {
1975 j++;
1976 }
1977 }
1978 }
1979 // check all
1980 for (int i = 0; myAllEdges.size() > 0 && i < (int)myAllEdges.size() - 1; i++) {
1981 int j = i + 1;
1982 while (j < (int)myAllEdges.size()) {
1983 if (myAllEdges[i] == myAllEdges[j]) {
1984 myAllEdges.erase(myAllEdges.begin() + j);
1985 } else {
1986 j++;
1987 }
1988 }
1989 }
1990}
1991
1992
1993bool
1994NBNode::hasIncoming(const NBEdge* const e) const {
1995 return std::find(myIncomingEdges.begin(), myIncomingEdges.end(), e) != myIncomingEdges.end();
1996}
1997
1998
1999bool
2000NBNode::hasOutgoing(const NBEdge* const e) const {
2001 return std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), e) != myOutgoingEdges.end();
2002}
2003
2004
2005NBEdge*
2008 if (find(edges.begin(), edges.end(), e) != edges.end()) {
2009 edges.erase(find(edges.begin(), edges.end(), e));
2010 }
2011 if (edges.size() == 0) {
2012 return nullptr;
2013 }
2014 if (e->getToNode() == this) {
2015 sort(edges.begin(), edges.end(), NBContHelper::edge_opposite_direction_sorter(e, this, false));
2016 } else {
2017 sort(edges.begin(), edges.end(), NBContHelper::edge_similar_direction_sorter(e));
2018 }
2019 return edges[0];
2020}
2021
2022
2023void
2025 const NBConnection& mustStop) {
2026 if (mayDrive.getFrom() == nullptr ||
2027 mayDrive.getTo() == nullptr ||
2028 mustStop.getFrom() == nullptr ||
2029 mustStop.getTo() == nullptr) {
2030
2031 WRITE_WARNING(TL("Something went wrong during the building of a connection..."));
2032 return; // !!! mark to recompute connections
2033 }
2035 conn.push_back(mayDrive);
2036 myBlockedConnections[mustStop] = conn;
2037}
2038
2039
2040NBEdge*
2041NBNode::getPossiblySplittedIncoming(const std::string& edgeid) {
2042 int size = (int) edgeid.length();
2043 for (EdgeVector::iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2044 std::string id = (*i)->getID();
2045 if (id.substr(0, size) == edgeid) {
2046 return *i;
2047 }
2048 }
2049 return nullptr;
2050}
2051
2052
2053NBEdge*
2054NBNode::getPossiblySplittedOutgoing(const std::string& edgeid) {
2055 int size = (int) edgeid.length();
2056 for (EdgeVector::iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2057 std::string id = (*i)->getID();
2058 if (id.substr(0, size) == edgeid) {
2059 return *i;
2060 }
2061 }
2062 return nullptr;
2063}
2064
2065
2066void
2067NBNode::removeEdge(NBEdge* edge, bool removeFromConnections) {
2068 EdgeVector::iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), edge);
2069 if (i != myAllEdges.end()) {
2070 myAllEdges.erase(i);
2071 i = std::find(myOutgoingEdges.begin(), myOutgoingEdges.end(), edge);
2072 if (i != myOutgoingEdges.end()) {
2073 myOutgoingEdges.erase(i);
2074 // potential self-loop
2075 i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2076 if (i != myIncomingEdges.end()) {
2077 myIncomingEdges.erase(i);
2078 }
2079 } else {
2080 i = std::find(myIncomingEdges.begin(), myIncomingEdges.end(), edge);
2081 if (i != myIncomingEdges.end()) {
2082 myIncomingEdges.erase(i);
2083 } else {
2084 // edge must have been either incoming or outgoing
2085 assert(false);
2086 }
2087 }
2088 if (removeFromConnections) {
2089 for (i = myAllEdges.begin(); i != myAllEdges.end(); ++i) {
2090 (*i)->removeFromConnections(edge);
2091 }
2092 }
2093 // invalidate controlled connections for loaded traffic light plans
2094 const bool incoming = edge->getToNode() == this;
2095 for (NBTrafficLightDefinition* const tld : myTrafficLights) {
2096 tld->replaceRemoved(edge, -1, nullptr, -1, incoming);
2097 }
2098 }
2099}
2100
2101
2104 Position pos(0, 0);
2105 for (const NBEdge* const in : myIncomingEdges) {
2106 Position toAdd = in->getFromNode()->getPosition();
2107 toAdd.sub(myPosition);
2108 toAdd.norm2D();
2109 pos.add(toAdd);
2110 }
2111 for (const NBEdge* const out : myOutgoingEdges) {
2112 Position toAdd = out->getToNode()->getPosition();
2113 toAdd.sub(myPosition);
2114 toAdd.norm2D();
2115 pos.add(toAdd);
2116 }
2117 pos.mul(-1. / (double)(myIncomingEdges.size() + myOutgoingEdges.size()));
2118 if (pos.x() == 0. && pos.y() == 0.) {
2119 pos = Position(1, 0);
2120 }
2121 pos.norm2D();
2122 return pos;
2123}
2124
2125
2126
2127void
2129 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2130 (*i)->invalidateConnections(reallowSetting);
2131 }
2132}
2133
2134
2135void
2137 for (EdgeVector::const_iterator i = myOutgoingEdges.begin(); i != myOutgoingEdges.end(); i++) {
2138 (*i)->invalidateConnections(reallowSetting);
2139 }
2140}
2141
2142
2143bool
2144NBNode::mustBrake(const NBEdge* const from, const NBEdge* const to, int fromLane, int toLane, bool includePedCrossings) const {
2145 // unregulated->does not need to brake
2146 if (myRequest == nullptr) {
2147 return false;
2148 }
2149 // vehicles which do not have a following lane must always decelerate to the end
2150 if (to == nullptr) {
2151 return true;
2152 }
2153 // maybe we need to brake due to entering a bidi-edge
2154 if (to->isBidiEdge() && !from->isBidiEdge()) {
2155 return true;
2156 }
2157 // check whether any other connection on this node prohibits this connection
2158 return myRequest->mustBrake(from, to, fromLane, toLane, includePedCrossings);
2159}
2160
2161bool
2162NBNode::mustBrakeForCrossing(const NBEdge* const from, const NBEdge* const to, const NBNode::Crossing& crossing) const {
2163 return NBRequest::mustBrakeForCrossing(this, from, to, crossing);
2164}
2165
2166bool
2167NBNode::brakeForCrossingOnExit(const NBEdge* to, LinkDirection dir, bool indirect) const {
2168 // code is called for connections exiting after an internal junction.
2169 // If the connection is turning we do not check for crossing priority anymore.
2170 if (dir == LinkDirection::STRAIGHT && !indirect) {
2171 return false;
2172 }
2173 for (auto& c : myCrossings) {
2174 if (std::find(c->edges.begin(), c->edges.end(), to) != c->edges.end()) {
2175 return true;
2176 }
2177 }
2178 return false;
2179}
2180
2181
2182bool
2183NBNode::rightTurnConflict(const NBEdge* from, const NBEdge* to, int fromLane,
2184 const NBEdge* prohibitorFrom, const NBEdge* prohibitorTo, int prohibitorFromLane) {
2185 if (from != prohibitorFrom) {
2186 return false;
2187 }
2188 if (from->isTurningDirectionAt(to)
2189 || prohibitorFrom->isTurningDirectionAt(prohibitorTo)) {
2190 // XXX should warn if there are any non-turning connections left of this
2191 return false;
2192 }
2193 // conflict if to is between prohibitorTo and from when going clockwise
2194 if (to->getStartAngle() == prohibitorTo->getStartAngle()) {
2195 // reduce rounding errors
2196 return false;
2197 }
2198 const LinkDirection d1 = from->getToNode()->getDirection(from, to);
2199 // must be a right turn to qualify as rightTurnConflict
2200 if (d1 == LinkDirection::STRAIGHT) {
2201 // no conflict for straight going connections
2202 // XXX actually this should check the main direction (which could also
2203 // be a turn)
2204 return false;
2205 } else {
2206 const LinkDirection d2 = prohibitorFrom->getToNode()->getDirection(prohibitorFrom, prohibitorTo);
2207 /* std::cout
2208 << "from=" << from->getID() << " to=" << to->getID() << " fromLane=" << fromLane
2209 << " pFrom=" << prohibitorFrom->getID() << " pTo=" << prohibitorTo->getID() << " pFromLane=" << prohibitorFromLane
2210 << " d1=" << toString(d1) << " d2=" << toString(d2)
2211 << "\n"; */
2212 bool flip = false;
2213 if (d1 == LinkDirection::LEFT || d1 == LinkDirection::PARTLEFT) {
2214 // check for leftTurnConflicht
2215 flip = !flip;
2217 // assume that the left-turning bicycle goes straight at first
2218 // and thus gets precedence over a right turning vehicle
2219 return false;
2220 }
2221 }
2222 if ((!flip && fromLane <= prohibitorFromLane) ||
2223 (flip && fromLane >= prohibitorFromLane)) {
2224 return false;
2225 }
2226 const double toAngleAtNode = fmod(to->getStartAngle() + 180, (double)360.0);
2227 const double prohibitorToAngleAtNode = fmod(prohibitorTo->getStartAngle() + 180, (double)360.0);
2228 return (flip != (GeomHelper::getCWAngleDiff(from->getEndAngle(), toAngleAtNode) <
2229 GeomHelper::getCWAngleDiff(from->getEndAngle(), prohibitorToAngleAtNode)));
2230 }
2231}
2232
2233bool
2234NBNode::mergeConflictYields(const NBEdge* from, int fromLane, int fromLaneFoe, NBEdge* to, int toLane) const {
2235 if (myRequest == nullptr) {
2236 return false;
2237 }
2238 const NBEdge::Connection& con = from->getConnection(fromLane, to, toLane);
2239 const NBEdge::Connection& prohibitorCon = from->getConnection(fromLaneFoe, to, toLane);
2240 return myRequest->mergeConflict(from, con, from, prohibitorCon, false);
2241}
2242
2243
2244bool
2246 const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2247 if (myRequest == nullptr) {
2248 return false;
2249 }
2250 return myRequest->mergeConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2251}
2252
2253bool
2255 const NBEdge* prohibitorFrom, const NBEdge::Connection& prohibitorCon, bool foes) const {
2256 if (myRequest == nullptr) {
2257 return false;
2258 }
2259 return myRequest->bidiConflict(from, con, prohibitorFrom, prohibitorCon, foes);
2260}
2261
2262bool
2263NBNode::turnFoes(const NBEdge* from, const NBEdge* to, int fromLane,
2264 const NBEdge* from2, const NBEdge* to2, int fromLane2,
2265 bool lefthand) const {
2266 UNUSED_PARAMETER(lefthand);
2267 if (from != from2 || to == to2 || fromLane == fromLane2) {
2268 return false;
2269 }
2270 if (from->isTurningDirectionAt(to)
2271 || from2->isTurningDirectionAt(to2)) {
2272 // XXX should warn if there are any non-turning connections left of this
2273 return false;
2274 }
2275 bool result = false;
2276 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2277 if (fromLane < fromLane2) {
2278 // conflict if 'to' comes before 'to2' going clockwise starting at 'from'
2279 while (*it != to2) {
2280 if (*it == to) {
2281 result = true;
2282 }
2284 }
2285 } else {
2286 // conflict if 'to' comes before 'to2' going counter-clockwise starting at 'from'
2287 while (*it != to2) {
2288 if (*it == to) {
2289 result = true;
2290 }
2292 }
2293 }
2294 /*
2295 if (result) {
2296 std::cout << "turnFoes node=" << getID()
2297 << " from=" << from->getLaneID(fromLane)
2298 << " to=" << to->getID()
2299 << " from2=" << from2->getLaneID(fromLane2)
2300 << " to2=" << to2->getID()
2301 << "\n";
2302 }
2303 */
2304 return result;
2305}
2306
2307
2308bool
2309NBNode::isLeftMover(const NBEdge* const from, const NBEdge* const to) const {
2310 // when the junction has only one incoming edge, there are no
2311 // problems caused by left blockings
2312 if (myIncomingEdges.size() == 1 || myOutgoingEdges.size() == 1) {
2313 return false;
2314 }
2315 double fromAngle = from->getAngleAtNode(this);
2316 double toAngle = to->getAngleAtNode(this);
2317 double cw = GeomHelper::getCWAngleDiff(fromAngle, toAngle);
2318 double ccw = GeomHelper::getCCWAngleDiff(fromAngle, toAngle);
2319 std::vector<NBEdge*>::const_iterator i = std::find(myAllEdges.begin(), myAllEdges.end(), from);
2320 do {
2322 } while ((!hasOutgoing(*i) || from->isTurningDirectionAt(*i)) && *i != from);
2323 return cw < ccw && (*i) == to && myOutgoingEdges.size() > 2;
2324}
2325
2326
2327bool
2328NBNode::forbids(const NBEdge* const possProhibitorFrom, const NBEdge* const possProhibitorTo,
2329 const NBEdge* const possProhibitedFrom, const NBEdge* const possProhibitedTo,
2330 bool regardNonSignalisedLowerPriority) const {
2331 return myRequest != nullptr && myRequest->forbids(possProhibitorFrom, possProhibitorTo,
2332 possProhibitedFrom, possProhibitedTo,
2333 regardNonSignalisedLowerPriority);
2334}
2335
2336
2337bool
2338NBNode::foes(const NBEdge* const from1, const NBEdge* const to1,
2339 const NBEdge* const from2, const NBEdge* const to2) const {
2340 return myRequest != nullptr && myRequest->foes(from1, to1, from2, to2);
2341}
2342
2343
2344void
2346 NBEdge* removed, const EdgeVector& incoming,
2347 const EdgeVector& outgoing) {
2348 assert(find(incoming.begin(), incoming.end(), removed) == incoming.end());
2349 bool changed = true;
2350 while (changed) {
2351 changed = false;
2352 NBConnectionProhibits blockedConnectionsTmp = myBlockedConnections;
2353 NBConnectionProhibits blockedConnectionsNew;
2354 // remap in connections
2355 for (NBConnectionProhibits::iterator i = blockedConnectionsTmp.begin(); i != blockedConnectionsTmp.end(); i++) {
2356 const NBConnection& blocker = (*i).first;
2357 const NBConnectionVector& blocked = (*i).second;
2358 // check the blocked connections first
2359 // check whether any of the blocked must be changed
2360 bool blockedChanged = false;
2361 NBConnectionVector newBlocked;
2362 NBConnectionVector::const_iterator j;
2363 for (j = blocked.begin(); j != blocked.end(); j++) {
2364 const NBConnection& sblocked = *j;
2365 if (sblocked.getFrom() == removed || sblocked.getTo() == removed) {
2366 blockedChanged = true;
2367 }
2368 }
2369 // adapt changes if so
2370 for (j = blocked.begin(); blockedChanged && j != blocked.end(); j++) {
2371 const NBConnection& sblocked = *j;
2372 if (sblocked.getFrom() == removed && sblocked.getTo() == removed) {
2373 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2374 !!! newBlocked.push_back(NBConnection(*k, *k));
2375 }*/
2376 } else if (sblocked.getFrom() == removed) {
2377 assert(sblocked.getTo() != removed);
2378 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2379 newBlocked.push_back(NBConnection(*k, sblocked.getTo()));
2380 }
2381 } else if (sblocked.getTo() == removed) {
2382 assert(sblocked.getFrom() != removed);
2383 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2384 newBlocked.push_back(NBConnection(sblocked.getFrom(), *k));
2385 }
2386 } else {
2387 newBlocked.push_back(NBConnection(sblocked.getFrom(), sblocked.getTo()));
2388 }
2389 }
2390 if (blockedChanged) {
2391 blockedConnectionsNew[blocker] = newBlocked;
2392 changed = true;
2393 }
2394 // if the blocked were kept
2395 else {
2396 if (blocker.getFrom() == removed && blocker.getTo() == removed) {
2397 changed = true;
2398 /* for(EdgeVector::const_iterator k=incoming.begin(); k!=incoming.end(); k++) {
2399 !!! blockedConnectionsNew[NBConnection(*k, *k)] = blocked;
2400 }*/
2401 } else if (blocker.getFrom() == removed) {
2402 assert(blocker.getTo() != removed);
2403 changed = true;
2404 for (EdgeVector::const_iterator k = incoming.begin(); k != incoming.end(); k++) {
2405 blockedConnectionsNew[NBConnection(*k, blocker.getTo())] = blocked;
2406 }
2407 } else if (blocker.getTo() == removed) {
2408 assert(blocker.getFrom() != removed);
2409 changed = true;
2410 for (EdgeVector::const_iterator k = outgoing.begin(); k != outgoing.end(); k++) {
2411 blockedConnectionsNew[NBConnection(blocker.getFrom(), *k)] = blocked;
2412 }
2413 } else {
2414 blockedConnectionsNew[blocker] = blocked;
2415 }
2416 }
2417 }
2418 myBlockedConnections = blockedConnectionsNew;
2419 }
2420 // remap in traffic lights
2421 tc.remapRemoved(removed, incoming, outgoing);
2422}
2423
2424
2425NBEdge*
2426NBNode::getNextCompatibleOutgoing(const NBEdge* incoming, SVCPermissions vehPerm, EdgeVector::const_iterator itOut, bool clockwise) const {
2427 EdgeVector::const_iterator i = itOut;
2428 while (*i != incoming) {
2429 if (clockwise) {
2431 } else {
2433 }
2434 if ((*i)->getFromNode() != this) {
2435 // only look for outgoing edges
2436 // @note we use myAllEdges to stop at the incoming edge
2437 continue;
2438 }
2439 if (incoming->isTurningDirectionAt(*i)) {
2440 return nullptr;
2441 }
2442 if ((vehPerm & (*i)->getPermissions()) != 0 || vehPerm == 0) {
2443 return *i;
2444 }
2445 }
2446 return nullptr;
2447}
2448
2449
2450bool
2451NBNode::isStraighter(const NBEdge* const incoming, const double angle, const SVCPermissions vehPerm, const int modeLanes, const NBEdge* const candidate) const {
2452 if (candidate != nullptr) {
2453 const double candAngle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), candidate->getAngleAtNode(this));
2454 // they are too similar it does not matter
2455 if (fabs(angle - candAngle) < 5.) {
2456 return false;
2457 }
2458 // the other edge is at least 5 degree straighter
2459 if (fabs(candAngle) < fabs(angle) - 5.) {
2460 return true;
2461 }
2462 if (fabs(angle) < fabs(candAngle) - 5.) {
2463 return false;
2464 }
2465 if (fabs(candAngle) < 44.) {
2466 // the lane count for the same modes is larger
2467 const int candModeLanes = candidate->getNumLanesThatAllow(vehPerm);
2468 if (candModeLanes > modeLanes) {
2469 return true;
2470 }
2471 if (candModeLanes < modeLanes) {
2472 return false;
2473 }
2474 // we would create a left turn
2475 if (candAngle < 0 && angle > 0) {
2476 return true;
2477 }
2478 if (angle < 0 && candAngle > 0) {
2479 return false;
2480 }
2481 }
2482 }
2483 return false;
2484}
2485
2487NBNode::getPassengerEdges(bool incoming) const {
2488 EdgeVector result;
2489 for (NBEdge* e : (incoming ? myIncomingEdges : myOutgoingEdges)) {
2490 if ((e->getPermissions() & SVC_PASSENGER) != 0) {
2491 result.push_back(e);
2492 }
2493 }
2494 return result;
2495}
2496
2498NBNode::getDirection(const NBEdge* const incoming, const NBEdge* const outgoing, bool leftHand) const {
2499 // ok, no connection at all -> dead end
2500 if (outgoing == nullptr) {
2501 return LinkDirection::NODIR;
2502 }
2503 assert(incoming->getToNode() == this);
2504 assert(outgoing->getFromNode() == this);
2507 }
2508 // turning direction
2509 if (incoming->isTurningDirectionAt(outgoing)) {
2510 if (isExplicitRailNoBidi(incoming, outgoing)) {
2512 }
2514 }
2515 // get the angle between incoming/outgoing at the junction
2516 const double angle = NBHelpers::normRelAngle(incoming->getAngleAtNode(this), outgoing->getAngleAtNode(this));
2517 // ok, should be a straight connection
2518 EdgeVector::const_iterator itOut = std::find(myAllEdges.begin(), myAllEdges.end(), outgoing);
2519 SVCPermissions vehPerm = incoming->getPermissions() & outgoing->getPermissions();
2520 if (vehPerm != SVC_PEDESTRIAN) {
2521 vehPerm &= ~SVC_PEDESTRIAN;
2522 }
2523 const int modeLanes = outgoing->getNumLanesThatAllow(vehPerm);
2524 if (fabs(angle) < 44.) {
2525 if (fabs(angle) > 6.) {
2526 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, true))) {
2528 }
2529 if (isStraighter(incoming, angle, vehPerm, modeLanes, getNextCompatibleOutgoing(incoming, vehPerm, itOut, false))) {
2531 }
2532 }
2533 if (angle > 0 && incoming->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
2534 return angle > 15 ? LinkDirection::RIGHT : LinkDirection::PARTRIGHT;
2535 }
2537 }
2538
2539 if (angle > 0) {
2540 // check whether any other edge goes further to the right
2541 if (angle > 90 + NUMERICAL_EPS) {
2542 return LinkDirection::RIGHT;
2543 }
2544 NBEdge* outCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, !leftHand);
2545 if (outCW != nullptr) {
2547 } else {
2548 return LinkDirection::RIGHT;
2549 }
2550 } else {
2551 // check whether any other edge goes further to the left
2552 if (angle < -170 && incoming->getGeometry().reverse() == outgoing->getGeometry()) {
2553 if (isExplicitRailNoBidi(incoming, outgoing)) {
2555 }
2557 } else if (angle < -(90 + NUMERICAL_EPS)) {
2558 return LinkDirection::LEFT;
2559 }
2560 NBEdge* outCCW = getNextCompatibleOutgoing(incoming, vehPerm, itOut, leftHand);
2561 if (outCCW != nullptr) {
2563 } else {
2564 return LinkDirection::LEFT;
2565 }
2566 }
2567}
2568
2569
2570bool
2571NBNode::isExplicitRailNoBidi(const NBEdge* incoming, const NBEdge* outgoing) {
2572 // assume explicit connections at sharp turn-arounds are either for reversal or due to a geometry glitch
2573 // (but should not have been guessed)
2574 // @note this function is also called from NBAlgorithms when there aren't any connections ready
2576 && isRailway(incoming->getPermissions())
2577 && isRailway(outgoing->getPermissions())
2578 && incoming->getBidiEdge() != outgoing);
2579}
2580
2581
2583NBNode::getLinkState(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane,
2584 bool mayDefinitelyPass, const std::string& tlID) const {
2586 return LINKSTATE_MAJOR; // the trains must run on time
2587 }
2588 if (tlID != "") {
2590 return LINKSTATE_ALLWAY_STOP;
2591 }
2592 return mustBrake(incoming, outgoing, fromLane, toLane, true) ? LINKSTATE_TL_OFF_BLINKING : LINKSTATE_TL_OFF_NOSIGNAL;
2593 }
2594 if (outgoing == nullptr) { // always off
2596 }
2598 && mustBrake(incoming, outgoing, fromLane, toLane, true)) {
2599 return LINKSTATE_EQUAL; // all the same
2600 }
2602 return LINKSTATE_ALLWAY_STOP; // all drive, first one to arrive may drive first
2603 }
2604 if (myType == SumoXMLNodeType::ZIPPER && zipperConflict(incoming, outgoing, fromLane, toLane)) {
2605 return LINKSTATE_ZIPPER;
2606 }
2607 if (!mayDefinitelyPass
2608 && mustBrake(incoming, outgoing, fromLane, toLane, true)
2609 // legacy mode
2610 && (!incoming->isInsideTLS() || getDirection(incoming, outgoing) != LinkDirection::STRAIGHT)
2611 // avoid linkstate minor at pure railway nodes
2614 }
2615 // traffic lights are not regarded here
2616 return LINKSTATE_MAJOR;
2617}
2618
2619
2620bool
2621NBNode::zipperConflict(const NBEdge* incoming, const NBEdge* outgoing, int fromLane, int toLane) const {
2622 if (mustBrake(incoming, outgoing, fromLane, toLane, false)) {
2623 // there should be another connection with the same target (not just some intersecting trajectories)
2624 for (const NBEdge* in : getIncomingEdges()) {
2625 for (const NBEdge::Connection& c : in->getConnections()) {
2626 if ((in != incoming || c.fromLane != fromLane) && c.toEdge == outgoing && c.toLane == toLane) {
2627 return true;
2628 }
2629 }
2630 }
2631 }
2632 return false;
2633}
2634
2635
2636bool
2638 SVCPermissions railClasses = 0;
2639 for (NBEdge* e : myIncomingEdges) {
2640 railClasses |= (e->getPermissions() & SVC_RAIL_CLASSES);
2641 }
2642 assert(railClasses != 0);
2643 return ((railClasses & myPermitUnsignalizedClasses) == railClasses
2644 && (railClasses & myHaveRailSignalClasses) == 0);
2645}
2646
2647
2648void
2650 myPermitUnsignalizedClasses = parseVehicleClasses(OptionsCont::getOptions().getStringVector("railway.signal.permit-unsignalized"));
2652 for (auto it : nc) {
2653 const NBNode* n = it.second;
2655 for (const NBEdge* in : n->getIncomingEdges()) {
2656 myHaveRailSignalClasses |= in->getPermissions();
2657 }
2658 }
2659 }
2660}
2661
2662
2663bool
2665 std::string reason;
2666 return checkIsRemovableReporting(reason);
2667}
2668
2669bool
2670NBNode::checkIsRemovableReporting(std::string& reason) const {
2671 if (getEdges().empty()) {
2672 return true;
2673 }
2674 // check whether this node is included in a traffic light or crossing
2675 if (myTrafficLights.size() != 0) {
2676 reason = "TLS";
2677 return false;
2678 }
2680 reason = "rail_signal";
2681 return false;
2682 }
2683 if (myCrossings.size() != 0) {
2684 reason = "crossing";
2685 return false;
2686 }
2687 EdgeVector::const_iterator i;
2688 // one in, one out -> just a geometry ...
2689 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2690 // ... if types match ...
2691 if (!myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2692 reason = "edges incompatible: " + reason;
2693 return false;
2694 }
2695 if (myIncomingEdges[0]->getTurnDestination(true) == myOutgoingEdges[0]) {
2696 reason = "turnaround";
2697 return false;
2698 }
2699 return true;
2700 }
2701 // two in, two out -> may be something else
2702 if (myOutgoingEdges.size() == 2 && myIncomingEdges.size() == 2) {
2703 // check whether the origin nodes of the incoming edges differ
2704 std::set<NBNode*> origSet;
2705 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2706 origSet.insert((*i)->getFromNode());
2707 }
2708 if (origSet.size() < 2) {
2709 // overlapping case
2710 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2711 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2712 return ((myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason) &&
2713 myIncomingEdges[1]->expandableBy(myOutgoingEdges[1], reason))
2714 || (myIncomingEdges[0]->expandableBy(myOutgoingEdges[1], reason) &&
2715 myIncomingEdges[1]->expandableBy(myOutgoingEdges[0], reason)));
2716 }
2717 }
2718 // check whether this node is an intermediate node of
2719 // a two-directional street
2720 for (i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2721 // each of the edges must have an opposite direction edge
2722 NBEdge* opposite = (*i)->getTurnDestination(true);
2723 if (opposite != nullptr) {
2724 // the other outgoing edges must be the continuation of the current
2725 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2726 // check whether the types allow joining
2727 if (!(*i)->expandableBy(continuation, reason)) {
2728 reason = "edges incompatible: " + reason;
2729 return false;
2730 }
2731 } else {
2732 // ok, at least one outgoing edge is not an opposite
2733 // of an incoming one
2734 reason = "not opposites";
2735 return false;
2736 }
2737 }
2738 return true;
2739 }
2740 // ok, a real node
2741 reason = "intersection";
2742 return false;
2743}
2744
2745
2746std::vector<std::pair<NBEdge*, NBEdge*> >
2748 assert(checkIsRemovable());
2749 std::vector<std::pair<NBEdge*, NBEdge*> > ret;
2750 // one in, one out-case
2751 if (myOutgoingEdges.size() == 1 && myIncomingEdges.size() == 1) {
2752 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2753 return ret;
2754 }
2755 if (myIncomingEdges.size() == 2 && myOutgoingEdges.size() == 2) {
2756 // two in, two out-case
2757 if (myIncomingEdges[0]->getGeometry() == myIncomingEdges[1]->getGeometry() &&
2758 myOutgoingEdges[0]->getGeometry() == myOutgoingEdges[1]->getGeometry()) {
2759 // overlapping edges
2760 std::string reason;
2761 if (myIncomingEdges[0]->expandableBy(myOutgoingEdges[0], reason)) {
2762 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[0]));
2763 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[1]));
2764 } else {
2765 ret.push_back(std::make_pair(myIncomingEdges[0], myOutgoingEdges[1]));
2766 ret.push_back(std::make_pair(myIncomingEdges[1], myOutgoingEdges[0]));
2767 }
2768 return ret;
2769 }
2770 }
2771 for (EdgeVector::const_iterator i = myIncomingEdges.begin(); i != myIncomingEdges.end(); i++) {
2772 // join with the edge that is not a turning direction
2773 NBEdge* opposite = (*i)->getTurnDestination(true);
2774 assert(opposite != 0);
2775 NBEdge* continuation = opposite == myOutgoingEdges.front() ? myOutgoingEdges.back() : myOutgoingEdges.front();
2776 ret.push_back(std::pair<NBEdge*, NBEdge*>(*i, continuation));
2777 }
2778 return ret;
2779}
2780
2781
2782const PositionVector&
2784 return myPoly;
2785}
2786
2787
2788void
2790 myPoly = shape;
2791 myHaveCustomPoly = (myPoly.size() > 1);
2792 if (myHaveCustomPoly) {
2793 for (EdgeVector::iterator i = myAllEdges.begin(); i != myAllEdges.end(); i++) {
2794 (*i)->resetNodeBorder(this);
2795 }
2796 }
2797}
2798
2799
2800NBEdge*
2802 for (NBEdge* e : myOutgoingEdges) {
2803 if (e->getToNode() == n && e->getPermissions() != 0) {
2804 return e;
2805 }
2806 }
2807 return nullptr;
2808}
2809
2810
2811bool
2813 if (isDistrict()) {
2814 return false;
2815 }
2816 for (const NBEdge* const t : getEdges()) {
2817 const NBNode* const other = t->getToNode() == this ? t->getFromNode() : t->getToNode();
2818 for (const NBEdge* const k : other->getEdges()) {
2819 if (k->getFromNode()->isDistrict() || k->getToNode()->isDistrict()) {
2820 return true;
2821 }
2822 }
2823 }
2824 return false;
2825}
2826
2827
2828bool
2832
2833
2834int
2836#ifdef DEBUG_PED_STRUCTURES
2838#endif
2839 int numGuessed = 0;
2840 if (myCrossings.size() > 0 || myDiscardAllCrossings) {
2841 // user supplied crossings, do not guess
2842 return numGuessed;
2843 }
2844 DEBUGCOUT(gDebugFlag1, "guess crossings for " << getID() << "\n")
2846 // check for pedestrial lanes going clockwise around the node
2847 std::vector<std::pair<NBEdge*, bool> > normalizedLanes;
2848 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
2849 NBEdge* edge = *it;
2850 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
2851 if (edge->getFromNode() == this) {
2852 for (std::vector<NBEdge::Lane>::const_reverse_iterator it_l = lanes.rbegin(); it_l != lanes.rend(); ++it_l) {
2853 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2854 }
2855 } else {
2856 for (std::vector<NBEdge::Lane>::const_iterator it_l = lanes.begin(); it_l != lanes.end(); ++it_l) {
2857 normalizedLanes.push_back(std::make_pair(edge, ((*it_l).permissions & SVC_PEDESTRIAN) != 0));
2858 }
2859 }
2860 }
2861 // do we even have a pedestrian lane?
2862 int firstSidewalk = -1;
2863 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2864 if (normalizedLanes[i].second) {
2865 firstSidewalk = i;
2866 break;
2867 }
2868 }
2869 int hadCandidates = 0;
2870 std::vector<int> connectedCandidates; // number of crossings that were built for each connected candidate
2871 if (firstSidewalk != -1) {
2872 // rotate lanes to ensure that the first one allows pedestrians
2873 std::vector<std::pair<NBEdge*, bool> > tmp;
2874 copy(normalizedLanes.begin() + firstSidewalk, normalizedLanes.end(), std::back_inserter(tmp));
2875 copy(normalizedLanes.begin(), normalizedLanes.begin() + firstSidewalk, std::back_inserter(tmp));
2876 normalizedLanes = tmp;
2877 // find candidates
2878 EdgeVector candidates;
2879 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
2880 NBEdge* edge = normalizedLanes[i].first;
2881 const bool allowsPed = normalizedLanes[i].second;
2882 DEBUGCOUT(gDebugFlag1, " cands=" << toString(candidates) << " edge=" << edge->getID() << " allowsPed=" << allowsPed << "\n")
2883 if (!allowsPed && (candidates.size() == 0 || candidates.back() != edge)) {
2884 candidates.push_back(edge);
2885 } else if (allowsPed) {
2886 if (candidates.size() > 0) {
2887 if (hadCandidates > 0 || forbidsPedestriansAfter(normalizedLanes, i)) {
2888 hadCandidates++;
2889 const int n = checkCrossing(candidates);
2890 numGuessed += n;
2891 if (n > 0) {
2892 connectedCandidates.push_back(n);
2893 }
2894 }
2895 candidates.clear();
2896 }
2897 }
2898 }
2899 if (hadCandidates > 0 && candidates.size() > 0) {
2900 // avoid wrapping around to the same sidewalk
2901 hadCandidates++;
2902 const int n = checkCrossing(candidates);
2903 numGuessed += n;
2904 if (n > 0) {
2905 connectedCandidates.push_back(n);
2906 }
2907 }
2908 }
2909 // Avoid duplicate crossing between the same pair of walkingareas
2910 DEBUGCOUT(gDebugFlag1, " hadCandidates=" << hadCandidates << " connectedCandidates=" << toString(connectedCandidates) << "\n")
2911 if (hadCandidates == 2 && connectedCandidates.size() == 2) {
2912 // One or both of them might be split: remove the one with less splits
2913 if (connectedCandidates.back() <= connectedCandidates.front()) {
2914 numGuessed -= connectedCandidates.back();
2915 myCrossings.erase(myCrossings.end() - connectedCandidates.back(), myCrossings.end());
2916 } else {
2917 numGuessed -= connectedCandidates.front();
2918 myCrossings.erase(myCrossings.begin(), myCrossings.begin() + connectedCandidates.front());
2919 }
2920 }
2922#ifdef DEBUG_PED_STRUCTURES
2923 if (gDebugFlag1) {
2924 std::cout << "guessedCrossings:\n";
2925 for (auto& crossing : myCrossings) {
2926 std::cout << " edges=" << toString(crossing->edges) << "\n";
2927 }
2928 }
2929#endif
2930 if (numGuessed > 0 && isSimpleContinuation(true, true)) {
2931 // avoid narrow node shape when there is a crossing
2932 computeNodeShape(-1);
2933 for (NBEdge* e : myAllEdges) {
2934 e->computeEdgeShape();
2935 }
2936 }
2937 return numGuessed;
2938}
2939
2940
2941int
2942NBNode::checkCrossing(EdgeVector candidates, bool checkOnly) {
2943 DEBUGCOUT(gDebugFlag1, "checkCrossing candidates=" << toString(candidates) << "\n")
2944 if (candidates.size() == 0) {
2945 DEBUGCOUT(gDebugFlag1, "no crossing added (numCandidates=" << candidates.size() << ")\n")
2946 return 0;
2947 } else {
2948 // check whether the edges may be part of a common crossing due to having similar angle
2949 double prevAngle = -100000; // dummy
2950 for (int i = 0; i < (int)candidates.size(); ++i) {
2951 NBEdge* edge = candidates[i];
2952 double angle = edge->getCrossingAngle(this);
2953 // edges should be sorted by angle but this only holds true approximately
2954 if (i > 0 && fabs(NBHelpers::relAngle(angle, prevAngle)) > EXTEND_CROSSING_ANGLE_THRESHOLD) {
2955 DEBUGCOUT(gDebugFlag1, "no crossing added (found angle difference of " << fabs(NBHelpers::relAngle(angle, prevAngle)) << " at i=" << i << "\n")
2956 return 0;
2957 }
2958 if (!checkOnly && !isTLControlled() && myType != SumoXMLNodeType::RAIL_CROSSING && edge->getSpeed() > OptionsCont::getOptions().getFloat("crossings.guess.speed-threshold")) {
2959 DEBUGCOUT(gDebugFlag1, "no crossing added (uncontrolled, edge with speed > " << edge->getSpeed() << ")\n")
2960 return 0;
2961 }
2962 prevAngle = angle;
2963 }
2964 if (candidates.size() == 1 || getType() == SumoXMLNodeType::RAIL_CROSSING) {
2965 if (!checkOnly) {
2967 || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
2968 DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
2969 }
2970 return 1;
2971 } else {
2972 // check for intermediate walking areas
2973 prevAngle = -100000; // dummy
2974 for (EdgeVector::iterator it = candidates.begin(); it != candidates.end(); ++it) {
2975 double angle = (*it)->getCrossingAngle(this);
2976 if (it != candidates.begin()) {
2977 NBEdge* prev = *(it - 1);
2978 NBEdge* curr = *it;
2979 Position prevPos, currPos;
2980 int laneI;
2981 // compute distance between candiate edges
2982 double intermediateWidth = 0;
2983 if (prev->getToNode() == this) {
2984 laneI = prev->getNumLanes() - 1;
2985 prevPos = prev->getLanes()[laneI].shape[-1];
2986 } else {
2987 laneI = 0;
2988 prevPos = prev->getLanes()[laneI].shape[0];
2989 }
2990 intermediateWidth -= 0.5 * prev->getLaneWidth(laneI);
2991 if (curr->getFromNode() == this) {
2992 laneI = curr->getNumLanes() - 1;
2993 currPos = curr->getLanes()[laneI].shape[0];
2994 } else {
2995 laneI = 0;
2996 currPos = curr->getLanes()[laneI].shape[-1];
2997 }
2998 intermediateWidth -= 0.5 * curr->getLaneWidth(laneI);
2999 intermediateWidth += currPos.distanceTo2D(prevPos);
3000 DEBUGCOUT(gDebugFlag1, " prevAngle=" << prevAngle << " angle=" << angle << " intermediateWidth=" << intermediateWidth << "\n")
3001 if (fabs(NBHelpers::relAngle(prevAngle, angle)) > SPLIT_CROSSING_ANGLE_THRESHOLD
3002 || (intermediateWidth > SPLIT_CROSSING_WIDTH_THRESHOLD)) {
3003 return checkCrossing(EdgeVector(candidates.begin(), it), checkOnly)
3004 + checkCrossing(EdgeVector(it, candidates.end()), checkOnly);
3005 }
3006 }
3007 prevAngle = angle;
3008 }
3009 if (!checkOnly) {
3011 || (isRoundabout() && OptionsCont::getOptions().getBool("crossings.guess.roundabout-priority")));
3012 DEBUGCOUT(gDebugFlag1, "adding crossing: " << toString(candidates) << "\n")
3013 }
3014 return 1;
3015 }
3016 }
3017}
3018
3019
3020bool
3022 // sort edge vector
3023 std::sort(edges.begin(), edges.end());
3024 // iterate over crossing to find a crossing with the same edges
3025 for (auto& crossing : myCrossings) {
3026 // sort edges of crossing before compare
3027 EdgeVector edgesOfCrossing = crossing->edges;
3028 std::sort(edgesOfCrossing.begin(), edgesOfCrossing.end());
3029 if (edgesOfCrossing == edges) {
3030 return true;
3031 }
3032 }
3033 return false;
3034}
3035
3036
3037bool
3038NBNode::forbidsPedestriansAfter(std::vector<std::pair<NBEdge*, bool> > normalizedLanes, int startIndex) {
3039 for (int i = startIndex; i < (int)normalizedLanes.size(); ++i) {
3040 if (!normalizedLanes[i].second) {
3041 return true;
3042 }
3043 }
3044 return false;
3045}
3046
3047
3048void
3051 buildWalkingAreas(OptionsCont::getOptions().getInt("junctions.corner-detail"),
3052 OptionsCont::getOptions().getFloat("walkingareas.join-dist"));
3054 // ensure that all crossings are properly connected
3055 bool recheck = myCrossings.size() > 0;
3056 while (recheck) {
3057 recheck = false;
3058 std::set<std::string> waIDs;
3059 int numSidewalks = 0;
3060 for (WalkingArea& wa : myWalkingAreas) {
3061 waIDs.insert(wa.id);
3062 numSidewalks += (int)(wa.prevSidewalks.size() + wa.nextSidewalks.size());
3063 }
3064 if (numSidewalks < 2) {
3065 // all crossings are invalid if there are fewer than 2 sidewalks involved
3066 waIDs.clear();
3067 }
3068 for (auto& crossing : myCrossings) {
3069 if (waIDs.count(crossing->prevWalkingArea) == 0 || waIDs.count(crossing->nextWalkingArea) == 0 || !crossing->valid) {
3070 if (crossing->valid) {
3071 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no walkingarea found)."),
3072 crossing->id, getID(), toString(crossing->edges));
3073 recheck = true;
3074 }
3075 for (auto waIt = myWalkingAreas.begin(); waIt != myWalkingAreas.end();) {
3076 WalkingArea& wa = *waIt;
3077 std::vector<std::string>::iterator it_nc = std::find(wa.nextCrossings.begin(), wa.nextCrossings.end(), crossing->id);
3078 if (it_nc != wa.nextCrossings.end()) {
3079 wa.nextCrossings.erase(it_nc);
3080 }
3081 if (wa.prevSidewalks.size() + wa.nextSidewalks.size() + wa.nextCrossings.size() + wa.prevCrossings.size() < 2) {
3082 waIt = myWalkingAreas.erase(waIt);
3083 recheck = true;
3084 } else {
3085 waIt++;
3086 }
3087 }
3088 crossing->valid = false;
3089 crossing->prevWalkingArea = "";
3090 crossing->nextWalkingArea = "";
3091 }
3092 }
3093 }
3094}
3095
3096
3097std::vector<NBNode::Crossing*>
3099 std::vector<Crossing*> result;
3100 for (auto& c : myCrossings) {
3101 if (c->valid) {
3102 result.push_back(c.get());
3103 }
3104 }
3105 //if (myCrossings.size() > 0) {
3106 // std::cout << "valid crossings at " << getID() << "\n";
3107 // for (std::vector<NBNode::Crossing*>::const_iterator it = result.begin(); it != result.end(); ++it) {
3108 // std::cout << " " << toString((*it)->edges) << "\n";
3109 // }
3110 //}
3111 return result;
3112}
3113
3114
3115void
3117 myCrossings.clear();
3118 // also discard all further crossings
3119 if (rejectAll) {
3120 myDiscardAllCrossings = true;
3121 }
3122}
3123
3124
3125void
3129
3130
3131double
3133 // myDisplacementError is computed during this operation. reset first
3135 // build inner edges for vehicle movements across the junction
3136 int noInternalNoSplits = 0;
3137 for (const NBEdge* const edge : myIncomingEdges) {
3138 for (const NBEdge::Connection& con : edge->getConnections()) {
3139 if (con.toEdge == nullptr) {
3140 continue;
3141 }
3142 noInternalNoSplits++;
3143 }
3144 }
3145 int lno = 0;
3146 int splitNo = 0;
3147 double maxCrossingSeconds = 0.;
3148 for (NBEdge* const edge : myIncomingEdges) {
3149 maxCrossingSeconds = MAX2(maxCrossingSeconds, edge->buildInnerEdges(*this, noInternalNoSplits, lno, splitNo));
3150 }
3151 return maxCrossingSeconds;
3152}
3153
3154
3155int
3157#ifdef DEBUG_PED_STRUCTURES
3159#endif
3160 DEBUGCOUT(gDebugFlag1, "build crossings for " << getID() << ":\n")
3162 myCrossings.clear();
3163 }
3164 int index = 0;
3165 const double defaultWidth = OptionsCont::getOptions().getFloat("default.crossing-width");
3166 for (auto& c : myCrossings) {
3167 c->valid = true;
3168 if (!isTLControlled()) {
3169 c->tlID = ""; // reset for Netedit, set via setCrossingTLIndices()
3170 }
3171 c->id = ":" + getID() + "_c" + toString(index++);
3172 c->width = (c->customWidth == NBEdge::UNSPECIFIED_WIDTH) ? defaultWidth : c->customWidth;
3173 // reset fields, so repeated computation (Netedit) will successfully perform the checks
3174 // in buildWalkingAreas (split crossings) and buildInnerEdges (sanity check)
3175 c->nextWalkingArea = "";
3176 c->prevWalkingArea = "";
3177 EdgeVector& edges = c->edges;
3178 DEBUGCOUT(gDebugFlag1, " crossing=" << c->id << " edges=" << toString(edges))
3179 // sorting the edges in the right way is imperative. We want to sort
3180 // them by getAngleAtNodeToCenter() but need to be extra carefull to avoid wrapping around 0 somewhere in between
3181 std::sort(edges.begin(), edges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
3182 DEBUGCOUT(gDebugFlag1, " sortedEdges=" << toString(edges) << "\n")
3183 // rotate the edges so that the largest relative angle difference comes at the end
3184 std::vector<double> rawAngleDiffs;
3185 double maxAngleDiff = 0;
3186 int maxAngleDiffIndex = 0; // index before maxDist
3187 for (int i = 0; i < (int) edges.size(); i++) {
3188 double diff = NBHelpers::relAngle(edges[i]->getAngleAtNodeToCenter(this),
3189 edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this));
3190 if (diff < 0) {
3191 diff += 360;
3192 }
3193 const double rawDiff = NBHelpers::relAngle(
3194 edges[i]->getAngleAtNodeNormalized(this),
3195 edges[(i + 1) % edges.size()]->getAngleAtNodeNormalized(this));
3196 rawAngleDiffs.push_back(fabs(rawDiff));
3197
3198 DEBUGCOUT(gDebugFlag1, " i=" << i << " a1=" << edges[i]->getAngleAtNodeToCenter(this) << " a2=" << edges[(i + 1) % edges.size()]->getAngleAtNodeToCenter(this) << " diff=" << diff << "\n")
3199 if (diff > maxAngleDiff) {
3200 maxAngleDiff = diff;
3201 maxAngleDiffIndex = i;
3202 }
3203 }
3204 if (maxAngleDiff > 2 && maxAngleDiff < 360 - 2) {
3205 // if the angle differences is too small, we better not rotate
3206 std::rotate(edges.begin(), edges.begin() + (maxAngleDiffIndex + 1) % edges.size(), edges.end());
3207 DEBUGCOUT(gDebugFlag1, " rotatedEdges=" << toString(edges))
3208 }
3209 bool diagonalCrossing = false;
3210 std::sort(rawAngleDiffs.begin(), rawAngleDiffs.end());
3211 if (rawAngleDiffs.size() >= 2 && rawAngleDiffs[rawAngleDiffs.size() - 2] > 30) {
3212 diagonalCrossing = true;
3213#ifdef DEBUG_PED_STRUCTURES
3214 if (gDebugFlag1) {
3215 std::cout << " detected pedScramble " << c->id << " edges=" << toString(edges) << " rawDiffs=" << toString(rawAngleDiffs) << "\n";
3216 for (auto e : edges) {
3217 std::cout << " e=" << e->getID()
3218 << " aC=" << e->getAngleAtNodeToCenter(this)
3219 << " a=" << e->getAngleAtNode(this)
3220 << " aN=" << e->getAngleAtNodeNormalized(this)
3221 << "\n";
3222 }
3223 }
3224#endif
3225 }
3226 // reverse to get them in CCW order (walking direction around the node)
3227 std::reverse(edges.begin(), edges.end());
3228 // compute shape
3229 c->shape.clear();
3230 const int begDir = (edges.front()->getFromNode() == this ? FORWARD : BACKWARD);
3231 const int endDir = (edges.back()->getToNode() == this ? FORWARD : BACKWARD);
3232 int firstNonPedLane = edges.front()->getFirstNonPedestrianLaneIndex(begDir);
3233 int lastNonPedLane = edges.back()->getFirstNonPedestrianLaneIndex(endDir);
3234 DEBUGCOUT(gDebugFlag1, " finalEdges=" << toString(edges) << " firstNonPedLane=" << firstNonPedLane << " lastNonPedLane=" << lastNonPedLane << "\n")
3235 if (firstNonPedLane < 0 || lastNonPedLane < 0) {
3236 // invalid crossing
3237 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (no vehicle lanes to cross)."), c->id, getID(), toString(c->edges));
3238 c->valid = false;
3239 // compute surrogate shape to make it visible in netedit
3240 firstNonPedLane = begDir == FORWARD ? 0 : edges.front()->getNumLanes() - 1;
3241 lastNonPedLane = endDir == FORWARD ? 0 : edges.back()->getNumLanes() - 1;
3242 }
3243 if (c->customShape.size() != 0) {
3244 c->shape = c->customShape;
3245 } else {
3246 NBEdge::Lane crossingBeg = edges.front()->getLanes()[firstNonPedLane];
3247 NBEdge::Lane crossingEnd = edges.back()->getLanes()[lastNonPedLane];
3248 crossingBeg.width = (crossingBeg.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingBeg.width);
3249 crossingEnd.width = (crossingEnd.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : crossingEnd.width);
3250 crossingBeg.shape.move2side(begDir * crossingBeg.width / 2);
3251 crossingEnd.shape.move2side(endDir * crossingEnd.width / 2);
3252 double offset = c->width / 2;
3254 crossingBeg.shape.extrapolate(offset);
3255 crossingEnd.shape.extrapolate(offset);
3256 // check if after all changes shape are NAN (in these case, discard)
3257 if (crossingBeg.shape.isNAN() || crossingEnd.shape.isNAN()) {
3258 WRITE_WARNINGF(TL("Discarding invalid crossing '%' at junction '%' with edges [%] (invalid shape)."), c->id, getID(), toString(c->edges));
3259 c->valid = false;
3260 } else {
3261 c->shape.push_back(crossingBeg.shape[begDir == FORWARD ? 0 : -1]);
3262 c->shape.push_back(crossingEnd.shape[endDir == FORWARD ? -1 : 0]);
3263 }
3264 if (diagonalCrossing) {
3265 c->shape.move2side(-c->width);
3266 }
3267 }
3268 }
3269 return index;
3270}
3271
3272
3273void
3275 if (myCrossings.size() == 1 && myAllEdges.size() >= 3) {
3276 EdgeVector nonPedIncoming;
3277 EdgeVector nonPedOutgoing;
3278 EdgeVector pedIncoming;
3279 EdgeVector pedOutgoing;
3280 for (NBEdge* e : getIncomingEdges()) {
3281 if (e->getPermissions() != SVC_PEDESTRIAN) {
3282 nonPedIncoming.push_back(e);
3283 } else {
3284 pedIncoming.push_back(e);
3285 }
3286 }
3287 for (NBEdge* e : getOutgoingEdges()) {
3288 if (e->getPermissions() != SVC_PEDESTRIAN) {
3289 nonPedOutgoing.push_back(e);
3290 } else {
3291 pedOutgoing.push_back(e);
3292 }
3293 }
3294 if (geometryLike(nonPedIncoming, nonPedOutgoing) && (pedIncoming.size() > 0 || pedOutgoing.size() > 0)) {
3295 double maxAngle = 0;
3296 const NBEdge* in = nonPedIncoming.front();
3297 const NBEdge* out = nonPedOutgoing.front();
3298 if (nonPedIncoming.size() == 1) {
3299 maxAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(this), out->getAngleAtNode(this)));
3300 } else {
3301 for (const NBEdge* const in2 : nonPedIncoming) {
3302 double minAngle = 180;
3303 for (const NBEdge* const out2 : nonPedOutgoing) {
3304 double angle = fabs(NBHelpers::relAngle(in2->getAngleAtNode(this), out2->getAngleAtNode(this)));
3305 if (angle < minAngle) {
3306 minAngle = angle;
3307 in = in2;
3308 out = out2;
3309 }
3310 }
3311 maxAngle = MAX2(maxAngle, minAngle);
3312 }
3313 }
3314 // changing the offset only handles the simple case where the road stays straight
3315 if (maxAngle < 15) {
3316 const int inLane = in->getFirstNonPedestrianLaneIndex(FORWARD);
3317 const int outLane = out->getFirstNonPedestrianLaneIndex(FORWARD);
3318 if (inLane >= 0 && outLane >= 0) {
3319 const Position& p0 = in->getLaneShape(inLane).back();
3320 const Position& p1 = out->getLaneShape(outLane).front();
3321 PositionVector road;
3322 road.push_back(p0);
3323 road.push_back(p1);
3324 Position mid = (p0 + p1) / 2;
3325 double maxPathDist = 0;
3326 for (NBEdge* e : pedIncoming) {
3327 const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).back()));
3328 maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3329 }
3330 for (NBEdge* e : pedOutgoing) {
3331 const Position roadPos = road.positionAtOffset2D(road.nearest_offset_to_point2D(e->getLaneShape(0).front()));
3332 maxPathDist = MAX2(maxPathDist, mid.distanceTo2D(roadPos));
3333 }
3334 // if the junction is stretched, the crossing should stay close to the paths
3335 if (maxPathDist < MAX2(myCrossings.front()->width, 4.0)) {
3336 offset = p0.distanceTo2D(p1) / 2;
3337 }
3338 }
3339 }
3340 }
3341 }
3342}
3343
3344
3345void
3346NBNode::buildWalkingAreas(int cornerDetail, double joinMinDist) {
3347#ifdef DEBUG_PED_STRUCTURES
3349#endif
3350 int index = 0;
3351 myWalkingAreas.clear();
3352 DEBUGCOUT(gDebugFlag1, "build walkingAreas for " << getID() << ":\n")
3353 if (myAllEdges.size() == 0) {
3354 return;
3355 }
3357 // shapes are all pointing away from the intersection
3358 std::vector<std::pair<NBEdge*, NBEdge::Lane> > normalizedLanes;
3359 for (EdgeVector::const_iterator it = allEdges.begin(); it != allEdges.end(); ++it) {
3360 NBEdge* edge = *it;
3361 const std::vector<NBEdge::Lane>& lanes = edge->getLanes();
3362 std::vector<NBEdge::Lane> tmp;
3363 bool hadSidewalk = false;
3364 bool hadNonSidewalk = false;
3365 for (int i = 0; i < (int)lanes.size(); i++) {
3366 NBEdge::Lane l = lanes[i];
3367 const bool sidewalk = (l.permissions & SVC_PEDESTRIAN) != 0;
3368 if (sidewalk) {
3369 if (hadSidewalk && hadNonSidewalk) {
3370 if (edge->getFromNode() == this) {
3371 WRITE_WARNINGF(TL("Ignoring additional sidewalk lane % on edge '%' for walkingareas."),
3372 i, edge->getID());
3373 }
3374 continue;
3375 }
3376 hadSidewalk = true;
3377 } else {
3378 hadNonSidewalk = true;
3379 }
3380 tmp.push_back(l);
3381 }
3382 if (edge->getFromNode() == this) {
3383 std::reverse(tmp.begin(), tmp.end());
3384 } else {
3385 for (NBEdge::Lane& l : tmp) {
3386 l.shape = l.shape.reverse();
3387 }
3388 }
3389 for (NBEdge::Lane& l : tmp) {
3390 l.shape = l.shape.getSubpartByIndex(0, 2);
3391 l.width = (l.width == NBEdge::UNSPECIFIED_WIDTH ? SUMO_const_laneWidth : l.width);
3392 normalizedLanes.push_back(std::make_pair(edge, l));
3393 }
3394 }
3395 //if (gDebugFlag1) std::cout << " normalizedLanes=" << normalizedLanes.size() << "\n";
3396 // collect [start,count[ indices in normalizedLanes that belong to a walkingArea
3397 std::vector<std::pair<int, int> > waIndices;
3398 int start = -1;
3399 NBEdge* prevEdge = normalizedLanes.back().first;
3400 for (int i = 0; i < (int)normalizedLanes.size(); ++i) {
3401 NBEdge* edge = normalizedLanes[i].first;
3402 NBEdge::Lane& l = normalizedLanes[i].second;
3403 if (start == -1) {
3404 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3405 start = i;
3406 }
3407 } else {
3408 if ((l.permissions & SVC_PEDESTRIAN) == 0
3409 || crossingBetween(edge, prevEdge)
3410 || alreadyConnectedPaths(edge, prevEdge, joinMinDist)
3411 || crossesFringe(edge, prevEdge)
3412 ) {
3413 waIndices.push_back(std::make_pair(start, i - start));
3414 if ((l.permissions & SVC_PEDESTRIAN) != 0) {
3415 start = i;
3416 } else {
3417 start = -1;
3418 }
3419
3420 }
3421 }
3422 DEBUGCOUT(gDebugFlag1, " i=" << i << " edge=" << edge->getID() << " start=" << start << " ped=" << ((l.permissions & SVC_PEDESTRIAN) != 0)
3423 << " waI=" << waIndices.size() << " crossingBetween=" << crossingBetween(edge, prevEdge) << "\n")
3424 prevEdge = edge;
3425 }
3426 // deal with wrap-around issues
3427 if (start != - 1) {
3428 const int waNumLanes = (int)normalizedLanes.size() - start;
3429 if (waIndices.size() == 0) {
3430 waIndices.push_back(std::make_pair(start, waNumLanes));
3431 DEBUGCOUT(gDebugFlag1, " single wa, end at wrap-around\n")
3432 } else {
3433 if (waIndices.front().first == 0) {
3434 NBEdge* edge = normalizedLanes.front().first;
3435 if (crossingBetween(edge, normalizedLanes.back().first)
3436 || crossesFringe(edge, normalizedLanes.back().first)) {
3437 // do not wrap-around (see above)
3438 waIndices.push_back(std::make_pair(start, waNumLanes));
3439 DEBUGCOUT(gDebugFlag1, " do not wrap around\n")
3440 } else {
3441 // first walkingArea wraps around
3442 waIndices.front().first = start;
3443 waIndices.front().second = waNumLanes + waIndices.front().second;
3444 DEBUGCOUT(gDebugFlag1, " wrapping around\n")
3445 }
3446 } else {
3447 // last walkingArea ends at the wrap-around
3448 waIndices.push_back(std::make_pair(start, waNumLanes));
3449 DEBUGCOUT(gDebugFlag1, " end at wrap-around\n")
3450 }
3451 }
3452 }
3453#ifdef DEBUG_PED_STRUCTURES
3454 if (gDebugFlag1) {
3455 std::cout << " normalizedLanes=" << normalizedLanes.size() << " waIndices:\n";
3456 for (int i = 0; i < (int)waIndices.size(); ++i) {
3457 std::cout << " " << waIndices[i].first << ", " << waIndices[i].second << "\n";
3458 }
3459 }
3460#endif
3461 // build walking areas connected to a sidewalk
3462 for (int i = 0; i < (int)waIndices.size(); ++i) {
3463 const bool buildExtensions = waIndices[i].second != (int)normalizedLanes.size();
3464 int startIdx = waIndices[i].first;
3465 const int prev = startIdx > 0 ? startIdx - 1 : (int)normalizedLanes.size() - 1;
3466 const int count = waIndices[i].second;
3467 const int end = (startIdx + count) % normalizedLanes.size();
3468 int lastIdx = (startIdx + count - 1) % normalizedLanes.size();
3469
3470 WalkingArea wa(":" + getID() + "_w" + toString(index++), 1);
3471 DEBUGCOUT(gDebugFlag1, "build walkingArea " << wa.id << " start=" << startIdx << " end=" << end << " count=" << count << " prev=" << prev << ":\n")
3472 double endCrossingWidth = 0;
3473 double startCrossingWidth = 0;
3474 PositionVector endCrossingShape;
3475 PositionVector startCrossingShape;
3476 // check for connected crossings
3477 bool connectsCrossing = false;
3478 bool crossingNearSidewalk = false;
3479 int numCrossings = 0;
3480 std::vector<Position> connectedPoints;
3481 for (auto c : getCrossings()) {
3482 DEBUGCOUT(gDebugFlag1, " crossing=" << c->id << " sortedEdges=" << toString(c->edges) << "\n")
3483 if (c->edges.back() == normalizedLanes[end].first
3484 && (normalizedLanes[end].second.permissions & SVC_PEDESTRIAN) == 0) {
3485 // crossing ends
3486 if (c->nextWalkingArea != "") {
3487 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' targets '%' and '%'."),
3488 getID(), c->id, c->nextWalkingArea, wa.id);
3489 c->valid = false;
3490 }
3491 c->nextWalkingArea = wa.id;
3492 wa.prevCrossings.push_back(c->id);
3493 if ((int)c->edges.size() < wa.minPrevCrossingEdges) {
3494 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3495 endCrossingWidth = c->width;
3496 endCrossingShape = c->shape;
3497 wa.width = MAX2(wa.width, endCrossingWidth);
3498 connectsCrossing = true;
3499 connectedPoints.push_back(c->shape[-1]);
3500 wa.minPrevCrossingEdges = (int)c->edges.size();
3501 numCrossings++;
3502 if (normalizedLanes[lastIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < endCrossingWidth) {
3503 crossingNearSidewalk = true;
3504 DEBUGCOUT(gDebugFlag1, " nearSidewalk\n")
3505 }
3506 }
3507 DEBUGCOUT(gDebugFlag1, " crossing " << c->id << " ends\n")
3508 }
3509 if (c->edges.front() == normalizedLanes[prev].first
3510 && (normalizedLanes[prev].second.permissions & SVC_PEDESTRIAN) == 0) {
3511 // crossing starts
3512 if (c->prevWalkingArea != "") {
3513 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' is targeted by '%' and '%'."),
3514 getID(), c->id, c->prevWalkingArea, wa.id);
3515 c->valid = false;
3516 }
3517 if (c->valid && std::find(wa.prevCrossings.begin(), wa.prevCrossings.end(), c->id) != wa.prevCrossings.end()) {
3518 WRITE_WARNINGF(TL("Invalid pedestrian topology at junction '%'; crossing '%' starts and ends at walkingarea '%'."),
3519 getID(), c->id, wa.id);
3520 c->valid = false;
3521 }
3522 c->prevWalkingArea = wa.id;
3523 wa.nextCrossings.push_back(c->id);
3524 if ((int)c->edges.size() < wa.minNextCrossingEdges) {
3525 // if there are multiple crossings, use the shape of the one that crosses fewer edges
3526 startCrossingWidth = c->width;
3527 startCrossingShape = c->shape;
3528 wa.width = MAX2(wa.width, startCrossingWidth);
3529 connectsCrossing = true;
3530 connectedPoints.push_back(c->shape[0]);
3531 wa.minNextCrossingEdges = (int)c->edges.size();
3532 numCrossings++;
3533 if (normalizedLanes[startIdx].second.shape[0].distanceTo2D(connectedPoints.back()) < startCrossingWidth) {
3534 crossingNearSidewalk = true;
3535 DEBUGCOUT(gDebugFlag1, " nearSidewalk\n")
3536 }
3537 }
3538 DEBUGCOUT(gDebugFlag1, " crossing " << c->id << " starts\n")
3539 }
3540 DEBUGCOUT(gDebugFlag1, " check connections to crossing " << c->id
3541 << " cFront=" << c->edges.front()->getID() << " cBack=" << c->edges.back()->getID()
3542 << " wEnd=" << normalizedLanes[end].first->getID() << " wStart=" << normalizedLanes[startIdx].first->getID()
3543 << " wStartPrev=" << normalizedLanes[prev].first->getID()
3544 << "\n")
3545 }
3546 if (count < 2 && !connectsCrossing) {
3547 // not relevant for walking
3548 DEBUGCOUT(gDebugFlag1, " not relevant for walking: count=" << count << " connectsCrossing=" << connectsCrossing << "\n")
3549 continue;
3550 }
3551 // build shape and connections
3552 std::set<const NBEdge*, ComparatorIdLess>& connected = wa.refEdges;
3553 for (int j = 0; j < count; ++j) {
3554 const int nlI = (startIdx + j) % normalizedLanes.size();
3555 NBEdge* edge = normalizedLanes[nlI].first;
3556 NBEdge::Lane l = normalizedLanes[nlI].second;
3557 wa.width = MAX2(wa.width, l.width);
3558 if (connected.count(edge) == 0) {
3559 if (edge->getFromNode() == this) {
3560 wa.nextSidewalks.push_back(edge->getSidewalkID());
3561 connectedPoints.push_back(edge->getLaneShape(0)[0]);
3562 } else {
3563 wa.prevSidewalks.push_back(edge->getSidewalkID());
3564 connectedPoints.push_back(edge->getLaneShape(0)[-1]);
3565 }
3566 DEBUGCOUT(gDebugFlag1, " connectedEdge=" << edge->getID() << " connectedPoint=" << connectedPoints.back() << "\n")
3567 connected.insert(edge);
3568 }
3569 l.shape.move2side(-l.width / 2);
3571 l.shape.move2side(l.width);
3572 wa.shape.push_back(l.shape[0]);
3573 }
3574 if (buildExtensions) {
3575 // extension at starting crossing
3576 if (startCrossingShape.size() > 0) {
3577 startCrossingShape.move2side(startCrossingWidth / 2);
3578 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // right corner
3579 startCrossingShape.move2side(-startCrossingWidth);
3580 wa.shape.push_front_noDoublePos(startCrossingShape[0]); // left corner goes first
3581 DEBUGCOUT(gDebugFlag1, " extension at startCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
3582 }
3583 // extension at ending crossing
3584 if (endCrossingShape.size() > 0) {
3585 endCrossingShape.move2side(endCrossingWidth / 2);
3586 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3587 endCrossingShape.move2side(-endCrossingWidth);
3588 wa.shape.push_back_noDoublePos(endCrossingShape[-1]);
3589 DEBUGCOUT(gDebugFlag1, " extension at endCrossingShape=" << endCrossingShape << " waShape=" << wa.shape << "\n")
3590 }
3591 }
3592 if (connected.size() == 2 && !connectsCrossing && wa.nextSidewalks.size() == 1 && wa.prevSidewalks.size() == 1
3593 && normalizedLanes.size() == 2) {
3594 // do not build a walkingArea since a normal connection exists
3595 const NBEdge* e1 = *connected.begin();
3596 const NBEdge* e2 = *(++connected.begin());
3597 if (e1->hasConnectionTo(e2, 0, 0) || e2->hasConnectionTo(e1, 0, 0)) {
3598 DEBUGCOUT(gDebugFlag1, " not building a walkingarea since normal connections exist\n")
3599 continue;
3600 }
3601 }
3602 if (count == (int)normalizedLanes.size()) {
3603 // junction is covered by the whole walkingarea
3604 wa.shape = myPoly;
3605 // increase walking width if the walkingare is wider than a single lane
3606 for (const NBEdge* in : myIncomingEdges) {
3607 for (const NBEdge* out : myOutgoingEdges) {
3608 if (in->getFromNode() == out->getToNode() && in->getInnerGeometry().reverse() == out->getInnerGeometry()
3609 && (in->getPermissions() & SVC_PEDESTRIAN)
3610 && (out->getPermissions() & SVC_PEDESTRIAN)) {
3611 // doesn't catch all cases but probably most
3612 wa.width = MAX2(wa.width, in->getTotalWidth() + out->getTotalWidth());
3613 }
3614 }
3615 }
3616 } else if (cornerDetail > 0) {
3617 // build smooth inner curve (optional)
3618 int smoothEnd = end;
3619 int smoothPrev = prev;
3620 // extend to green verge
3621 if (endCrossingWidth > 0 && normalizedLanes[smoothEnd].second.permissions == 0) {
3622 smoothEnd = (smoothEnd + 1) % normalizedLanes.size();
3623 }
3624 if (startCrossingWidth > 0 && normalizedLanes[smoothPrev].second.permissions == 0) {
3625 if (smoothPrev == 0) {
3626 smoothPrev = (int)normalizedLanes.size() - 1;
3627 } else {
3628 smoothPrev--;
3629 }
3630 }
3631 PositionVector begShape = normalizedLanes[smoothEnd].second.shape;
3632 begShape = begShape.reverse();
3633 double shiftBegExtra = 0;
3634 double shiftEndExtra = 0;
3635 if (lastIdx == startIdx) {
3636 lastIdx = (startIdx + 1) % normalizedLanes.size();
3637 DEBUGCOUT(gDebugFlag1, " new lastIdx=" << lastIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
3638 if (normalizedLanes[startIdx].first == normalizedLanes[lastIdx].first) {
3639 lastIdx = startIdx;
3640 startIdx--;
3641 if (startIdx < 0) {
3642 startIdx = (int)normalizedLanes.size() - 1;
3643 }
3644 DEBUGCOUT(gDebugFlag1, " new startIdx=" << startIdx << " startEdge=" << normalizedLanes[startIdx].first->getID() << " lastEdge=" << normalizedLanes[lastIdx].first->getID() << "\n")
3645 shiftEndExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
3646 } else {
3647 shiftBegExtra += OptionsCont::getOptions().getFloat("default.sidewalk-width");
3648 }
3649 }
3650 PositionVector begShapeOuter = normalizedLanes[lastIdx].second.shape;
3651 begShapeOuter = begShapeOuter.reverse();
3652 //begShape.extrapolate(endCrossingWidth);
3653 begShape.move2side(normalizedLanes[smoothEnd].second.width / 2);
3654 begShapeOuter.move2side(normalizedLanes[lastIdx].second.width / 2 + shiftBegExtra);
3655 PositionVector endShape = normalizedLanes[smoothPrev].second.shape;
3656 PositionVector endShapeOuter = normalizedLanes[startIdx].second.shape;;
3657 endShape.move2side(normalizedLanes[smoothPrev].second.width / 2);
3658 endShapeOuter.move2side(normalizedLanes[startIdx].second.width / 2 + shiftEndExtra);
3659 //endShape.extrapolate(startCrossingWidth);
3660 PositionVector curve;
3661 if (count != (int)normalizedLanes.size() || count == 2) {
3662 const double angle = GeomHelper::angleDiff(begShape.angleAt2D(-2), endShape.angleAt2D(0));
3663 if (count == 1 && angle > 0 && crossingNearSidewalk && numCrossings < 2) {
3664 // do not build smooth shape for an unconnected left turn
3665 // (the walkingArea would get bigger without a reason to
3666 // walk there)
3667 } else if ((normalizedLanes[smoothEnd].first->getPermissions() & normalizedLanes[smoothPrev].first->getPermissions() &
3668 ~(SVC_PEDESTRIAN | SVC_RAIL_CLASSES)) != 0) {
3669 DEBUGCOUT(gDebugFlag1, " traffic curve\n")
3670 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, gDebugFlag1 ? this : nullptr);
3671 if (curve.length2D() - begShape.back().distanceTo2D(endShape.front()) > 5) {
3672 DEBUGCOUT(gDebugFlag1, " reduceBulge directLength=" << begShape.back().distanceTo2D(endShape.front())
3673 << " curveLength=" << curve.length2D()
3674 << " delta=" << curve.length2D() - begShape.back().distanceTo2D(endShape.front())
3675 << "\n")
3676 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3677 }
3678 } else {
3679 DEBUGCOUT(gDebugFlag1, " nonTraffic curve\n")
3680 const double extend = MIN2(10.0, begShape.back().distanceTo2D(endShape.front()) / 2);
3681 curve = computeSmoothShape(begShape, endShape, cornerDetail + 2, false, extend, extend, nullptr, FOUR_CONTROL_POINTS);
3682 }
3683 if (curve.size() > 2) {
3684 curve.erase(curve.begin());
3685 curve.pop_back();
3686 if (endCrossingWidth > 0) {
3687 wa.shape.pop_back();
3688 }
3689 if (startCrossingWidth > 0) {
3690 wa.shape.erase(wa.shape.begin());
3691 }
3692 if (count == (int)normalizedLanes.size()) {
3693 curve = curve.reverse();
3694 }
3695 wa.shape.append(curve, 0);
3696 }
3697 DEBUGCOUT(gDebugFlag1, " end=" << smoothEnd << " prev=" << smoothPrev
3698 << " endCrossingWidth=" << endCrossingWidth << " startCrossingWidth=" << startCrossingWidth
3699 << " begShape=" << begShape << " endShape=" << endShape << " smooth curve=" << curve
3700 << " begShapeOuter=" << begShapeOuter << " endShapeOuter=" << endShapeOuter
3701 << " waShape=" << wa.shape
3702 << "\n")
3703 }
3704 if (curve.size() > 2 && (count == 2 || (count == 1 && numCrossings > 0))) {
3705 const double innerDist = begShape.back().distanceTo2D(endShape[0]);
3706 const double outerDist = begShapeOuter.back().distanceTo2D(endShapeOuter[0]);
3707 DEBUGCOUT(gDebugFlag1, " innerDist=" << innerDist << " outerDist=" << outerDist << "\n")
3708 if (outerDist > innerDist) {
3709 // we also need a rounded outer curve (unless we have only a single walkingarea)
3710 const double extend = MIN2(10.0, begShapeOuter.back().distanceTo2D(endShapeOuter.front()) / 2);
3711 curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, extend, extend, nullptr);
3712 if (curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front()) > 5) {
3713 DEBUGCOUT(gDebugFlag1, " reduceBulge directLength=" << begShapeOuter.back().distanceTo2D(endShapeOuter.front())
3714 << " curveLength=" << curve.length2D()
3715 << " delta=" << curve.length2D() - begShapeOuter.back().distanceTo2D(endShapeOuter.front())
3716 << "\n")
3717 curve = computeSmoothShape(begShapeOuter, endShapeOuter, cornerDetail + 2, false, 25, 25, nullptr, AVOID_WIDE_LEFT_TURN | AVOID_INTERSECTING_LEFT_TURNS);
3718 }
3719 curve = curve.reverse();
3720 // keep the points in case of extraShift
3721 if (shiftBegExtra != 0) {
3722 curve.push_front_noDoublePos(wa.shape[1]);
3723 curve.push_back_noDoublePos(wa.shape[2]);
3724 } else if (shiftEndExtra != 0) {
3725 curve.push_back_noDoublePos(wa.shape[1]);
3726 curve.push_back_noDoublePos(wa.shape[2]);
3727 }
3728 DEBUGCOUT(gDebugFlag1, " outerCurveRaw=" << curve << " wa1=" << wa.shape[1] << " wa2=" << wa.shape[2] << "\n")
3729 wa.shape.erase(wa.shape.begin() + 1, wa.shape.begin() + 3);
3730 wa.shape.insert(wa.shape.begin() + 1, curve.begin(), curve.end());
3731 DEBUGCOUT(gDebugFlag1, " outerCurve=" << curve << "\n")
3732 }
3733 }
3734 }
3735 // apply custom shapes
3736 if (myWalkingAreaCustomShapes.size() > 0) {
3737 for (auto wacs : myWalkingAreaCustomShapes) {
3738 // every edge in wasc.edges must be part of connected
3739 if ((wacs.shape.size() != 0 || wacs.width != NBEdge::UNSPECIFIED_WIDTH) && includes(connected, wacs.edges)) {
3740 if (wacs.shape.size() != 0) {
3741 wa.shape = wacs.shape;
3742 }
3743 if (wacs.width != NBEdge::UNSPECIFIED_WIDTH) {
3744 wa.width = wacs.width;
3745 }
3746 wa.hasCustomShape = true;
3747 }
3748 }
3749 }
3750 // determine length (average of all possible connections)
3751 double lengthSum = 0;
3752 int combinations = 0;
3753 for (std::vector<Position>::const_iterator it1 = connectedPoints.begin(); it1 != connectedPoints.end(); ++it1) {
3754 for (std::vector<Position>::const_iterator it2 = connectedPoints.begin(); it2 != connectedPoints.end(); ++it2) {
3755 const Position& p1 = *it1;
3756 const Position& p2 = *it2;
3757 if (p1 != p2) {
3758 lengthSum += p1.distanceTo2D(p2);
3759 combinations += 1;
3760 }
3761 }
3762 }
3763 DEBUGCOUT(gDebugFlag1, " combinations=" << combinations << " connectedPoints=" << connectedPoints << "\n")
3764 wa.length = POSITION_EPS;
3765 if (combinations > 0) {
3766 wa.length = MAX2(POSITION_EPS, lengthSum / combinations);
3767 }
3768 myWalkingAreas.push_back(wa);
3769 }
3770 // build walkingAreas between split crossings
3771 std::vector<Crossing*> validCrossings = getCrossings();
3772 for (std::vector<Crossing*>::iterator it = validCrossings.begin(); it != validCrossings.end(); ++it) {
3773 Crossing& prev = **it;
3774 Crossing& next = (it != validCrossings.begin() ? **(it - 1) :** (validCrossings.end() - 1));
3775 DEBUGCOUT(gDebugFlag1, " checkIntermediate: prev=" << prev.id << " next=" << next.id << " prev.nextWA=" << prev.nextWalkingArea << " next.prevWA=" << next.prevWalkingArea << "\n")
3776 if (prev.nextWalkingArea == "") {
3777 if (next.prevWalkingArea != "" || &prev == &next) {
3778 WRITE_WARNINGF(TL("Invalid pedestrian topology: crossing '%' across [%] has no target."), prev.id, toString(prev.edges));
3779 prev.valid = false;
3780 continue;
3781 }
3782 WalkingArea wa(":" + getID() + "_w" + toString(index++), prev.width);
3783 prev.nextWalkingArea = wa.id;
3784 wa.nextCrossings.push_back(next.id);
3785 next.prevWalkingArea = wa.id;
3786 // back of previous crossing
3787 PositionVector tmp = prev.shape;
3788 tmp.move2side(-prev.width / 2);
3789 wa.shape.push_back(tmp[-1]);
3790 tmp.move2side(prev.width);
3791 wa.shape.push_back(tmp[-1]);
3792 // front of next crossing
3793 tmp = next.shape;
3794 tmp.move2side(prev.width / 2);
3795 wa.shape.push_back(tmp[0]);
3796 tmp.move2side(-prev.width);
3797 wa.shape.push_back(tmp[0]);
3798 wa.refEdges.insert(prev.edges.begin(), prev.edges.end());
3799 wa.refEdges.insert(next.edges.begin(), next.edges.end());
3800 // apply custom shapes
3801 if (myWalkingAreaCustomShapes.size() > 0) {
3802 for (auto wacs : myWalkingAreaCustomShapes) {
3803 // every edge in wacs.edges must be part of crossed
3804 if (wacs.shape.size() != 0 && wacs.edges.size() > 1 && includes(wa.refEdges, wacs.edges)) {
3805 wa.shape = wacs.shape;
3806 wa.hasCustomShape = true;
3807 }
3808 }
3809 }
3810 // length (special case)
3811 wa.length = MAX2(POSITION_EPS, prev.shape.back().distanceTo2D(next.shape.front()));
3812 myWalkingAreas.push_back(wa);
3813 DEBUGCOUT(gDebugFlag1, " build wa=" << wa.id << "\n")
3814 }
3815 }
3816}
3817
3818
3819void
3821#ifdef DEBUG_CROSSING_OUTLINE
3822 if (myCrossings.size() > 0) {
3823 std::cerr << "<add>\n";
3824 }
3825#endif
3826 std::map<std::string, PositionVector> waShapes;
3827 for (auto wa : myWalkingAreas) {
3828 waShapes[wa.id] = wa.shape;
3829 }
3830 for (auto c : getCrossings()) {
3831 PositionVector wa1 = waShapes[c->prevWalkingArea];
3832 PositionVector wa2 = waShapes[c->nextWalkingArea];
3833 if (wa1.empty() || wa2.empty()) {
3834 continue;
3835 }
3836 wa1.closePolygon();
3837 wa2.closePolygon();
3838 PositionVector side1 = c->shape;
3839 PositionVector side2 = c->shape.reverse();
3840 side1.move2side(c->width / 2);
3841 side2.move2side(c->width / 2);
3842 PositionVector side1default = side1;
3843 PositionVector side2default = side2;
3844 side1.extrapolate(POSITION_EPS);
3845 side2.extrapolate(c->width);
3846 side1 = cutAtShapes(side1, wa1, wa2, side1default);
3847 side2 = cutAtShapes(side2, wa1, wa2, side2default);
3848 PositionVector side1ex = side1;
3849 PositionVector side2ex = side2;
3850 side1ex.extrapolate(POSITION_EPS);
3851 side2ex.extrapolate(side2 == side2default ? c->width / 2 : POSITION_EPS);
3852 PositionVector side3 = cutAtShapes(wa2, side1ex, side2ex, PositionVector());
3853 PositionVector side4 = cutAtShapes(wa1, side1ex, side2ex, PositionVector());
3854 c->outlineShape = side1;
3855 c->outlineShape.append(side3, POSITION_EPS);
3856 c->outlineShape.append(side2, POSITION_EPS);
3857 c->outlineShape.append(side4, POSITION_EPS);
3858 c->outlineShape.removeDoublePoints();
3859 if (c->outlineShape.back().almostSame(c->outlineShape.front())) {
3860 c->outlineShape.pop_back();
3861 }
3862 // DEBUG
3863#ifdef DEBUG_CROSSING_OUTLINE
3864 std::cout << " side1=" << side1 << "\n side2=" << side2 << "\n side3=" << side3 << "\n side4=" << side4 << "\n";
3865 std::cerr << "<poly id=\"" << c->id << "\" shape=\"" << c->outlineShape << "\" color=\"blue\" lineWidth=\"0.2\" layer=\"100\"/>\n";
3866#endif
3867 }
3868#ifdef DEBUG_CROSSING_OUTLINE
3869 if (myCrossings.size() > 0) {
3870 std::cerr << "</add>\n";
3871 }
3872#endif
3873}
3874
3875
3877NBNode::cutAtShapes(const PositionVector& cut, const PositionVector& border1, const PositionVector& border2, const PositionVector& def) {
3878 std::vector<double> is1 = cut.intersectsAtLengths2D(border1);
3879 std::vector<double> is2 = cut.intersectsAtLengths2D(border2);
3880#ifdef DEBUG_CROSSING_OUTLINE
3881 std::cout << "is1=" << is1 << " is2=" << is2 << " cut=" << cut << " border1=" << border1 << " border2=" << border2 << "\n";
3882#endif
3883 if (is1.size() == 0 && border1.size() == 2) {
3884 const double d1 = cut.distance2D(border1.front());
3885 const double d2 = cut.distance2D(border1.back());
3886 Position closer = d1 < d2 ? border1.front() : border1.back();
3887 double nOp = cut.nearest_offset_to_point2D(closer, false);
3888#ifdef DEBUG_CROSSING_OUTLINE
3889 std::cout << " closer=" << closer << " nOp=" << nOp << "\n";
3890#endif
3891 if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
3892 is1.push_back(cut.length2D());
3893 } else {
3894 is1.push_back(nOp);
3895 }
3896 }
3897 if (is2.size() == 0 && border2.size() == 2) {
3898 const double d1 = cut.distance2D(border2.front());
3899 const double d2 = cut.distance2D(border2.back());
3900 Position closer = d1 < d2 ? border2.front() : border2.back();
3901 double nOp = cut.nearest_offset_to_point2D(closer, false);
3902 if (nOp <= 2 * POSITION_EPS && cut.back().distanceTo2D(closer) <= 2 * POSITION_EPS) {
3903 is2.push_back(cut.length2D());
3904 } else {
3905 is2.push_back(nOp);
3906 }
3907 }
3908 if (is1.size() > 0 && is2.size() > 0) {
3909 double of1 = VectorHelper<double>::maxValue(is1);
3910 double of2 = VectorHelper<double>::minValue(is2);
3911#ifdef DEBUG_CROSSING_OUTLINE
3912 std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3913#endif
3914 if (of1 > of2) {
3917#ifdef DEBUG_CROSSING_OUTLINE
3918 std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3919#endif
3920 }
3921 if (of1 > of2) {
3924#ifdef DEBUG_CROSSING_OUTLINE
3925 std::cout << " of1=" << of1 << " of2=" << of2 << "\n";
3926#endif
3927 }
3928 assert(of1 <= of2);
3929 return cut.getSubpart(of1, of2);
3930 } else {
3931 return def;
3932 }
3933}
3934
3935
3936bool
3937NBNode::includes(const std::set<const NBEdge*, ComparatorIdLess>& super,
3938 const std::set<const NBEdge*, ComparatorIdLess>& sub) {
3939 // for some reason std::include does not work reliably
3940 for (const NBEdge* e : sub) {
3941 if (super.count(const_cast<NBEdge*>(e)) == 0) {
3942 return false;
3943 }
3944 }
3945 return true;
3946}
3947
3948
3949bool
3950NBNode::crossingBetween(const NBEdge* e1, const NBEdge* e2) const {
3951 if (e1 == e2) {
3952 return false;
3953 }
3954 if (myAllEdges.size() > 3) {
3955 // pedestrian scramble
3956 return false;
3957 }
3958 for (auto c : getCrossings()) {
3959 const EdgeVector& edges = c->edges;
3960 EdgeVector::const_iterator it1 = std::find(edges.begin(), edges.end(), e1);
3961 EdgeVector::const_iterator it2 = std::find(edges.begin(), edges.end(), e2);
3962 if (it1 != edges.end() && it2 != edges.end()) {
3963 return true;
3964 }
3965 }
3966 return false;
3967}
3968
3969
3970bool
3971NBNode::alreadyConnectedPaths(const NBEdge* e1, const NBEdge* e2, double dist) const {
3972 if (e1 == e2) {
3973 return false;
3974 }
3975 if (e1->getPermissions() != SVC_PEDESTRIAN
3976 || e2->getPermissions() != SVC_PEDESTRIAN) {
3977 // no paths
3978 return false;
3979 }
3980 if (e1->getFinalLength() > dist &&
3981 e2->getFinalLength() > dist) {
3982 // too long
3983 return false;
3984 }
3985 NBNode* other1 = e1->getFromNode() == this ? e1->getToNode() : e1->getFromNode();
3986 NBNode* other2 = e2->getFromNode() == this ? e2->getToNode() : e2->getFromNode();
3987 return other1 == other2;
3988}
3989
3990
3991bool
3992NBNode::crossesFringe(const NBEdge* e1, const NBEdge* e2) const {
3994 && myIncomingEdges.size() == 1 && myOutgoingEdges.size() == 1
3995 && (e1->isTurningDirectionAt(e2) || e2->isTurningDirectionAt(e1));
3996}
3997
3998
4000NBNode::edgesBetween(const NBEdge* e1, const NBEdge* e2) const {
4001 EdgeVector result;
4002 EdgeVector::const_iterator it = std::find(myAllEdges.begin(), myAllEdges.end(), e1);
4003 assert(it != myAllEdges.end());
4005 EdgeVector::const_iterator it_end = std::find(myAllEdges.begin(), myAllEdges.end(), e2);
4006 assert(it_end != myAllEdges.end());
4007 while (it != it_end) {
4008 result.push_back(*it);
4010 }
4011 return result;
4012}
4013
4014
4015void
4016NBNode::addWalkingAreaShape(EdgeVector edges, const PositionVector& shape, double width) {
4018 wacs.edges.insert(edges.begin(), edges.end());
4019 wacs.shape = shape;
4020 wacs.width = width;
4021 myWalkingAreaCustomShapes.push_back(wacs);
4022}
4023
4024
4025bool
4029
4030bool
4031NBNode::geometryLike(const EdgeVector& incoming, const EdgeVector& outgoing) {
4032 if (incoming.size() == 1 && outgoing.size() == 1) {
4033 return true;
4034 }
4035 if (incoming.size() == 2 && outgoing.size() == 2) {
4036 // check whether the incoming and outgoing edges are pairwise (near) parallel and
4037 // thus the only cross-connections could be turn-arounds
4038 NBEdge* in0 = incoming[0];
4039 NBEdge* in1 = incoming[1];
4040 NBEdge* out0 = outgoing[0];
4041 NBEdge* out1 = outgoing[1];
4042 if ((in0->isTurningDirectionAt(out0) || in0->isTurningDirectionAt(out1))
4043 && (in1->isTurningDirectionAt(out0) || in1->isTurningDirectionAt(out1))) {
4044 return true;
4045 }
4046 if (in0->getGeometry() == in1->getGeometry() && out0->getGeometry() == out1->getGeometry()) {
4047 // overlapping edges
4048 return true;
4049 }
4050 for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4051 NBEdge* inEdge = *it;
4052 double angle0 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out0->getAngleAtNode(out0->getFromNode())));
4053 double angle1 = fabs(NBHelpers::relAngle(inEdge->getAngleAtNode(inEdge->getToNode()), out1->getAngleAtNode(out1->getFromNode())));
4054 if (MAX2(angle0, angle1) <= 160) {
4055 // neither of the outgoing edges is parallel to inEdge
4056 return false;
4057 }
4058 }
4059 return true;
4060 }
4061 return false;
4062}
4063
4064void
4070
4071bool
4073 for (NBEdge* out : myOutgoingEdges) {
4074 if (out->getJunctionPriority(this) == NBEdge::JunctionPriority::ROUNDABOUT) {
4075 return true;
4076 }
4077 }
4078 return false;
4079}
4080
4082NBNode::addCrossing(EdgeVector edges, double width, bool priority, int tlIndex, int tlIndex2,
4083 const PositionVector& customShape, bool fromSumoNet, const Parameterised* params) {
4084 Crossing* c = new Crossing(this, edges, width, priority, tlIndex, tlIndex2, customShape);
4085 if (params != nullptr) {
4086 c->updateParameters(params->getParametersMap());
4087 }
4088 myCrossings.push_back(std::unique_ptr<Crossing>(c));
4089 if (fromSumoNet) {
4091 }
4092 return c;
4093}
4094
4095
4096void
4098 EdgeSet edgeSet(edges.begin(), edges.end());
4099 for (auto it = myCrossings.begin(); it != myCrossings.end();) {
4100 EdgeSet edgeSet2((*it)->edges.begin(), (*it)->edges.end());
4101 if (edgeSet == edgeSet2) {
4102 it = myCrossings.erase(it);
4103 } else {
4104 ++it;
4105 }
4106 }
4107}
4108
4109
4111NBNode::getCrossing(const std::string& id) const {
4112 for (auto& c : myCrossings) {
4113 if (c->id == id) {
4114 return c.get();
4115 }
4116 }
4117 throw ProcessError(TLF("Request for unknown crossing '%'", id));
4118}
4119
4120
4122NBNode::getCrossing(const EdgeVector& edges, bool hardFail) const {
4123 const EdgeSet edgeSet(edges.begin(), edges.end());
4124 for (auto& crossing : myCrossings) {
4125 const EdgeSet edgeSet2(crossing->edges.begin(), crossing->edges.end());
4126 if (edgeSet == edgeSet2) {
4127 return crossing.get();
4128 }
4129 }
4130 if (!hardFail) {
4131 return nullptr;
4132 }
4133 throw ProcessError(TL("Request for unknown crossing for the given Edges"));
4134}
4135
4136
4138NBNode::getWalkingArea(const std::string& id) {
4139 for (auto& walkingArea : myWalkingAreas) {
4140 if (walkingArea.id == id) {
4141 return walkingArea;
4142 }
4143 }
4144 // not found, maybe we need to rebuild
4146 sortEdges(true);
4148 for (auto& walkingArea : myWalkingAreas) {
4149 if (walkingArea.id == id) {
4150 return walkingArea;
4151 }
4152 }
4153 if (myWalkingAreas.size() > 0) {
4154 // don't crash
4155 WRITE_WARNINGF("Could not retrieve walkingarea '%' (edge ordering changed after recompute).", id);
4156 return myWalkingAreas.front();
4157 }
4158 throw ProcessError(TLF("Request for unknown walkingarea '%'.", id));
4159}
4160
4161
4162bool
4163NBNode::setCrossingTLIndices(const std::string& tlID, int startIndex, bool ignoreCustom) {
4164 bool usedCustom = false;
4165 for (auto c : getCrossings()) {
4166 c->tlLinkIndex = startIndex++;
4167 c->tlID = tlID;
4168 if (c->customTLIndex != -1 && !ignoreCustom) {
4169 usedCustom |= (c->tlLinkIndex != c->customTLIndex);
4170 c->tlLinkIndex = c->customTLIndex;
4171 }
4172 if (c->customTLIndex2 != -1 && !ignoreCustom) {
4173 usedCustom = true;
4174 c->tlLinkIndex2 = c->customTLIndex2;
4175 }
4176 }
4177 return usedCustom;
4178}
4179
4180
4181int
4183 if (myRequest == nullptr) {
4184 // could be an uncontrolled type
4185 int result = 0;
4186 for (const NBEdge* const edge : myIncomingEdges) {
4187 result += (int)edge->getConnections().size();
4188 }
4189 return result;
4190 } else {
4191 return myRequest->getSizes().second;
4192 }
4193}
4194
4195
4196int
4198 int result = 0;
4199 for (const NBEdge* const e : myIncomingEdges) {
4200 for (const NBEdge::Connection& cand : e->getConnections()) {
4201 if (e == from && cand.fromLane == con.fromLane && cand.toLane == con.toLane && cand.toEdge == con.toEdge) {
4202 return result;
4203 }
4204 result++;
4205 }
4206 }
4207 return -1;
4208}
4209
4210
4213 /* Conceptually, the center point would be identical with myPosition.
4214 * However, if the shape is influenced by custom geometry endpoints of the adjoining edges,
4215 * myPosition may fall outside the shape. In this case it is better to use
4216 * the center of the shape
4217 **/
4218 PositionVector tmp = myPoly;
4219 tmp.closePolygon();
4220 //std::cout << getID() << " around=" << tmp.around(myPosition) << " dist=" << tmp.distance2D(myPosition) << "\n";
4221 if (tmp.size() < 3 || tmp.around(myPosition) || tmp.distance2D(myPosition) < POSITION_EPS) {
4222 return myPosition;
4223 }
4224 return myPoly.getPolygonCenter();
4225}
4226
4227
4230 EdgeVector result = myAllEdges;
4231#ifdef DEBUG_PED_STRUCTURES
4232 if (gDebugFlag1) {
4233 std::cout << " angles:\n";
4234 for (EdgeVector::const_iterator it = result.begin(); it != result.end(); ++it) {
4235 std::cout << " edge=" << (*it)->getID() << " edgeAngle=" << (*it)->getAngleAtNode(this) << " angleToShape=" << (*it)->getAngleAtNodeToCenter(this) << "\n";
4236 }
4237 std::cout << " allEdges before: " << toString(result) << "\n";
4238 }
4239#endif
4240 sort(result.begin(), result.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4241 // let the first edge in myAllEdges remain the first
4242 DEBUGCOUT(gDebugFlag1, " allEdges sorted: " << toString(result) << "\n")
4243 rotate(result.begin(), std::find(result.begin(), result.end(), *myAllEdges.begin()), result.end());
4244 DEBUGCOUT(gDebugFlag1, " allEdges rotated: " << toString(result) << "\n")
4245 return result;
4246}
4247
4248
4249void
4251 // simple case: edges with LaneSpreadFunction::CENTER and a (possible) turndirection at the same node
4252 bool haveModifications = false;
4253 for (EdgeVector::iterator it = myIncomingEdges.begin(); it != myIncomingEdges.end(); it++) {
4254 NBEdge* edge = *it;
4255 NBEdge* turnDest = edge->getTurnDestination(true);
4256 if (turnDest != nullptr) {
4257 haveModifications |= edge->shiftPositionAtNode(this, turnDest);
4258 haveModifications |= turnDest->shiftPositionAtNode(this, edge);
4259 }
4260 }
4261 if (haveModifications) {
4263 }
4264 // @todo: edges in the same direction with sharp angles starting/ending at the same position
4265}
4266
4267
4268bool
4274
4275
4276bool
4277NBNode::extraConflict(int index, int foeIndex) const {
4279 if (def->extraConflict(index, foeIndex)) {
4280 return true;
4281 }
4282 }
4283 return false;
4284}
4285
4286
4287void
4288NBNode::sortEdges(bool useNodeShape) {
4289 if (myAllEdges.size() == 0) {
4290 return;
4291 }
4292 EdgeVector allEdgesOriginal = myAllEdges;
4293 EdgeVector& allEdges = myAllEdges;
4294 EdgeVector& incoming = myIncomingEdges;
4295 EdgeVector& outgoing = myOutgoingEdges;
4296
4297 // sort the edges by angle (this is the canonical sorting)
4298 std::sort(allEdges.begin(), allEdges.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4299 std::sort(incoming.begin(), incoming.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4300 std::sort(outgoing.begin(), outgoing.end(), NBNodesEdgesSorter::edge_by_junction_angle_sorter(this));
4301 std::vector<NBEdge*>::iterator j;
4302 for (j = allEdges.begin(); j != allEdges.end() - 1 && j != allEdges.end(); ++j) {
4304 }
4305 if (allEdges.size() > 1 && j != allEdges.end()) {
4306 NBNodesEdgesSorter::swapWhenReversed(this, allEdges.end() - 1, allEdges.begin());
4307 }
4308
4309 // sort again using additional geometry information
4310 NBEdge* firstOfAll = allEdges.front();
4311 NBEdge* firstOfIncoming = incoming.size() > 0 ? incoming.front() : 0;
4312 NBEdge* firstOfOutgoing = outgoing.size() > 0 ? outgoing.front() : 0;
4313 // sort by the angle between the node shape center and the point where the edge meets the node shape
4314 std::sort(allEdges.begin(), allEdges.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4315 std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4316 std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(this));
4317 // let the first edge remain the first
4318 rotate(allEdges.begin(), std::find(allEdges.begin(), allEdges.end(), firstOfAll), allEdges.end());
4319 if (firstOfIncoming != nullptr) {
4320 rotate(incoming.begin(), std::find(incoming.begin(), incoming.end(), firstOfIncoming), incoming.end());
4321 }
4322 if (firstOfOutgoing != nullptr) {
4323 rotate(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), firstOfOutgoing), outgoing.end());
4324 }
4325#ifdef DEBUG_EDGE_SORTING
4326 if (DEBUGCOND) {
4327 std::cout << "sortedEdges (useNodeShape=" << useNodeShape << "):\n";
4328 for (NBEdge* e : allEdges) {
4329 std::cout << " " << e->getID()
4330 << " angleToCenter=" << e->getAngleAtNodeToCenter(this)
4331 << " junctionAngle=" << e->getAngleAtNode(this) << "\n";
4332 }
4333 }
4334#endif
4335
4336 // fixing some pathological all edges orderings
4337 // 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'
4338 if (incoming.size() == outgoing.size() && incoming.front() == allEdges.front()) {
4339 std::vector<NBEdge*>::const_iterator in, out;
4340 std::vector<NBEdge*> allTmp;
4341 for (in = incoming.begin(), out = outgoing.begin(); in != incoming.end(); ++in, ++out) {
4342 if ((*in)->isTurningDirectionAt(*out)) {
4343 allTmp.push_back(*in);
4344 allTmp.push_back(*out);
4345 } else {
4346 break;
4347 }
4348 }
4349 if (allTmp.size() == allEdges.size()) {
4350 allEdges = allTmp;
4351 }
4352 }
4353 // sort the crossings
4354 std::sort(myCrossings.begin(), myCrossings.end(), NBNodesEdgesSorter::crossing_by_junction_angle_sorter(this, allEdges));
4355 //if (crossings.size() > 0) {
4356 // std::cout << " crossings at " << getID() << "\n";
4357 // for (std::vector<NBNode::Crossing*>::iterator it = crossings.begin(); it != crossings.end(); ++it) {
4358 // std::cout << " " << toString((*it)->edges) << "\n";
4359 // }
4360 //}
4361
4362 if (useNodeShape && myAllEdges != allEdgesOriginal) {
4363 // sorting order changed after node shape was computed.
4364 computeNodeShape(-1);
4365 for (NBEdge* e : myAllEdges) {
4366 e->computeEdgeShape();
4367 }
4368 }
4369}
4370
4371std::vector<std::pair<Position, std::string> >
4373 // using a set would be nicer but we want to have some slack in position identification
4374 std::vector<std::pair<Position, std::string> >result;
4375 for (NBEdge* e : myAllEdges) {
4376 Position pos = this == e->getFromNode() ? e->getGeometry().front() : e->getGeometry().back();
4377 const std::string origID = e->getParameter(this == e->getFromNode() ? "origFrom" : "origTo");
4378 bool unique = true;
4379 for (const auto& pair : result) {
4380 if (pos.almostSame(pair.first) || (origID != "" && pair.second == origID)) {
4381 unique = false;
4382 break;
4383 }
4384 }
4385 if (unique) {
4386 result.push_back(std::make_pair(pos, origID));
4387 }
4388 }
4389 return result;
4390}
4391
4392
4393/****************************************************************************/
#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:2136
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:2498
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:1994
void addWalkingAreaShape(EdgeVector edges, const PositionVector &shape, double width)
add custom shape for walkingArea
Definition NBNode.cpp:4016
void avoidOverlap()
fix overlap
Definition NBNode.cpp:4250
void removeEdge(NBEdge *edge, bool removeFromConnections=true)
Removes edge from this node and optionally removes connections as well.
Definition NBNode.cpp:2067
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:4212
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:2144
void removeCrossing(const EdgeVector &edges)
remove a pedestrian crossing from this node (identified by its edges)
Definition NBNode.cpp:4097
NBEdge * getNextCompatibleOutgoing(const NBEdge *incoming, SVCPermissions vehPerm, EdgeVector::const_iterator start, bool clockwise) const
Definition NBNode.cpp:2426
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:3274
SVCPermissions findToLaneForPermissions(NBEdge *currentOutgoing, int fromLane, NBEdge *incoming, SVCPermissions unsatisfied)
helper function to add connections for unsatisfied modes
Definition NBNode.cpp:1721
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:4082
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:2583
int getConnectionIndex(const NBEdge *from, const NBEdge::Connection &con) const
return the index of the given connection
Definition NBNode.cpp:4197
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:4182
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:4111
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:3038
void recheckVClassConnections(NBEdge *currentOutgoing)
ensure connectivity for all vClasses
Definition NBNode.cpp:1540
bool zipperConflict(const NBEdge *incoming, const NBEdge *outgoing, int fromLane, int toLane) const
Definition NBNode.cpp:2621
void buildCrossingsAndWalkingAreas()
build crossings, and walkingareas. Also removes invalid loaded crossings if wished
Definition NBNode.cpp:3049
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:2637
static bool isExplicitRailNoBidi(const NBEdge *incoming, const NBEdge *outgoing)
detect explict rail turns with potential geometry problem
Definition NBNode.cpp:2571
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:1095
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:2789
void computeNodeShape(double mismatchThreshold)
Compute the junction shape for this node.
Definition NBNode.cpp:1195
void buildWalkingAreas(int cornerDetail, double joinMinDist)
build pedestrian walking areas and set connections from/to walkingAreas
Definition NBNode.cpp:3346
void remapRemoved(NBTrafficLightLogicCont &tc, NBEdge *removed, const EdgeVector &incoming, const EdgeVector &outgoing)
remap removed
Definition NBNode.cpp:2345
int buildCrossings()
build pedestrian crossings
Definition NBNode.cpp:3156
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:1711
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:3126
void computeLogic(const NBEdgeCont &ec)
computes the node's type, logic and traffic light
Definition NBNode.cpp:1056
void invalidateIncomingConnections(bool reallowSetting=false)
invalidate incoming connections
Definition NBNode.cpp:2128
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:1031
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:2167
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:4277
std::vector< std::pair< Position, std::string > > getEndPoints() const
return list of unique endpoint coordinates of all edges at this node
Definition NBNode.cpp:4372
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:2183
std::vector< std::pair< NBEdge *, NBEdge * > > getEdgesToJoin() const
get edges to join
Definition NBNode.cpp:2747
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:2942
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:2103
static void initRailSignalClasses(const NBNodeCont &nc)
initialize signalized rail classes
Definition NBNode.cpp:2649
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:1607
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:3877
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:2328
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:3971
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:2162
bool hasConflict() const
whether there are conflicting streams of traffic at this node
Definition NBNode.cpp:1160
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:1921
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:2234
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:1852
EdgeVector myAllEdges
Vector of incoming and outgoing edges.
Definition NBNode.h:951
void computeKeepClear()
compute keepClear status for all connections
Definition NBNode.cpp:1102
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:4288
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:3937
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:2254
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:2309
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:3021
void setRoundabout()
update the type of this node as a roundabout
Definition NBNode.cpp:4065
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:2245
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:3098
void addSortedLinkFoes(const NBConnection &mayDrive, const NBConnection &mustStop)
add shorted link FOES
Definition NBNode.cpp:2024
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:1888
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:2263
void discardAllCrossings(bool rejectAll)
discard all current (and optionally future) crossings
Definition NBNode.cpp:3116
bool hasOutgoing(const NBEdge *const e) const
Returns whether the given edge starts at this node.
Definition NBNode.cpp:2000
bool writeLogic(OutputDevice &into) const
writes the XML-representation of the logic as a bitset-logic XML representation
Definition NBNode.cpp:1131
EdgeVector getPassengerEdges(bool incoming) const
return edges that permit passengers (either incoming or outgoing)
Definition NBNode.cpp:2487
NBEdge * getPossiblySplittedOutgoing(const std::string &edgeid)
get possibly splitted outgoing edge
Definition NBNode.cpp:2054
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:2451
void removeJoinedTrafficLights()
remove all traffic light definitions that are part of a joined tls
Definition NBNode.cpp:1041
bool crossingBetween(const NBEdge *e1, const NBEdge *e2) const
return true if the given edges are connected by a crossing
Definition NBNode.cpp:3950
bool isDistrict() const
check if node is a district
Definition NBNode.cpp:2829
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:1234
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:1151
void buildCrossingOutlines()
build crossing outlines after walkingareas are finished
Definition NBNode.cpp:3820
static bool isLongEnough(NBEdge *out, double minLength)
check if is long enough
Definition NBNode.cpp:1813
const PositionVector & getShape() const
retrieve the junction shape
Definition NBNode.cpp:2783
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:1185
int addedLanesRight(NBEdge *out, int addedLanes) const
check whether this edge has extra lanes on the right side
Definition NBNode.cpp:1759
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:4163
bool checkIsRemovable() const
check if node is removable
Definition NBNode.cpp:2664
bool isRoundabout() const
return whether this node is part of a roundabout
Definition NBNode.cpp:4072
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:2670
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:2338
void removeDoubleEdges()
remove duble edges
Definition NBNode.cpp:1956
bool avoidConfict(NBEdge *incoming, NBEdge *currentOutgoing, SVCPermissions svcSpecial, LinkDirection dir, int i)
helper function for recheckSpecialConnections
Definition NBNode.cpp:1678
double buildInnerEdges()
build internal lanes, pedestrian crossings and walking areas
Definition NBNode.cpp:3132
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:2801
bool crossesFringe(const NBEdge *e1, const NBEdge *e2) const
return true if the given sidewalks are separated by a fringe road
Definition NBNode.cpp:3992
void getEdgesThatApproach(NBEdge *currentOutgoing, EdgeVector &approaching)
returns a list of edges which are connected to the given outgoing edge
Definition NBNode.cpp:1830
EdgeVector getEdgesSortedByAngleAtNodeCenter() const
returns the list of all edges sorted clockwise by getAngleAtNodeToCenter
Definition NBNode.cpp:4229
EdgeVector edgesBetween(const NBEdge *e1, const NBEdge *e2) const
return all edges that lie clockwise between the given edges
Definition NBNode.cpp:4000
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:2041
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:4026
bool isNearDistrict() const
@chech if node is near district
Definition NBNode.cpp:2812
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:4138
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:2835
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:1141
NBEdge * getOppositeIncoming(NBEdge *e) const
returns the opposite incoming edge of certain edge
Definition NBNode.cpp:2006
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