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