Eclipse SUMO - Simulation of Urban MObility
NBAlgorithms.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2012-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 /****************************************************************************/
19 // Algorithms for network computation
20 /****************************************************************************/
21 #include <config.h>
22 
23 #include <sstream>
24 #include <iostream>
25 #include <cassert>
26 #include <algorithm>
28 #include <utils/common/ToString.h>
30 #include "NBEdge.h"
31 #include "NBOwnTLDef.h"
33 #include "NBNodeCont.h"
34 #include "NBTypeCont.h"
35 #include "NBNode.h"
36 #include "NBAlgorithms.h"
37 
38 
39 //#define DEBUG_SETPRIORITIES
40 //#define DEBUG_TURNAROUNDS
41 #define DEBUGCOND (n.getID() == "C")
42 //#define DEBUGCOND2(obj) (obj->getID() == "")
43 #define DEBUGCOND2(obj) (true)
44 
45 // ===========================================================================
46 // method definitions
47 // ===========================================================================
48 // ---------------------------------------------------------------------------
49 // NBTurningDirectionsComputer
50 // ---------------------------------------------------------------------------
51 void
53  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
54  computeTurnDirectionsForNode(i->second, warn);
55  }
56 }
57 
58 void
60  const std::vector<NBEdge*>& incoming = node->getIncomingEdges();
61  const std::vector<NBEdge*>& outgoing = node->getOutgoingEdges();
62  // reset turning directions since this may be called multiple times
63  for (std::vector<NBEdge*>::const_iterator k = incoming.begin(); k != incoming.end(); ++k) {
64  (*k)->setTurningDestination(nullptr);
65  }
66  std::vector<Combination> combinations;
67  const bool geometryLike = node->geometryLike();
68  for (NBEdge* outedge : outgoing) {
69  for (NBEdge* e : incoming) {
70  // @todo: check whether NBHelpers::relAngle is properly defined and whether it should really be used, here
71  const double signedAngle = NBHelpers::normRelAngle(e->getAngleAtNode(node), outedge->getAngleAtNode(node));
72  if (signedAngle > 0 && signedAngle < 177 && e->getGeometry().back().distanceTo2D(outedge->getGeometry().front()) < POSITION_EPS) {
73  // backwards curving edges can only be turnaround when there are
74  // non-default endpoints
75 #ifdef DEBUG_TURNAROUNDS
76  if (DEBUGCOND2(node)) {
77  std::cout << "incoming=" << e->getID() << " outgoing=" << outedge->getID() << " signedAngle=" << signedAngle << " skipped\n";
78  }
79 #endif
80  continue;
81  }
82  double angle = fabs(signedAngle);
83 #ifdef DEBUG_TURNAROUNDS
84  if (DEBUGCOND2(node)) {
85  std::cout << "incoming=" << e->getID() << " outgoing=" << outedge->getID() << " relAngle=" << NBHelpers::relAngle(e->getAngleAtNode(node), outedge->getAngleAtNode(node)) << "\n";
86  }
87 #endif
88  const bool badPermissions = ((outedge->getPermissions() & e->getPermissions() & ~SVC_PEDESTRIAN) == 0
89  && !geometryLike
90  && outedge->getPermissions() != e->getPermissions());
91  if (e->getFromNode() == outedge->getToNode()
92  && (angle > 120 || e->getFromNode()->getPosition() == e->getToNode()->getPosition())
93  && !badPermissions) {
94  // they connect the same nodes; should be the turnaround direction
95  // we'll assign a maximum number
96  //
97  // @todo: indeed, we have observed some pathological intersections
98  // see "294831560" in OSM/adlershof. Here, several edges are connecting
99  // same nodes. We have to do the angle check before...
100  //
101  // @todo: and well, there are some other as well, see plain import
102  // of delphi_muenchen (elmar), intersection "59534191". Not that it would
103  // be realistic in any means; we will warn, here.
104  angle += 360;
105  }
106  if (angle < 160) {
107  if (angle > 135) {
108  // could be a turnaround with a green median island, look at
109  // angle further away from the junction
110  const double inFA = getFarAngleAtNode(e, node);
111  const double outFA = getFarAngleAtNode(outedge, node);
112  const double signedFarAngle = NBHelpers::normRelAngle(inFA, outFA);
113 #ifdef DEBUG_TURNAROUNDS
114  if (DEBUGCOND2(node)) {
115  std::cout << " inFA=" << inFA << " outFA=" << outFA << " sFA=" << signedFarAngle << "\n";
116  }
117 #endif
118  if (signedFarAngle > -160) {
119  continue;
120  }
121  } else {
122  continue;
123  }
124  }
125  if (badPermissions) {
126  // penalty
127  angle -= 90;
128  }
129  Combination c;
130  c.from = e;
131  c.to = outedge;
132  c.angle = angle;
133  combinations.push_back(c);
134  }
135  }
136  // sort combinations so that the ones with the highest angle are at the begin
137  std::sort(combinations.begin(), combinations.end(), combination_by_angle_sorter());
138  std::set<NBEdge*> seen;
139 #ifdef DEBUG_TURNAROUNDS
140  if (DEBUGCOND2(node)) {
141  std::cout << "check combinations at " << node->getID() << "\n";
142  }
143 #endif
144  for (std::vector<Combination>::const_iterator j = combinations.begin(); j != combinations.end(); ++j) {
145 #ifdef DEBUG_TURNAROUNDS
146  if (DEBUGCOND2(node)) {
147  std::cout << " from=" << (*j).from->getID() << " to=" << (*j).to->getID() << " a=" << (*j).angle << "\n";
148  }
149 #endif
150  if (seen.find((*j).from) != seen.end() || seen.find((*j).to) != seen.end()) {
151  // do not regard already set edges
152  if ((*j).angle > 360 && warn) {
153  WRITE_WARNINGF(TL("Ambiguity in turnarounds computation at junction '%'."), node->getID());
154  //std::cout << " already seen: " << toString(seen) << "\n";
155  warn = false;
156  }
157  continue;
158  }
159  // mark as seen
160  seen.insert((*j).from);
161  seen.insert((*j).to);
162  // set turnaround information
163  bool onlyPossible = (*j).from->getConnections().size() != 0 && !(*j).from->isConnectedTo((*j).to);
164 #ifdef DEBUG_TURNAROUNDS
165  if (DEBUGCOND2(node)) {
166  std::cout << " setTurningDestination from=" << (*j).from->getID() << " to=" << (*j).to->getID() << " onlyPossible=" << onlyPossible << "\n";
167  }
168 #endif
169  (*j).from->setTurningDestination((*j).to, onlyPossible);
170  }
171 }
172 
173 
174 double
176  Position atNode;
177  Position far;
178  double angle;
179  const NBEdge* next = e;
180  if (e->getToNode() == n) {
181  // search upstream
182  atNode = e->getGeometry().back();
183  while (dist > 0) {
184  const double length = next->getGeometry().length();
185  if (dist <= length) {
186  far = next->getGeometry().positionAtOffset(length - dist);
187  break;
188  } else {
189  far = next->getGeometry().front();
190  dist -= length;
191  if (next->getToNode()->getIncomingEdges().size() == 1) {
192  next = next->getToNode()->getIncomingEdges().front();
193  } else {
194  break;
195  }
196  }
197  }
198  angle = far.angleTo2D(atNode);
199  //std::cout << " e=" << e->getID() << " n=" << n->getID() << " far=" << far << " atNode=" << atNode << " a=" << RAD2DEG(angle) << "\n";
200  } else {
201  // search downstream
202  atNode = e->getGeometry().front();
203  while (dist > 0) {
204  const double length = next->getGeometry().length();
205  if (dist <= length) {
206  far = next->getGeometry().positionAtOffset(dist);
207  break;
208  } else {
209  far = next->getGeometry().back();
210  dist -= length;
211  if (next->getToNode()->getOutgoingEdges().size() == 1) {
212  next = next->getToNode()->getOutgoingEdges().front();
213  } else {
214  break;
215  }
216  }
217  }
218  angle = atNode.angleTo2D(far);
219  //std::cout << " e=" << e->getID() << " n=" << n->getID() << " atNode=" << atNode << " far=" << far << " a=" << RAD2DEG(angle) << "\n";
220  }
221  return GeomHelper::legacyDegree(angle);
222 }
223 
224 
225 // ---------------------------------------------------------------------------
226 // NBNodesEdgesSorter
227 // ---------------------------------------------------------------------------
228 void
230  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
231  i->second->sortEdges(useNodeShape);
232  }
233 }
234 
235 
236 void
238  const std::vector<NBEdge*>::iterator& i1,
239  const std::vector<NBEdge*>::iterator& i2) {
240  NBEdge* e1 = *i1;
241  NBEdge* e2 = *i2;
242  // @todo: The difference between "isTurningDirectionAt" and "isTurnaround"
243  // is not nice. Maybe we could get rid of it if we would always mark edges
244  // as turnarounds, even if they do not have to be added, as mentioned in
245  // notes on NBTurningDirectionsComputer::computeTurnDirectionsForNode
246  if (e2->getToNode() == n && e2->isTurningDirectionAt(e1)) {
247  std::swap(*i1, *i2);
248  }
249 }
250 
251 
252 // ---------------------------------------------------------------------------
253 // NBNodeTypeComputer
254 // ---------------------------------------------------------------------------
255 void
257  validateRailCrossings(nc, tlc);
258  const OptionsCont& oc = OptionsCont::getOptions();
259  const double rightBeforeLeftSpeed = oc.getFloat("junctions.right-before-left.speed-threshold");
260  for (const auto& nodeIt : nc) {
261  NBNode* const n = nodeIt.second;
262  // the type may already be set from the data
264  n->myTypeWasGuessed = false;
265  continue;
266  }
267  // check whether the node was set to be unregulated by the user
268  if (oc.getBool("keep-nodes-unregulated") || oc.isInStringVector("keep-nodes-unregulated.explicit", n->getID())
269  || (oc.getBool("keep-nodes-unregulated.district-nodes") && (n->isNearDistrict() || n->isDistrict()))) {
271  continue;
272  }
273  // check whether the node is a waterway node. Set to unregulated by default
274  bool waterway = true;
275  for (NBEdge* e : n->getEdges()) {
276  if (!isWaterway(e->getPermissions())) {
277  waterway = false;
278  break;
279  }
280  }
281  if (waterway && (n->myType == SumoXMLNodeType::UNKNOWN || n->myType == SumoXMLNodeType::DEAD_END)) {
283  continue;
284  }
285 
286  // check whether the junction is not a real junction
287  if (n->myIncomingEdges.size() == 1) {
289  continue;
290  }
291  // @todo "isSimpleContinuation" should be revalidated
292  if (n->isSimpleContinuation()) {
294  continue;
295  }
296  if (isRailwayNode(n)) {
297  // priority instead of unregulated to ensure that collisions can be detected
299  continue;
300  }
301  // determine the type
303  for (EdgeVector::const_iterator i = n->myIncomingEdges.begin(); i != n->myIncomingEdges.end(); i++) {
304  for (EdgeVector::const_iterator j = i + 1; j != n->myIncomingEdges.end(); j++) {
305  // @todo "getOppositeIncoming" should probably be refactored into something the edge knows
306  if (n->getOppositeIncoming(*j) == *i && n->myIncomingEdges.size() > 2) {
307  continue;
308  }
309  // @todo check against a legal document
310  // @todo figure out when SumoXMLNodeType::PRIORITY_STOP is appropriate
311  const double s1 = (*i)->getSpeed();
312  const double s2 = (*j)->getSpeed();
313  const int p1 = (*i)->getPriority();
314  const int p2 = (*j)->getPriority();
315  if (fabs(s1 - s2) > (9.5 / 3.6) || MAX2(s1, s2) >= rightBeforeLeftSpeed || p1 != p2) {
317  break;
318  }
319  }
320  }
321  // save type
322  n->myType = type;
323  n->myTypeWasGuessed = true;
324  }
325 }
326 
327 
328 void
330  for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
331  NBNode* n = (*i).second;
333  // check if it really is a rail crossing
334  int numRailway = 0;
335  int numNonRailIn = 0;
336  int numNonRailOut = 0;
337  std::set<const NBNode*> nonRailNodes;
338  int numNonRailwayNonPed = 0;
339  for (NBEdge* e : n->getIncomingEdges()) {
340  if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
341  numNonRailIn += 1;
342  if (e->getPermissions() != SVC_PEDESTRIAN) {
343  numNonRailwayNonPed++;
344  }
345  nonRailNodes.insert(e->getFromNode());
346  } else if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
347  numRailway++;
348  }
349  }
350  for (NBEdge* e : n->getOutgoingEdges()) {
351  if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
352  numNonRailOut += 1;
353  nonRailNodes.insert(e->getToNode());
354  }
355  }
356  if (numNonRailIn == 0 || numNonRailOut == 0 || numRailway == 0) {
357  // not a crossing (maybe unregulated or rail_signal)
358  WRITE_WARNINGF(TL("Converting invalid rail_crossing to priority junction '%'."), n->getID());
360  } else if (numNonRailwayNonPed > 2 || nonRailNodes.size() > 2) {
361  // does not look like a rail crossing (roads in conflict). maybe a traffic light?
362  WRITE_WARNINGF(TL("Converting invalid rail_crossing to traffic_light at junction '%'."), n->getID());
364  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(n->getID(), n, 0, type);
366  if (!tlc.insert(tlDef)) {
367  // actually, nothing should fail here
368  n->removeTrafficLight(tlDef);
370  delete tlDef;
371  WRITE_WARNINGF(TL("Could not allocate tls '%'."), n->getID());
372  }
373  }
374  }
375  }
376 }
377 
378 
379 bool
381  bool hasRailway = false;
382  for (NBEdge* e : n->getIncomingEdges()) {
383  if ((e->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_TAXI)) != 0) {
384  return false;
385  } else if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
386  hasRailway = true;
387  }
388  }
389  return hasRailway;
390 }
391 
392 // ---------------------------------------------------------------------------
393 // NBEdgePriorityComputer
394 // ---------------------------------------------------------------------------
395 void
397  for (const auto& node : nc) {
398  // preset all junction's edge priorities to zero
399  for (NBEdge* const edge : node.second->myAllEdges) {
400  edge->setJunctionPriority(node.second, NBEdge::JunctionPriority::MINOR_ROAD);
401  }
402  node.second->markBentPriority(false);
403  // check if the junction is not a real junction
404  if (node.second->myIncomingEdges.size() == 1 && node.second->myOutgoingEdges.size() == 1) {
405  continue;
406  }
407  // compute the priorities on junction when needed
408  if (node.second->getType() != SumoXMLNodeType::RIGHT_BEFORE_LEFT
409  && node.second->getType() != SumoXMLNodeType::LEFT_BEFORE_RIGHT
410  && node.second->getType() != SumoXMLNodeType::ALLWAY_STOP
411  && node.second->getType() != SumoXMLNodeType::NOJUNCTION) {
412  if (node.second->getRightOfWay() == RightOfWay::EDGEPRIORITY) {
413  for (NBEdge* e : node.second->getIncomingEdges()) {
414  e->setJunctionPriority(node.second, e->getPriority());
415  }
416  } else {
417  setPriorityJunctionPriorities(*node.second);
418  }
419  }
420  }
421 }
422 
423 
424 void
426  if (n.myIncomingEdges.size() == 0 || n.myOutgoingEdges.size() == 0) {
427  return;
428  }
429  int minPrio = std::numeric_limits<int>::max();
430  int maxPrio = -std::numeric_limits<int>::max();
431  int maxNumLanes = -std::numeric_limits<int>::max();
432  double maxSpeed = -std::numeric_limits<double>::max();
433  if (forceStraight) {
434  // called a second time, preset all junction's edge priorities to zero
435  for (NBEdge* const edge : n.myAllEdges) {
436  edge->setJunctionPriority(&n, NBEdge::JunctionPriority::MINOR_ROAD);
437  minPrio = MIN2(minPrio, edge->getPriority());
438  maxPrio = MAX2(maxPrio, edge->getPriority());
439  maxNumLanes = MAX2(maxNumLanes, edge->getNumLanes());
440  maxSpeed = MAX2(maxSpeed, edge->getSpeed());
441  }
442  }
443  EdgeVector incoming = n.myIncomingEdges;
444  EdgeVector outgoing = n.myOutgoingEdges;
445  // what we do want to have is to extract the pair of roads that are
446  // the major roads for this junction
447  // let's get the list of incoming edges with the highest priority
448  std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_priority_sorter());
449  EdgeVector bestIncoming;
450  NBEdge* bestIn = incoming[0];
451  while (incoming.size() > 0 && (forceStraight || samePriority(bestIn, incoming[0]))) {
452  bestIncoming.push_back(*incoming.begin());
453  incoming.erase(incoming.begin());
454  }
455  // now, let's get the list of best outgoing
456  assert(outgoing.size() != 0);
457  sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_priority_sorter());
458  EdgeVector bestOutgoing;
459  const NBEdge* const firstOut = outgoing[0];
460  while (outgoing.size() > 0 && (forceStraight || samePriority(firstOut, outgoing[0]))) { //->getPriority()==best->getPriority())
461  bestOutgoing.push_back(*outgoing.begin());
462  outgoing.erase(outgoing.begin());
463  }
464  // special case: user input makes mainDirection unambiguous
465  const bool mainDirectionExplicit = (
466  bestIncoming.size() == 1 && n.myIncomingEdges.size() <= 2
467  && (incoming.size() == 0 || bestIncoming[0]->getPriority() > incoming[0]->getPriority())
468  && bestOutgoing.size() == 1 && n.myOutgoingEdges.size() <= 2
469  && (outgoing.size() == 0 || bestOutgoing[0]->getPriority() > outgoing[0]->getPriority())
470  && !bestIncoming[0]->isTurningDirectionAt(bestOutgoing[0]));
471  // now, let's compute for each of the best incoming edges
472  // the incoming which is most opposite
473  // the outgoing which is most opposite
474  EdgeVector::iterator i;
475  std::map<NBEdge*, NBEdge*> counterIncomingEdges;
476  std::map<NBEdge*, NBEdge*> counterOutgoingEdges;
477  incoming = n.myIncomingEdges;
478  outgoing = n.myOutgoingEdges;
479  for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
480  std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
481  counterIncomingEdges[*i] = *incoming.begin();
482  std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
483  counterOutgoingEdges[*i] = *outgoing.begin();
484  }
485 #ifdef DEBUG_SETPRIORITIES
486  if (DEBUGCOND) {
487  std::map<std::string, std::string> tmp1;
488  for (auto item : counterIncomingEdges) {
489  tmp1[item.first->getID()] = item.second->getID();
490  }
491  std::map<std::string, std::string> tmp2;
492  for (auto item : counterOutgoingEdges) {
493  tmp2[item.first->getID()] = item.second->getID();
494  }
495  std::cout << "n=" << n.getID() << " bestIn=" << bestIn->getID() << " bestOut=" << toString(bestOutgoing)
496  << " counterBest=" << counterIncomingEdges.find(bestIncoming[0])->second->getID()
497  << " mainExplicit=" << mainDirectionExplicit
498  << " forceStraight=" << forceStraight
499  << "\n bestIncoming=" << toString(bestIncoming) << " allIncoming=" << toString(incoming)
500  << "\n bestOutgoing=" << toString(bestOutgoing) << " allOutgoing=" << toString(outgoing)
501  << "\n counterIncomingEdges=" << toString(tmp1)
502  << "\n counterOutgoingEdges=" << toString(tmp2)
503  << "\n";
504  }
505 #endif
506  // at a tls junction we must prevent an underlying bent-priority layout
507  // because that would lead to invalid right-of-way rules for an oncoming
508  // tls layout (but not vice versa). See #7764
509  const bool hasTLS = n.isTLControlled();
510  // ok, let's try
511  // 1) there is one best incoming road
512  if (bestIncoming.size() == 1) {
513  // let's mark this road as the best
514  NBEdge* best1 = extractAndMarkFirst(n, bestIncoming);
515  if (!mainDirectionExplicit && counterIncomingEdges.find(best1) != counterIncomingEdges.end()) {
516  // ok, look, what we want is the opposit of the straight continuation edge
517  // but, what if such an edge does not exist? By now, we'll determine it
518  // geometrically
519  NBEdge* s = counterIncomingEdges.find(best1)->second;
520  const double minAngleDiff = GeomHelper::getMinAngleDiff(best1->getAngleAtNode(&n), s->getAngleAtNode(&n));
521  if (minAngleDiff > 180 - 45
522  || (minAngleDiff > 75 && s->getPriority() == best1->getPriority() && hasDifferentPriorities(incoming, best1))) {
524  }
525  }
526  markBestParallel(n, best1, nullptr);
527  assert(bestOutgoing.size() != 0);
528  // mark the best outgoing as the continuation
529  sort(bestOutgoing.begin(), bestOutgoing.end(), NBContHelper::edge_similar_direction_sorter(best1));
530  // assign extra priority if the priorities are unambiguous (regardless of geometry)
531  NBEdge* bestOut = extractAndMarkFirst(n, bestOutgoing);
532  if (!mainDirectionExplicit && counterOutgoingEdges.find(bestOut) != counterOutgoingEdges.end()) {
533  NBEdge* s = counterOutgoingEdges.find(bestOut)->second;
534  if (GeomHelper::getMinAngleDiff(bestOut->getAngleAtNode(&n), s->getAngleAtNode(&n)) > 180 - 45) {
535  s->setJunctionPriority(&n, 1);
536  }
537  }
538  const bool isBent = n.getDirection(best1, bestOut) != LinkDirection::STRAIGHT;
539 #ifdef DEBUG_SETPRIORITIES
540  if (DEBUGCOND) {
541  std::cout << " best1=" << best1->getID() << " bestOut=" << bestOut->getID() << " bestOutgoing=" << toString(bestOutgoing) << " mainDirectionExplicit=" << mainDirectionExplicit << " isBent=" << isBent << "\n";
542  }
543 #endif
544  if (isBent && hasTLS && !forceStraight) {
545  // redo but force straight computation
547  } else {
548  n.markBentPriority(isBent);
549  }
550  return;
551  }
552 
553  // ok, what we want to do in this case is to determine which incoming
554  // has the best continuation...
555  // This means, when several incoming roads have the same priority,
556  // we want a (any) straight connection to be more priorised than a turning
557  double bestAngle = -1;
558  NBEdge* bestFirst = nullptr;
559  NBEdge* bestSecond = nullptr;
560  for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
561  EdgeVector::iterator j;
562  NBEdge* t1 = *i;
563  double angle1 = t1->getAngleAtNode(&n) + 180;
564  if (angle1 >= 360) {
565  angle1 -= 360;
566  }
567  for (j = i + 1; j != bestIncoming.end(); ++j) {
568  NBEdge* t2 = *j;
569  double angle2 = t2->getAngleAtNode(&n) + 180;
570  if (angle2 >= 360) {
571  angle2 -= 360;
572  }
573  double score = forceStraight ? getScore(t1, t2, minPrio, maxPrio, maxNumLanes, maxSpeed) : 0;
574  double angle = GeomHelper::getMinAngleDiff(angle1, angle2) + 45 * score;
575  if (angle > bestAngle) {
576  //if (forceStraight) std::cout << " node=" << n.getID() << " t1=" << t1->getID() << " t2=" << t2->getID() << " angle=" << angle << " bestAngle=" << bestAngle << " score=" << score << " minPrio=" << minPrio << " maxPrio=" << maxPrio << "\n";
577  bestAngle = MAX2(angle, bestAngle);
578  bestFirst = *i;
579  bestSecond = *j;
580  }
581  }
582  }
583  bestFirst->setJunctionPriority(&n, 1);
584  sort(bestOutgoing.begin(), bestOutgoing.end(), NBContHelper::edge_similar_direction_sorter(bestFirst));
585 #ifdef DEBUG_SETPRIORITIES
586  if (DEBUGCOND) {
587  std::cout << " bestFirst=" << bestFirst->getID() << " bestOutgoingFirst=" << toString(bestOutgoing) << "\n";
588  }
589 #endif
590  if (bestOutgoing.size() != 0) {
591  extractAndMarkFirst(n, bestOutgoing);
592  }
593  bestSecond->setJunctionPriority(&n, 1);
594  sort(bestOutgoing.begin(), bestOutgoing.end(), NBContHelper::edge_similar_direction_sorter(bestSecond));
595 #ifdef DEBUG_SETPRIORITIES
596  if (DEBUGCOND) {
597  std::cout << " bestSecond=" << bestSecond->getID() << " bestOutgoingSecond=" << toString(bestOutgoing) << "\n";
598  }
599 #endif
600  if (bestOutgoing.size() != 0) {
601  extractAndMarkFirst(n, bestOutgoing);
602  }
603  const bool isBent = GeomHelper::getMinAngleDiff(bestFirst->getAngleAtNode(&n), bestSecond->getAngleAtNode(&n)) < 135;
604  if (isBent && hasTLS && !forceStraight) {
605  // redo but force straight computation
607  } else {
608  n.markBentPriority(isBent);
609  markBestParallel(n, bestFirst, bestSecond);
610  }
611 }
612 
613 double
614 NBEdgePriorityComputer::getScore(const NBEdge* e1, const NBEdge* e2, int minPrio, int maxPrio, int maxNumLanes, double maxSpeed) {
615  // normalize priorities to [0.1,1]
616  double normPrio1 = 1;
617  double normPrio2 = 1;
618  if (minPrio != maxPrio) {
619  normPrio1 = ((e1->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
620  normPrio2 = ((e2->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
621  }
622  return (normPrio1
623  * e1->getNumLanes() / maxNumLanes
624  * e1->getSpeed() / maxSpeed
625  * normPrio2
626  * e2->getNumLanes() / maxNumLanes
627  * e2->getSpeed() / maxSpeed);
628 }
629 
630 void
631 NBEdgePriorityComputer::markBestParallel(const NBNode& n, NBEdge* bestFirst, NBEdge* bestSecond) {
632  // edges running parallel to the main direction should also be prioritised
633  const double a1 = bestFirst->getAngleAtNode(&n);
634  const double a2 = bestSecond == nullptr ? a1 : bestSecond->getAngleAtNode(&n);
635  SVCPermissions p1 = bestFirst->getPermissions();
636  SVCPermissions p2 = bestSecond == nullptr ? p1 : bestSecond->getPermissions();
637  for (NBEdge* e : n.getIncomingEdges()) {
638  // @note: this rule might also apply if there are common permissions but
639  // then we would not further rules to resolve the priority between the best edge and its parallel edge
640  SVCPermissions perm = e->getPermissions();
641  if (((GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a1) < 10
642  || GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a2) < 10))
643  && (p1 & perm) == 0 && (p2 & perm) == 0) {
644  e->setJunctionPriority(&n, 1);
645  }
646  }
647 }
648 
649 
650 NBEdge*
651 NBEdgePriorityComputer::extractAndMarkFirst(NBNode& n, std::vector<NBEdge*>& s, int prio) {
652  if (s.size() == 0) {
653  return nullptr;
654  }
655  NBEdge* ret = s.front();
656  s.erase(s.begin());
657  ret->setJunctionPriority(&n, prio);
658  return ret;
659 }
660 
661 
662 bool
663 NBEdgePriorityComputer::samePriority(const NBEdge* const e1, const NBEdge* const e2) {
664  if (e1 == e2) {
665  return true;
666  }
667  if (e1->getPriority() != e2->getPriority()) {
668  return false;
669  }
670  if ((int) e1->getSpeed() != (int) e2->getSpeed()) {
671  return false;
672  }
673  return (int) e1->getNumLanes() == (int) e2->getNumLanes();
674 }
675 
676 
677 bool
679  if (edges.size() < 2) {
680  return false;
681  }
682  int prio = edges[0] == excluded ? edges[1]->getPriority() : edges[0]->getPriority();
683  for (auto e : edges) {
684  if (e != excluded && e->getPriority() != prio) {
685  return true;
686  }
687  }
688  return false;
689 }
690 
691 
693  // reorder based on getAngleAtNodeToCenter
694  myOrdering = ordering;
696  // let the first edge remain the first
697  rotate(myOrdering.begin(), std::find(myOrdering.begin(), myOrdering.end(), ordering.front()), myOrdering.end());
698 }
699 
700 
701 /****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define TL(string)
Definition: MsgHandler.h:315
#define DEBUGCOND
#define DEBUGCOND2(obj)
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:35
bool isWaterway(SVCPermissions permissions)
Returns whether an edge with the given permission is a waterway edge.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_TAXI
vehicle is a taxi
@ SVC_PEDESTRIAN
pedestrian
TrafficLightType
@ STRAIGHT
The link is a straight direction.
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static double legacyDegree(const double angle, const bool positive=false)
Definition: GeomHelper.cpp:214
static double getMinAngleDiff(double angle1, double angle2)
Returns the minimum distance (clockwise/counter-clockwise) between both angles.
Definition: GeomHelper.cpp:172
Class to sort edges by their angle in relation to the given edge.
Definition: NBContHelper.h:142
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:4308
@ PRIORITY_ROAD
Definition: NBEdge.h:382
const std::string & getID() const
Definition: NBEdge.h:1522
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
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:3594
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
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2110
int getPriority() const
Returns the priority of the edge.
Definition: NBEdge.h:523
void setJunctionPriority(const NBNode *const node, int prio)
Sets the junction priority of the edge.
Definition: NBEdge.cpp:2094
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1358
static double getScore(const NBEdge *e1, const NBEdge *e2, int minPrio, int maxPrio, int maxNumLanes, double maxSpeed)
score pair of edges for multi-criteria evaluatoin of angle, priority, laneNumber and speed
static void markBestParallel(const NBNode &n, NBEdge *bestFirst, NBEdge *bestSecond)
set priority for edges that are parallel to the best edges
static NBEdge * extractAndMarkFirst(NBNode &n, std::vector< NBEdge * > &s, int prio=1)
Sets the priorites in case of a priority junction.
static bool hasDifferentPriorities(const EdgeVector &edges, const NBEdge *excluded)
return whether the priorite attribute can be used to distinguish the edges
static void computeEdgePriorities(NBNodeCont &nc)
Computes edge priorities within a node.
static void setPriorityJunctionPriorities(NBNode &n, bool forceStraight=false)
Sets the priorites in case of a priority junction.
static bool samePriority(const NBEdge *const e1, const NBEdge *const e2)
Returns whether both edges have the same priority.
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
Container for nodes during the netbuilding process.
Definition: NBNodeCont.h:57
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition: NBNodeCont.h:113
std::map< std::string, NBNode * >::const_iterator end() const
Returns the pointer to the end of the stored nodes.
Definition: NBNodeCont.h:118
Represents a single node (junction) during network building.
Definition: NBNode.h:66
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:2349
bool isSimpleContinuation(bool checkLaneNumbers=true, bool checkWidth=false) const
check if node is a simple continuation
Definition: NBNode.cpp:510
bool myTypeWasGuessed
whether the node type was guessed rather than loaded
Definition: NBNode.h:978
SumoXMLNodeType myType
The type of the junction.
Definition: NBNode.h:928
EdgeVector myOutgoingEdges
Vector of outgoing edges.
Definition: NBNode.h:913
EdgeVector myAllEdges
Vector of incoming and outgoing edges.
Definition: NBNode.h:916
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:273
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:278
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:268
bool isDistrict() const
check if node is a district
Definition: NBNode.cpp:2651
void removeTrafficLight(NBTrafficLightDefinition *tlDef)
Removes the given traffic light from this node.
Definition: NBNode.cpp:406
void markBentPriority(bool isBent)
mark whether a priority road turns at this node
Definition: NBNode.h:830
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3753
bool isNearDistrict() const
@chech if node is near district
Definition: NBNode.cpp:2634
EdgeVector myIncomingEdges
Vector of incoming edges.
Definition: NBNode.h:910
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition: NBNode.h:331
NBEdge * getOppositeIncoming(NBEdge *e) const
returns the opposite incoming edge of certain edge
Definition: NBNode.cpp:1859
static bool isRailwayNode(const NBNode *n)
whether the given node only has rail edges
static void computeNodeTypes(NBNodeCont &nc, NBTrafficLightLogicCont &tlc)
Computes node types.
static void validateRailCrossings(NBNodeCont &nc, NBTrafficLightLogicCont &tlc)
Checks rail_crossing for validity.
crossing_by_junction_angle_sorter(const NBNode *node, const EdgeVector &ordering)
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.
static void sortNodesEdges(NBNodeCont &nc, bool useNodeShape=false)
Sorts a node's edges clockwise regarding driving direction.
A traffic light logics which must be computed (only nodes/edges are given)
Definition: NBOwnTLDef.h:44
The base class for traffic light logic definitions.
A container for traffic light definitions and built programs.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
Sorts "Combination"s by decreasing angle.
Definition: NBAlgorithms.h:78
static double getFarAngleAtNode(const NBEdge *e, const NBNode *n, double dist=50)
compute angle to junction at a point further away
static void computeTurnDirections(NBNodeCont &nc, bool warn=true)
Computes turnaround destinations for all edges (if exist)
static void computeTurnDirectionsForNode(NBNode *node, bool warn)
Computes turnaround destinations for all incoming edges of the given nodes (if any)
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)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
bool isInStringVector(const std::string &optionName, const std::string &itemName) const
Returns the named option is a list of string values containing the specified item.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
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:281
double length() const
Returns the length.
Position positionAtOffset(double pos, double lateralOffset=0) const
Returns the position at the given length.
static StringBijection< TrafficLightType > TrafficLightTypes
traffic light types
T get(const std::string &str) const
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
Stores the information about the angle between an incoming ("from") and an outgoing ("to") edge.
Definition: NBAlgorithms.h:68