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

            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         5306 : NBTurningDirectionsComputer::computeTurnDirections(NBNodeCont& nc, bool warn) {
      53       195262 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
      54       189956 :         computeTurnDirectionsForNode(i->second, warn);
      55              :     }
      56         5306 : }
      57              : 
      58              : void
      59       192035 : 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       516070 :     for (std::vector<NBEdge*>::const_iterator k = incoming.begin(); k != incoming.end(); ++k) {
      64       324035 :         (*k)->setTurningDestination(nullptr);
      65              :     }
      66              :     std::vector<Combination> combinations;
      67       192035 :     const bool geometryLike = node->geometryLike();
      68       515834 :     for (NBEdge* outedge : outgoing) {
      69      1081126 :         for (NBEdge* e : incoming) {
      70              :             // @todo: check whether NBHelpers::relAngle is properly defined and whether it should really be used, here
      71       757327 :             const double signedAngle = NBHelpers::normRelAngle(e->getAngleAtNode(node), outedge->getAngleAtNode(node));
      72       757327 :             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       537475 :                 continue;
      81              :             }
      82       537920 :             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       537920 :             const bool badPermissions = ((outedge->getPermissions() & e->getPermissions() & ~SVC_PEDESTRIAN) == 0
      89        83049 :                                          && !geometryLike
      90       616283 :                                          && outedge->getPermissions() != e->getPermissions());
      91              :             if (e->getFromNode() == outedge->getToNode()
      92       208584 :                     && (angle > 120 || e->getFromNode()->getPosition() == e->getToNode()->getPosition())
      93       742806 :                     && !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       203257 :                 angle += 360;
     105              :             }
     106       537920 :             if (angle < 160) {
     107       318734 :                 if (angle > 135) {
     108              :                     // could be a turnaround with a green median island, look at
     109              :                     // angle further away from the junction
     110         4416 :                     const double inFA = getFarAngleAtNode(e, node);
     111         4416 :                     const double outFA = getFarAngleAtNode(outedge, node);
     112         4416 :                     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         4416 :                     if (signedFarAngle > -160) {
     119         3750 :                         continue;
     120              :                     }
     121              :                 } else {
     122       314318 :                     continue;
     123              :                 }
     124              :             }
     125       219852 :             if (badPermissions) {
     126              :                 // penalty
     127         5179 :                 angle -= 90;
     128              :             }
     129              :             Combination c;
     130       219852 :             c.from = e;
     131       219852 :             c.to = outedge;
     132       219852 :             c.angle = angle;
     133       219852 :             combinations.push_back(c);
     134              :         }
     135              :     }
     136              :     // sort combinations so that the ones with the highest angle are at the begin
     137       192035 :     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       411887 :     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       432103 :         if (seen.find((*j).from) != seen.end() || seen.find((*j).to) != seen.end()) {
     151              :             // do not regard already set edges
     152         9470 :             if ((*j).angle > 360 && warn) {
     153          462 :                 WRITE_WARNINGF(TL("Ambiguity in turnarounds computation at junction '%'."), node->getID());
     154              :                 //std::cout << "  already seen: " << toString(seen) << "\n";
     155              :                 warn = false;
     156              :             }
     157         9470 :             continue;
     158              :         }
     159              :         // mark as seen
     160       210382 :         seen.insert((*j).from);
     161       210382 :         seen.insert((*j).to);
     162              :         // set turnaround information
     163       210382 :         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       210382 :         (*j).from->setTurningDestination((*j).to, onlyPossible);
     170              :     }
     171       192035 : }
     172              : 
     173              : 
     174              : double
     175         8832 : 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         8832 :     if (e->getToNode() == n) {
     181              :         // search upstream
     182         4416 :         atNode = e->getGeometry().back();
     183         4757 :         while (dist > 0) {
     184         4757 :             const double length = next->getGeometry().length();
     185         4757 :             if (dist <= length) {
     186         1692 :                 far = next->getGeometry().positionAtOffset(length - dist);
     187         1692 :                 break;
     188              :             } else {
     189         3065 :                 far = next->getGeometry().front();
     190         3065 :                 dist -= length;
     191         3065 :                 if (next->getToNode()->getIncomingEdges().size() == 1) {
     192          341 :                     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         4416 :         atNode = e->getGeometry().front();
     203         5584 :         while (dist > 0) {
     204         5584 :             const double length = next->getGeometry().length();
     205         5584 :             if (dist <= length) {
     206         2158 :                 far = next->getGeometry().positionAtOffset(dist);
     207         2158 :                 break;
     208              :             } else {
     209         3426 :                 far = next->getGeometry().back();
     210         3426 :                 dist -= length;
     211         3426 :                 if (next->getToNode()->getOutgoingEdges().size() == 1) {
     212         1168 :                     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         8832 :     return GeomHelper::legacyDegree(angle);
     222              : }
     223              : 
     224              : 
     225              : // ---------------------------------------------------------------------------
     226              : // NBNodesEdgesSorter
     227              : // ---------------------------------------------------------------------------
     228              : void
     229         5145 : NBNodesEdgesSorter::sortNodesEdges(NBNodeCont& nc, bool useNodeShape) {
     230       174559 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     231       169414 :         i->second->sortEdges(useNodeShape);
     232              :     }
     233         5145 : }
     234              : 
     235              : 
     236              : void
     237       557276 : NBNodesEdgesSorter::swapWhenReversed(const NBNode* const n,
     238              :                                      const std::vector<NBEdge*>::iterator& i1,
     239              :                                      const std::vector<NBEdge*>::iterator& i2) {
     240       557276 :     NBEdge* e1 = *i1;
     241       557276 :     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       557276 :     if (e2->getToNode() == n && e2->isTurningDirectionAt(e1)) {
     247              :         std::swap(*i1, *i2);
     248              :     }
     249       557276 : }
     250              : 
     251              : 
     252              : // ---------------------------------------------------------------------------
     253              : // NBNodeTypeComputer
     254              : // ---------------------------------------------------------------------------
     255              : void
     256         1695 : NBNodeTypeComputer::computeNodeTypes(NBNodeCont& nc, NBTrafficLightLogicCont& tlc) {
     257         1695 :     validateRailCrossings(nc, tlc);
     258         1695 :     const OptionsCont& oc = OptionsCont::getOptions();
     259         3390 :     const double rightBeforeLeftSpeed = oc.getFloat("junctions.right-before-left.speed-threshold");
     260        52457 :     for (const auto& nodeIt : nc) {
     261        50762 :         NBNode* const n = nodeIt.second;
     262              :         // the type may already be set from the data
     263        50762 :         if (n->myType != SumoXMLNodeType::UNKNOWN && n->myType != SumoXMLNodeType::DEAD_END) {
     264        23750 :             n->myTypeWasGuessed = false;
     265        23750 :             continue;
     266              :         }
     267              :         // check whether the node was set to be unregulated by the user
     268        81036 :         if (oc.getBool("keep-nodes-unregulated") || oc.isInStringVector("keep-nodes-unregulated.explicit", n->getID())
     269       108048 :                 || (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        27105 :         for (NBEdge* e : n->getEdges()) {
     276        27077 :             if (!isWaterway(e->getPermissions())) {
     277              :                 waterway = false;
     278              :                 break;
     279              :             }
     280              :         }
     281        27012 :         if (waterway && (n->myType == SumoXMLNodeType::UNKNOWN || n->myType == SumoXMLNodeType::DEAD_END)) {
     282           28 :             n->myType = SumoXMLNodeType::NOJUNCTION;
     283           28 :             continue;
     284              :         }
     285              : 
     286              :         // check whether the junction is not a real junction
     287        26984 :         if (n->myIncomingEdges.size() == 1) {
     288        13063 :             n->myType = SumoXMLNodeType::PRIORITY;
     289        13063 :             continue;
     290              :         }
     291              :         // @todo "isSimpleContinuation" should be revalidated
     292        13921 :         if (n->isSimpleContinuation()) {
     293         1322 :             n->myType = SumoXMLNodeType::PRIORITY;
     294         1322 :             continue;
     295              :         }
     296        12599 :         if (isRailwayNode(n)) {
     297              :             // priority instead of unregulated to ensure that collisions can be detected
     298          317 :             n->myType = SumoXMLNodeType::PRIORITY;
     299          317 :             continue;
     300              :         }
     301              :         // determine the type
     302        24564 :         SumoXMLNodeType type = oc.getBool("junctions.left-before-right") ? SumoXMLNodeType::LEFT_BEFORE_RIGHT : SumoXMLNodeType::RIGHT_BEFORE_LEFT;
     303        38030 :         for (EdgeVector::const_iterator i = n->myIncomingEdges.begin(); i != n->myIncomingEdges.end(); i++) {
     304        35909 :             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        19325 :                 if (n->getOppositeIncoming(*j) == *i && n->myIncomingEdges.size() > 2) {
     307         5278 :                     continue;
     308              :                 }
     309              :                 // @todo check against a legal document
     310              :                 // @todo figure out when SumoXMLNodeType::PRIORITY_STOP is appropriate
     311        14047 :                 const double s1 = (*i)->getSpeed();
     312        14047 :                 const double s2 = (*j)->getSpeed();
     313        14047 :                 const int p1 = (*i)->getPriority();
     314        14047 :                 const int p2 = (*j)->getPriority();
     315        23353 :                 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        12282 :         n->myType = type;
     323        12282 :         n->myTypeWasGuessed = true;
     324              :     }
     325         1695 : }
     326              : 
     327              : 
     328              : void
     329         1842 : NBNodeTypeComputer::validateRailCrossings(NBNodeCont& nc, NBTrafficLightLogicCont& tlc) {
     330        75237 :     for (std::map<std::string, NBNode*>::const_iterator i = nc.begin(); i != nc.end(); ++i) {
     331        73395 :         NBNode* n = (*i).second;
     332        73395 :         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         2201 :             for (NBEdge* e : n->getIncomingEdges()) {
     340         1458 :                 if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
     341          566 :                     numNonRailIn += 1;
     342          566 :                     if (e->getPermissions() != SVC_PEDESTRIAN) {
     343          160 :                         numNonRailwayNonPed++;
     344              :                     }
     345          566 :                     nonRailNodes.insert(e->getFromNode());
     346          892 :                 } else if ((e->getPermissions() & SVC_RAIL_CLASSES) != 0) {
     347          892 :                     numRailway++;
     348              :                 }
     349              :             }
     350         2195 :             for (NBEdge* e : n->getOutgoingEdges()) {
     351         1452 :                 if ((e->getPermissions() & ~SVC_RAIL_CLASSES) != 0) {
     352          563 :                     numNonRailOut += 1;
     353          563 :                     nonRailNodes.insert(e->getToNode());
     354              :                 }
     355              :             }
     356          743 :             if (numNonRailIn == 0 || numNonRailOut == 0 || numRailway == 0) {
     357              :                 // not a crossing (maybe unregulated or rail_signal)
     358          909 :                 WRITE_WARNINGF(TL("Converting invalid rail_crossing to priority junction '%'."), n->getID());
     359          303 :                 n->myType = SumoXMLNodeType::PRIORITY;
     360          440 :             } else if (numNonRailwayNonPed > 2 || nonRailNodes.size() > 2) {
     361              :                 // does not look like a rail crossing (roads in conflict). maybe a traffic light?
     362           75 :                 WRITE_WARNINGF(TL("Converting invalid rail_crossing to traffic_light at junction '%'."), n->getID());
     363           25 :                 TrafficLightType type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
     364           25 :                 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(n->getID(), n, 0, type);
     365           25 :                 n->myType = SumoXMLNodeType::TRAFFIC_LIGHT;
     366           25 :                 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         1842 : }
     377              : 
     378              : 
     379              : bool
     380       102463 : NBNodeTypeComputer::isRailwayNode(const NBNode* n) {
     381              :     bool hasRailway = false;
     382       107636 :     for (NBEdge* e : n->getIncomingEdges()) {
     383       103071 :         if ((e->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_TAXI)) != 0) {
     384              :             return false;
     385         5173 :         } 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         1695 : NBEdgePriorityComputer::computeEdgePriorities(NBNodeCont& nc) {
     397        52457 :     for (const auto& node : nc) {
     398              :         // preset all junction's edge priorities to zero
     399       232370 :         for (NBEdge* const edge : node.second->myAllEdges) {
     400       181608 :             edge->setJunctionPriority(node.second, NBEdge::JunctionPriority::MINOR_ROAD);
     401              :         }
     402        50762 :         node.second->markBentPriority(false);
     403              :         // check if the junction is not a real junction
     404        50762 :         if (node.second->myIncomingEdges.size() == 1 && node.second->myOutgoingEdges.size() == 1) {
     405        14411 :             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        27072 :             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        27068 :                 setPriorityJunctionPriorities(*node.second);
     418              :             }
     419              :         }
     420              :     }
     421         1695 : }
     422              : 
     423              : 
     424              : void
     425        27460 : NBEdgePriorityComputer::setPriorityJunctionPriorities(NBNode& n, bool forceStraight) {
     426        27460 :     if (n.myIncomingEdges.size() == 0 || n.myOutgoingEdges.size() == 0) {
     427        13113 :         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        24111 :     if (forceStraight) {
     434              :         // called a second time, preset all junction's edge priorities to zero
     435         2734 :         for (NBEdge* const edge : n.myAllEdges) {
     436         2342 :             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         2342 :             maxSpeed = MAX2(maxSpeed, edge->getSpeed());
     441              :         }
     442              :     }
     443        24111 :     EdgeVector incoming = n.myIncomingEdges;
     444        24111 :     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        24111 :     std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_by_priority_sorter());
     449              :     EdgeVector bestIncoming;
     450        24111 :     NBEdge* bestIn = incoming[0];
     451        71009 :     while (incoming.size() > 0 && (forceStraight || samePriority(bestIn, incoming[0]))) {
     452        46898 :         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        24111 :     sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_by_priority_sorter());
     458              :     EdgeVector bestOutgoing;
     459        24111 :     const NBEdge* const firstOut = outgoing[0];
     460        73287 :     while (outgoing.size() > 0 && (forceStraight || samePriority(firstOut, outgoing[0]))) { //->getPriority()==best->getPriority())
     461        49176 :         bestOutgoing.push_back(*outgoing.begin());
     462              :         outgoing.erase(outgoing.begin());
     463              :     }
     464              :     // special case: user input makes mainDirection unambiguous
     465              :     const bool mainDirectionExplicit = (
     466         9764 :                                            bestIncoming.size() == 1 && n.myIncomingEdges.size() <= 2
     467         8173 :                                            && (incoming.size() == 0 || bestIncoming[0]->getPriority() > incoming[0]->getPriority())
     468         6883 :                                            && bestOutgoing.size() == 1 && n.myOutgoingEdges.size() <= 2
     469         3919 :                                            && (outgoing.size() == 0 || bestOutgoing[0]->getPriority() > outgoing[0]->getPriority())
     470        27486 :                                            && !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        24111 :     incoming = n.myIncomingEdges;
     478        24111 :     outgoing = n.myOutgoingEdges;
     479        71009 :     for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
     480        93796 :         std::sort(incoming.begin(), incoming.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
     481        46898 :         counterIncomingEdges[*i] = *incoming.begin();
     482        93796 :         std::sort(outgoing.begin(), outgoing.end(), NBContHelper::edge_opposite_direction_sorter(*i, &n, !forceStraight));
     483        46898 :         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        24111 :     if (bestIncoming.size() == 1) {
     513              :         // let's mark this road as the best
     514         9764 :         NBEdge* best1 = extractAndMarkFirst(n, bestIncoming);
     515        16474 :         if (!mainDirectionExplicit && counterIncomingEdges.find(best1) != counterIncomingEdges.end()) {
     516              :             // ok, look, what we want is the opposite of the straight continuation edge
     517              :             // but, what if such an edge does not exist? By now, we'll determine it
     518              :             // geometrically
     519         6710 :             NBEdge* s = counterIncomingEdges.find(best1)->second;
     520         6710 :             const double minAngleDiff = GeomHelper::getMinAngleDiff(best1->getAngleAtNode(&n), s->getAngleAtNode(&n));
     521              :             if (minAngleDiff > 180 - 45
     522         6710 :                     || (minAngleDiff > 75 && s->getPriority() == best1->getPriority() && hasDifferentPriorities(incoming, best1))) {
     523         1537 :                 s->setJunctionPriority(&n, NBEdge::PRIORITY_ROAD);
     524              :             }
     525              :         }
     526         9764 :         markBestParallel(n, best1, nullptr);
     527              :         assert(bestOutgoing.size() != 0);
     528              :         // mark the best outgoing as the continuation
     529         9764 :         sort(bestOutgoing.begin(), bestOutgoing.end(), NBContHelper::edge_similar_direction_sorter(best1));
     530              :         // assign extra priority if the priorities are unambiguous (regardless of geometry)
     531         9764 :         NBEdge* bestOut = extractAndMarkFirst(n, bestOutgoing);
     532        16474 :         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, NBEdge::PRIORITY_ROAD);
     536              :             }
     537              :         }
     538         9764 :         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         9764 :         if (isBent && hasTLS && !forceStraight) {
     545              :             // redo but force straight computation
     546          191 :             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        51481 :     for (i = bestIncoming.begin(); i != bestIncoming.end(); ++i) {
     561              :         EdgeVector::iterator j;
     562        37134 :         NBEdge* t1 = *i;
     563        37134 :         double angle1 = t1->getAngleAtNode(&n) + 180;
     564        37134 :         if (angle1 >= 360) {
     565            0 :             angle1 -= 360;
     566              :         }
     567        71822 :         for (j = i + 1; j != bestIncoming.end(); ++j) {
     568        34688 :             NBEdge* t2 = *j;
     569        34688 :             double angle2 = t2->getAngleAtNode(&n) + 180;
     570        34688 :             if (angle2 >= 360) {
     571            0 :                 angle2 -= 360;
     572              :             }
     573        34688 :             double score = forceStraight ? getScore(t1, t2, minPrio, maxPrio, maxNumLanes, maxSpeed) : 0;
     574        34688 :             double angle = GeomHelper::getMinAngleDiff(angle1, angle2) + 45 * score;
     575        34688 :             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        19968 :                 bestFirst = *i;
     579        19968 :                 bestSecond = *j;
     580              :             }
     581              :         }
     582              :     }
     583        14347 :     bestFirst->setJunctionPriority(&n, NBEdge::PRIORITY_ROAD);
     584        14347 :     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        14347 :     if (bestOutgoing.size() != 0) {
     591        14347 :         extractAndMarkFirst(n, bestOutgoing);
     592              :     }
     593        14347 :     bestSecond->setJunctionPriority(&n, NBEdge::PRIORITY_ROAD);
     594        14347 :     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        14347 :     if (bestOutgoing.size() != 0) {
     601        12934 :         extractAndMarkFirst(n, bestOutgoing);
     602              :     }
     603        14347 :     const bool isBent = GeomHelper::getMinAngleDiff(bestFirst->getAngleAtNode(&n), bestSecond->getAngleAtNode(&n)) < 135;
     604        14347 :     if (isBent && hasTLS && !forceStraight) {
     605              :         // redo but force straight computation
     606          201 :         setPriorityJunctionPriorities(n, true);
     607              :     } else {
     608              :         n.markBentPriority(isBent);
     609        14146 :         markBestParallel(n, bestFirst, bestSecond);
     610              :     }
     611        24111 : }
     612              : 
     613              : double
     614         1598 : 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         1598 :     if (minPrio != maxPrio) {
     619          955 :         normPrio1 = ((e1->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
     620          955 :         normPrio2 = ((e2->getPriority() - minPrio) / (maxPrio - minPrio)) * 0.9 + 0.1;
     621              :     }
     622              :     return (normPrio1
     623         1598 :             * e1->getNumLanes() / maxNumLanes
     624         1598 :             * e1->getSpeed() / maxSpeed
     625         1598 :             * normPrio2
     626         1598 :             * e2->getNumLanes() / maxNumLanes
     627         1598 :             * e2->getSpeed() / maxSpeed);
     628              : }
     629              : 
     630              : void
     631        23910 : NBEdgePriorityComputer::markBestParallel(const NBNode& n, NBEdge* bestFirst, NBEdge* bestSecond) {
     632              :     // edges running parallel to the main direction should also be prioritised
     633        23910 :     const double a1 = bestFirst->getAngleAtNode(&n);
     634        23910 :     const double a2 = bestSecond == nullptr ? a1 : bestSecond->getAngleAtNode(&n);
     635        23910 :     SVCPermissions p1 = bestFirst->getPermissions();
     636        23910 :     SVCPermissions p2 = bestSecond == nullptr ? p1 : bestSecond->getPermissions();
     637        83864 :     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        59954 :         SVCPermissions perm = e->getPermissions();
     641        59954 :         if (((GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a1) < 10
     642        35255 :                 || GeomHelper::getMinAngleDiff(e->getAngleAtNode(&n), a2) < 10))
     643        74172 :                 && (p1 & perm) == 0 && (p2 & perm) == 0) {
     644          348 :             e->setJunctionPriority(&n, NBEdge::PRIORITY_ROAD);
     645              :         }
     646              :     }
     647        23910 : }
     648              : 
     649              : 
     650              : NBEdge*
     651        46809 : NBEdgePriorityComputer::extractAndMarkFirst(NBNode& n, std::vector<NBEdge*>& s, int prio) {
     652        46809 :     if (s.size() == 0) {
     653              :         return nullptr;
     654              :     }
     655        46809 :     NBEdge* ret = s.front();
     656              :     s.erase(s.begin());
     657        46809 :     ret->setJunctionPriority(&n, prio);
     658        46809 :     return ret;
     659              : }
     660              : 
     661              : 
     662              : bool
     663       113094 : NBEdgePriorityComputer::samePriority(const NBEdge* const e1, const NBEdge* const e2) {
     664       113094 :     if (e1 == e2) {
     665              :         return true;
     666              :     }
     667        65656 :     if (e1->getPriority() != e2->getPriority()) {
     668              :         return false;
     669              :     }
     670        53259 :     if ((int) e1->getSpeed() != (int) e2->getSpeed()) {
     671              :         return false;
     672              :     }
     673        49803 :     return (int) e1->getNumLanes() == (int) e2->getNumLanes();
     674              : }
     675              : 
     676              : 
     677              : bool
     678          672 : NBEdgePriorityComputer::hasDifferentPriorities(const EdgeVector& edges, const NBEdge* excluded) {
     679          672 :     if (edges.size() < 2) {
     680              :         return false;
     681              :     }
     682          672 :     int prio = edges[0] == excluded ? edges[1]->getPriority() : edges[0]->getPriority();
     683         2162 :     for (auto e : edges) {
     684         1562 :         if (e != excluded && e->getPriority() != prio) {
     685              :             return true;
     686              :         }
     687              :     }
     688              :     return false;
     689              : }
     690              : 
     691              : 
     692       170659 : NBNodesEdgesSorter::crossing_by_junction_angle_sorter::crossing_by_junction_angle_sorter(const NBNode* node, const EdgeVector& ordering) {
     693              :     // reorder based on getAngleAtNodeToCenter
     694       170659 :     myOrdering = ordering;
     695       170659 :     sort(myOrdering.begin(), myOrdering.end(), NBContHelper::edge_by_angle_to_nodeShapeCentroid_sorter(node));
     696              :     // let the first edge remain the first
     697       170659 :     rotate(myOrdering.begin(), std::find(myOrdering.begin(), myOrdering.end(), ordering.front()), myOrdering.end());
     698       170659 : }
     699              : 
     700              : 
     701              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1