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