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