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