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