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