LCOV - code coverage report
Current view: top level - src/netbuild - NBAlgorithms.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 267 278 96.0 %
Date: 2024-05-07 15:28:01 Functions: 16 16 100.0 %

          Line data    Source code
       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             : /****************************************************************************/
      14             : /// @file    NBAlgorithms.cpp
      15             : /// @author  Daniel Krajzewicz
      16             : /// @author  Jakob Erdmann
      17             : /// @date    02. March 2012
      18             : ///
      19             : // Algorithms for network computation
      20             : /****************************************************************************/
      21             : #include <config.h>
      22             : 
      23             : #include <sstream>
      24             : #include <iostream>
      25             : #include <cassert>
      26             : #include <algorithm>
      27             : #include <utils/common/MsgHandler.h>
      28             : #include <utils/common/ToString.h>
      29             : #include <utils/options/OptionsCont.h>
      30             : #include "NBEdge.h"
      31             : #include "NBOwnTLDef.h"
      32             : #include "NBTrafficLightLogicCont.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
      52        5736 : NBTurningDirectionsComputer::computeTurnDirections(NBNodeCont& nc, bool warn) {
      53      280344 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
      54      274608 :         computeTurnDirectionsForNode(i->second, warn);
      55             :     }
      56        5736 : }
      57             : 
      58             : void
      59      277317 : NBTurningDirectionsComputer::computeTurnDirectionsForNode(NBNode* node, bool warn) {
      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      746271 :     for (std::vector<NBEdge*>::const_iterator k = incoming.begin(); k != incoming.end(); ++k) {
      64      468954 :         (*k)->setTurningDestination(nullptr);
      65             :     }
      66             :     std::vector<Combination> combinations;
      67      277317 :     const bool geometryLike = node->geometryLike();
      68      745900 :     for (NBEdge* outedge : outgoing) {
      69     1548444 :         for (NBEdge* e : incoming) {
      70             :             // @todo: check whether NBHelpers::relAngle is properly defined and whether it should really be used, here
      71     1079861 :             const double signedAngle = NBHelpers::normRelAngle(e->getAngleAtNode(node), outedge->getAngleAtNode(node));
      72     1079861 :             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      772273 :                 continue;
      81             :             }
      82      763923 :             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      763923 :             const bool badPermissions = ((outedge->getPermissions() & e->getPermissions() & ~SVC_PEDESTRIAN) == 0
      89      125035 :                                          && !geometryLike
      90      880783 :                                          && outedge->getPermissions() != e->getPermissions());
      91             :             if (e->getFromNode() == outedge->getToNode()
      92      292399 :                     && (angle > 120 || e->getFromNode()->getPosition() == e->getToNode()->getPosition())
      93     1050074 :                     && !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      283194 :                 angle += 360;
     105             :             }
     106      763923 :             if (angle < 160) {
     107      457428 :                 if (angle > 135) {
     108             :                     // could be a turnaround with a green median island, look at
     109             :                     // angle further away from the junction
     110        7014 :                     const double inFA = getFarAngleAtNode(e, node);
     111        7014 :                     const double outFA = getFarAngleAtNode(outedge, node);
     112        7014 :                     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        7014 :                     if (signedFarAngle > -160) {
     119        5921 :                         continue;
     120             :                     }
     121             :                 } else {
     122      450414 :                     continue;
     123             :                 }
     124             :             }
     125      307588 :             if (badPermissions) {
     126             :                 // penalty
     127        8359 :                 angle -= 90;
     128             :             }
     129             :             Combination c;
     130      307588 :             c.from = e;
     131      307588 :             c.to = outedge;
     132      307588 :             c.angle = angle;
     133      307588 :             combinations.push_back(c);
     134             :         }
     135             :     }
     136             :     // sort combinations so that the ones with the highest angle are at the begin
     137      277317 :     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      584905 :     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      602282 :         if (seen.find((*j).from) != seen.end() || seen.find((*j).to) != seen.end()) {
     151             :             // do not regard already set edges
     152       15861 :             if ((*j).angle > 360 && warn) {
     153         805 :                 WRITE_WARNINGF(TL("Ambiguity in turnarounds computation at junction '%'."), node->getID());
     154             :                 //std::cout << "  already seen: " << toString(seen) << "\n";
     155             :                 warn = false;
     156             :             }
     157       15861 :             continue;
     158             :         }
     159             :         // mark as seen
     160      291727 :         seen.insert((*j).from);
     161      291727 :         seen.insert((*j).to);
     162             :         // set turnaround information
     163      291727 :         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      291727 :         (*j).from->setTurningDestination((*j).to, onlyPossible);
     170             :     }
     171      277317 : }
     172             : 
     173             : 
     174             : double
     175       14028 : NBTurningDirectionsComputer::getFarAngleAtNode(const NBEdge* e, const NBNode* n, double dist) {
     176             :     Position atNode;
     177             :     Position far;
     178             :     double angle;
     179             :     const NBEdge* next = e;
     180       14028 :     if (e->getToNode() == n) {
     181             :         // search upstream
     182        7014 :         atNode = e->getGeometry().back();
     183        7613 :         while (dist > 0) {
     184        7613 :             const double length = next->getGeometry().length();
     185        7613 :             if (dist <= length) {
     186        2505 :                 far = next->getGeometry().positionAtOffset(length - dist);
     187        2505 :                 break;
     188             :             } else {
     189        5108 :                 far = next->getGeometry().front();
     190        5108 :                 dist -= length;
     191        5108 :                 if (next->getToNode()->getIncomingEdges().size() == 1) {
     192         599 :                     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        7014 :         atNode = e->getGeometry().front();
     203        8843 :         while (dist > 0) {
     204        8843 :             const double length = next->getGeometry().length();
     205        8843 :             if (dist <= length) {
     206        3173 :                 far = next->getGeometry().positionAtOffset(dist);
     207        3173 :                 break;
     208             :             } else {
     209        5670 :                 far = next->getGeometry().back();
     210        5670 :                 dist -= length;
     211        5670 :                 if (next->getToNode()->getOutgoingEdges().size() == 1) {
     212        1829 :                     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       14028 :     return GeomHelper::legacyDegree(angle);
     222             : }
     223             : 
     224             : 
     225             : // ---------------------------------------------------------------------------
     226             : // NBNodesEdgesSorter
     227             : // ---------------------------------------------------------------------------
     228             : void
     229        5569 : NBNodesEdgesSorter::sortNodesEdges(NBNodeCont& nc, bool useNodeShape) {
     230      252410 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     231      246841 :         i->second->sortEdges(useNodeShape);
     232             :     }
     233        5569 : }
     234             : 
     235             : 
     236             : void
     237      815529 : NBNodesEdgesSorter::swapWhenReversed(const NBNode* const n,
     238             :                                      const std::vector<NBEdge*>::iterator& i1,
     239             :                                      const std::vector<NBEdge*>::iterator& i2) {
     240      815529 :     NBEdge* e1 = *i1;
     241      815529 :     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      815529 :     if (e2->getToNode() == n && e2->isTurningDirectionAt(e1)) {
     247             :         std::swap(*i1, *i2);
     248             :     }
     249      815529 : }
     250             : 
     251             : 
     252             : // ---------------------------------------------------------------------------
     253             : // NBNodeTypeComputer
     254             : // ---------------------------------------------------------------------------
     255             : void
     256        1837 : NBNodeTypeComputer::computeNodeTypes(NBNodeCont& nc, NBTrafficLightLogicCont& tlc) {
     257        1837 :     validateRailCrossings(nc, tlc);
     258        1837 :     const OptionsCont& oc = OptionsCont::getOptions();
     259        3674 :     const double rightBeforeLeftSpeed = oc.getFloat("junctions.right-before-left.speed-threshold");
     260       73651 :     for (const auto& nodeIt : nc) {
     261       71814 :         NBNode* const n = nodeIt.second;
     262             :         // the type may already be set from the data
     263       71814 :         if (n->myType != SumoXMLNodeType::UNKNOWN && n->myType != SumoXMLNodeType::DEAD_END) {
     264       32368 :             n->myTypeWasGuessed = false;
     265       32368 :             continue;
     266             :         }
     267             :         // check whether the node was set to be unregulated by the user
     268      157784 :         if (oc.getBool("keep-nodes-unregulated") || oc.isInStringVector("keep-nodes-unregulated.explicit", n->getID())
     269      118338 :                 || (oc.getBool("keep-nodes-unregulated.district-nodes") && (n->isNearDistrict() || n->isDistrict()))) {
     270           0 :             n->myType = SumoXMLNodeType::NOJUNCTION;
     271           0 :             continue;
     272             :         }
     273             :         // check whether the node is a waterway node. Set to unregulated by default
     274             :         bool waterway = true;
     275       39599 :         for (NBEdge* e : n->getEdges()) {
     276       39558 :             if (!isWaterway(e->getPermissions())) {
     277             :                 waterway = false;
     278             :                 break;
     279             :             }
     280             :         }
     281       39446 :         if (waterway && (n->myType == SumoXMLNodeType::UNKNOWN || n->myType == SumoXMLNodeType::DEAD_END)) {
     282          41 :             n->myType = SumoXMLNodeType::NOJUNCTION;
     283          41 :             continue;
     284             :         }
     285             : 
     286             :         // check whether the junction is not a real junction
     287       39405 :         if (n->myIncomingEdges.size() == 1) {
     288       19009 :             n->myType = SumoXMLNodeType::PRIORITY;
     289       19009 :             continue;
     290             :         }
     291             :         // @todo "isSimpleContinuation" should be revalidated
     292       20396 :         if (n->isSimpleContinuation()) {
     293        1492 :             n->myType = SumoXMLNodeType::PRIORITY;
     294        1492 :             continue;
     295             :         }
     296       18904 :         if (isRailwayNode(n)) {
     297             :             // priority instead of unregulated to ensure that collisions can be detected
     298         494 :             n->myType = SumoXMLNodeType::PRIORITY;
     299         494 :             continue;
     300             :         }
     301             :         // determine the type
     302       36820 :         SumoXMLNodeType type = oc.getBool("junctions.left-before-right") ? SumoXMLNodeType::LEFT_BEFORE_RIGHT : SumoXMLNodeType::RIGHT_BEFORE_LEFT;
     303       57296 :         for (EdgeVector::const_iterator i = n->myIncomingEdges.begin(); i != n->myIncomingEdges.end(); i++) {
     304       54231 :             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       28882 :                 if (n->getOppositeIncoming(*j) == *i && n->myIncomingEdges.size() > 2) {
     307        7637 :                     continue;
     308             :                 }
     309             :                 // @todo check against a legal document
     310             :                 // @todo figure out when SumoXMLNodeType::PRIORITY_STOP is appropriate
     311       21245 :                 const double s1 = (*i)->getSpeed();
     312       21245 :                 const double s2 = (*j)->getSpeed();
     313       21245 :                 const int p1 = (*i)->getPriority();
     314       21245 :                 const int p2 = (*j)->getPriority();
     315       34945 :                 if (fabs(s1 - s2) > (9.5 / 3.6) || MAX2(s1, s2) >= rightBeforeLeftSpeed || p1 != p2) {
     316             :                     type = SumoXMLNodeType::PRIORITY;
     317             :                     break;
     318             :                 }
     319             :             }
     320             :         }
     321             :         // save type
     322       18410 :         n->myType = type;
     323       18410 :         n->myTypeWasGuessed = true;
     324             :     }
     325        1837 : }
     326             : 
     327             : 
     328             : void
     329        1986 : NBNodeTypeComputer::validateRailCrossings(NBNodeCont& nc, NBTrafficLightLogicCont& tlc) {
     330      110879 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     331      108893 :         NBNode* n = (*i).second;
     332      108893 :         if (n->myType == SumoXMLNodeType::RAIL_CROSSING) {
     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        3595 :             for (NBEdge* e : n->getIncomingEdges()) {
     340        2444 :                 if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
     341         987 :                     numNonRailIn += 1;
     342         987 :                     if (e->getPermissions() != SVC_PEDESTRIAN) {
     343         244 :                         numNonRailwayNonPed++;
     344             :                     }
     345         987 :                     nonRailNodes.insert(e->getFromNode());
     346        1457 :                 } else if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     347        1457 :                     numRailway++;
     348             :                 }
     349             :             }
     350        3589 :             for (NBEdge* e : n->getOutgoingEdges()) {
     351        2438 :                 if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
     352         981 :                     numNonRailOut += 1;
     353         981 :                     nonRailNodes.insert(e->getToNode());
     354             :                 }
     355             :             }
     356        1151 :             if (numNonRailIn == 0 || numNonRailOut == 0 || numRailway == 0) {
     357             :                 // not a crossing (maybe unregulated or rail_signal)
     358        1101 :                 WRITE_WARNINGF(TL("Converting invalid rail_crossing to priority junction '%'."), n->getID());
     359         367 :                 n->myType = SumoXMLNodeType::PRIORITY;
     360         784 :             } else if (numNonRailwayNonPed > 2 || nonRailNodes.size() > 2) {
     361             :                 // does not look like a rail crossing (roads in conflict). maybe a traffic light?
     362         147 :                 WRITE_WARNINGF(TL("Converting invalid rail_crossing to traffic_light at junction '%'."), n->getID());
     363         147 :                 TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
     364          49 :                 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(n->getID(), n, 0, type);
     365          49 :                 n->myType = SumoXMLNodeType::TRAFFIC_LIGHT;
     366          49 :                 if (!tlc.insert(tlDef)) {
     367             :                     // actually, nothing should fail here
     368           0 :                     n->removeTrafficLight(tlDef);
     369           0 :                     n->myType = SumoXMLNodeType::PRIORITY;
     370           0 :                     delete tlDef;
     371           0 :                     WRITE_WARNINGF(TL("Could not allocate tls '%'."), n->getID());
     372             :                 }
     373             :             }
     374             :         }
     375             :     }
     376        1986 : }
     377             : 
     378             : 
     379             : bool
     380      143487 : NBNodeTypeComputer::isRailwayNode(const NBNode* n) {
     381             :     bool hasRailway = false;
     382      151132 :     for (NBEdge* e : n->getIncomingEdges()) {
     383      144648 :         if ((e->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_TAXI)) != 0) {
     384             :             return false;
     385        7645 :         } else if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     386             :             hasRailway = true;
     387             :         }
     388             :     }
     389             :     return hasRailway;
     390             : }
     391             : 
     392             : // ---------------------------------------------------------------------------
     393             : // NBEdgePriorityComputer
     394             : // ---------------------------------------------------------------------------
     395             : void
     396        1837 : NBEdgePriorityComputer::computeEdgePriorities(NBNodeCont& nc) {
     397       73651 :     for (const auto& node : nc) {
     398             :         // preset all junction's edge priorities to zero
     399      328068 :         for (NBEdge* const edge : node.second->myAllEdges) {
     400      256254 :             edge->setJunctionPriority(node.second, NBEdge::JunctionPriority::MINOR_ROAD);
     401             :         }
     402       71814 :         node.second->markBentPriority(false);
     403             :         // check if the junction is not a real junction
     404       71814 :         if (node.second->myIncomingEdges.size() == 1 && node.second->myOutgoingEdges.size() == 1) {
     405       19831 :             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       51983 :                 && node.second->getType() != SumoXMLNodeType::NOJUNCTION) {
     412       38087 :             if (node.second->getRightOfWay() == RightOfWay::EDGEPRIORITY) {
     413          24 :                 for (NBEdge* e : node.second->getIncomingEdges()) {
     414          20 :                     e->setJunctionPriority(node.second, e->getPriority());
     415             :                 }
     416             :             } else {
     417       38083 :                 setPriorityJunctionPriorities(*node.second);
     418             :             }
     419             :         }
     420             :     }
     421        1837 : }
     422             : 
     423             : 
     424             : void
     425       38634 : NBEdgePriorityComputer::setPriorityJunctionPriorities(NBNode& n, bool forceStraight) {
     426       38634 :     if (n.myIncomingEdges.size() == 0 || n.myOutgoingEdges.size() == 0) {
     427       19941 :         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       33989 :     if (forceStraight) {
     434             :         // called a second time, preset all junction's edge priorities to zero
     435        3885 :         for (NBEdge* const edge : n.myAllEdges) {
     436        3334 :             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        3334 :             maxSpeed = MAX2(maxSpeed, edge->getSpeed());
     441             :         }
     442             :     }
     443       33989 :     EdgeVector incoming = n.myIncomingEdges;
     444       33989 :     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       33989 :     std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_priority_sorter());
     449             :     EdgeVector bestIncoming;
     450       33989 :     NBEdge* bestIn = incoming[0];
     451       96160 :     while (incoming.size() > 0 && (forceStraight || samePriority(bestIn, incoming[0]))) {
     452       62171 :         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       33989 :     sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_priority_sorter());
     458             :     EdgeVector bestOutgoing;
     459       33989 :     const NBEdge* const firstOut = outgoing[0];
     460       99892 :     while (outgoing.size() > 0 && (forceStraight || samePriority(firstOut, outgoing[0]))) { //->getPriority()==best->getPriority())
     461       65903 :         bestOutgoing.push_back(*outgoing.begin());
     462             :         outgoing.erase(outgoing.begin());
     463             :     }
     464             :     // special case: user input makes mainDirection unambiguous
     465             :     const bool mainDirectionExplicit = (
     466       15296 :                                            bestIncoming.size() == 1 && n.myIncomingEdges.size() <= 2
     467       12985 :                                            && (incoming.size() == 0 || bestIncoming[0]->getPriority() > incoming[0]->getPriority())
     468       11082 :                                            && bestOutgoing.size() == 1 && n.myOutgoingEdges.size() <= 2
     469        6347 :                                            && (outgoing.size() == 0 || bestOutgoing[0]->getPriority() > outgoing[0]->getPriority())
     470       39466 :                                            && !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       33989 :     incoming = n.myIncomingEdges;
     478       33989 :     outgoing = n.myOutgoingEdges;
     479       96160 :     for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
     480      124342 :         std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
     481       62171 :         counterIncomingEdges[*i] = *incoming.begin();
     482      124342 :         std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
     483       62171 :         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       33989 :     if (bestIncoming.size() == 1) {
     513             :         // let's mark this road as the best
     514       15296 :         NBEdge* best1 = extractAndMarkFirst(n, bestIncoming);
     515       25564 :         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       10268 :             NBEdge* s = counterIncomingEdges.find(best1)->second;
     520       10268 :             const double minAngleDiff = GeomHelper::getMinAngleDiff(best1->getAngleAtNode(&n), s->getAngleAtNode(&n));
     521             :             if (minAngleDiff > 180 - 45
     522       10268 :                     || (minAngleDiff > 75 && s->getPriority() == best1->getPriority() && hasDifferentPriorities(incoming, best1))) {
     523        2151 :                 s->setJunctionPriority(&n, NBEdge::PRIORITY_ROAD);
     524             :             }
     525             :         }
     526       15296 :         markBestParallel(n, best1, nullptr);
     527             :         assert(bestOutgoing.size() != 0);
     528             :         // mark the best outgoing as the continuation
     529       15296 :         sort(bestOutgoing.begin(), bestOutgoing.end(), NBContHelper::edge_similar_direction_sorter(best1));
     530             :         // assign extra priority if the priorities are unambiguous (regardless of geometry)
     531       15296 :         NBEdge* bestOut = extractAndMarkFirst(n, bestOutgoing);
     532       25564 :         if (!mainDirectionExplicit && counterOutgoingEdges.find(bestOut) != counterOutgoingEdges.end()) {
     533           0 :             NBEdge* s = counterOutgoingEdges.find(bestOut)->second;
     534           0 :             if (GeomHelper::getMinAngleDiff(bestOut->getAngleAtNode(&n), s->getAngleAtNode(&n)) > 180 - 45) {
     535           0 :                 s->setJunctionPriority(&n, 1);
     536             :             }
     537             :         }
     538       15296 :         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       15296 :         if (isBent && hasTLS && !forceStraight) {
     545             :             // redo but force straight computation
     546         263 :             setPriorityJunctionPriorities(n, true);
     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       65568 :     for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
     561             :         EdgeVector::iterator j;
     562       46875 :         NBEdge* t1 = *i;
     563       46875 :         double angle1 = t1->getAngleAtNode(&n) + 180;
     564       46875 :         if (angle1 >= 360) {
     565           0 :             angle1 -= 360;
     566             :         }
     567       88505 :         for (j = i + 1; j != bestIncoming.end(); ++j) {
     568       41630 :             NBEdge* t2 = *j;
     569       41630 :             double angle2 = t2->getAngleAtNode(&n) + 180;
     570       41630 :             if (angle2 >= 360) {
     571           0 :                 angle2 -= 360;
     572             :             }
     573       41630 :             double score = forceStraight ? getScore(t1, t2, minPrio, maxPrio, maxNumLanes, maxSpeed) : 0;
     574       41630 :             double angle = GeomHelper::getMinAngleDiff(angle1, angle2) + 45 * score;
     575       41630 :             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       25082 :                 bestFirst = *i;
     579       25082 :                 bestSecond = *j;
     580             :             }
     581             :         }
     582             :     }
     583       18693 :     bestFirst->setJunctionPriority(&n, 1);
     584       18693 :     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       18693 :     if (bestOutgoing.size() != 0) {
     591       18693 :         extractAndMarkFirst(n, bestOutgoing);
     592             :     }
     593       18693 :     bestSecond->setJunctionPriority(&n, 1);
     594       18693 :     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       18693 :     if (bestOutgoing.size() != 0) {
     601       16800 :         extractAndMarkFirst(n, bestOutgoing);
     602             :     }
     603       18693 :     const bool isBent = GeomHelper::getMinAngleDiff(bestFirst->getAngleAtNode(&n), bestSecond->getAngleAtNode(&n)) < 135;
     604       18693 :     if (isBent && hasTLS && !forceStraight) {
     605             :         // redo but force straight computation
     606         288 :         setPriorityJunctionPriorities(n, true);
     607             :     } else {
     608             :         n.markBentPriority(isBent);
     609       18405 :         markBestParallel(n, bestFirst, bestSecond);
     610             :     }
     611             : }
     612             : 
     613             : double
     614        2413 : 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        2413 :     if (minPrio != maxPrio) {
     619        1577 :         normPrio1 = ((e1->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
     620        1577 :         normPrio2 = ((e2->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
     621             :     }
     622             :     return (normPrio1
     623        2413 :             * e1->getNumLanes() / maxNumLanes
     624        2413 :             * e1->getSpeed() / maxSpeed
     625        2413 :             * normPrio2
     626        2413 :             * e2->getNumLanes() / maxNumLanes
     627        2413 :             * e2->getSpeed() / maxSpeed);
     628             : }
     629             : 
     630             : void
     631       33701 : NBEdgePriorityComputer::markBestParallel(const NBNode& n, NBEdge* bestFirst, NBEdge* bestSecond) {
     632             :     // edges running parallel to the main direction should also be prioritised
     633       33701 :     const double a1 = bestFirst->getAngleAtNode(&n);
     634       33701 :     const double a2 = bestSecond == nullptr ? a1 : bestSecond->getAngleAtNode(&n);
     635       33701 :     SVCPermissions p1 = bestFirst->getPermissions();
     636       33701 :     SVCPermissions p2 = bestSecond == nullptr ? p1 : bestSecond->getPermissions();
     637      116457 :     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       82756 :         SVCPermissions perm = e->getPermissions();
     641       82756 :         if (((GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a1) < 10
     642       47800 :                 || GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a2) < 10))
     643      101316 :                 && (p1 & perm) == 0 && (p2 & perm) == 0) {
     644         559 :             e->setJunctionPriority(&n, 1);
     645             :         }
     646             :     }
     647       33701 : }
     648             : 
     649             : 
     650             : NBEdge*
     651       66085 : NBEdgePriorityComputer::extractAndMarkFirst(NBNode& n, std::vector<NBEdge*>& s, int prio) {
     652       66085 :     if (s.size() == 0) {
     653             :         return nullptr;
     654             :     }
     655       66085 :     NBEdge* ret = s.front();
     656             :     s.erase(s.begin());
     657       66085 :     ret->setJunctionPriority(&n, prio);
     658       66085 :     return ret;
     659             : }
     660             : 
     661             : 
     662             : bool
     663      155190 : NBEdgePriorityComputer::samePriority(const NBEdge* const e1, const NBEdge* const e2) {
     664      155190 :     if (e1 == e2) {
     665             :         return true;
     666             :     }
     667       88314 :     if (e1->getPriority() != e2->getPriority()) {
     668             :         return false;
     669             :     }
     670       68715 :     if ((int) e1->getSpeed() != (int) e2->getSpeed()) {
     671             :         return false;
     672             :     }
     673       63039 :     return (int) e1->getNumLanes() == (int) e2->getNumLanes();
     674             : }
     675             : 
     676             : 
     677             : bool
     678        1070 : NBEdgePriorityComputer::hasDifferentPriorities(const EdgeVector& edges, const NBEdge* excluded) {
     679        1070 :     if (edges.size() < 2) {
     680             :         return false;
     681             :     }
     682        1070 :     int prio = edges[0] == excluded ? edges[1]->getPriority() : edges[0]->getPriority();
     683        3413 :     for (auto e : edges) {
     684        2473 :         if (e != excluded && e->getPriority() != prio) {
     685             :             return true;
     686             :         }
     687             :     }
     688             :     return false;
     689             : }
     690             : 
     691             : 
     692      249940 : NBNodesEdgesSorter::crossing_by_junction_angle_sorter::crossing_by_junction_angle_sorter(const NBNode* node, const EdgeVector& ordering) {
     693             :     // reorder based on getAngleAtNodeToCenter
     694      249940 :     myOrdering = ordering;
     695      249940 :     sort(myOrdering.begin(), myOrdering.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(node));
     696             :     // let the first edge remain the first
     697      249940 :     rotate(myOrdering.begin(), std::find(myOrdering.begin(), myOrdering.end(), ordering.front()), myOrdering.end());
     698      249940 : }
     699             : 
     700             : 
     701             : /****************************************************************************/

Generated by: LCOV version 1.14