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