Eclipse SUMO - Simulation of Urban MObility
NBNodeCont.cpp
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
24 // Container for nodes during the netbuilding process
25 /****************************************************************************/
26 #include <config.h>
27 
28 #include <string>
29 #include <map>
30 #include <algorithm>
31 #include <cmath>
33 #include <utils/geom/Boundary.h>
34 #include <utils/geom/GeomHelper.h>
39 #include <utils/common/StdDefs.h>
40 #include <utils/common/ToString.h>
46 #include "NBHelpers.h"
47 #include "NBAlgorithms.h"
48 #include "NBDistrict.h"
49 #include "NBEdgeCont.h"
51 #include "NBOwnTLDef.h"
52 #include "NBPTStop.h"
53 #include "NBNodeCont.h"
54 #include "NBPTStopCont.h"
55 #include "NBPTLineCont.h"
56 #include "NBParking.h"
57 
58 // ===========================================================================
59 // Algorithm constants
60 // ===========================================================================
61 #define MAX_SLIPLANE_LENGTH 1000
62 
63 // ===========================================================================
64 // Debug Flags
65 // ===========================================================================
66 
67 //#define DEBUG_JOINJUNCTIONS
68 //#define DEBUG_JOINJUNCTIONS_CONNECTIONS
69 //#define DEBUG_GUESSSIGNALS
70 #define DEBUGNODEID ""
71 #define DEBUGNODEID2 ""
72 //#define DEBUGNODEID "5548037023"
73 #define DEBUGCOND(obj) ((obj) != 0 && ((obj)->getID() == DEBUGNODEID || (obj)->getID() == DEBUGNODEID2))
74 //#define DEBUGCOND(obj) (true)
75 
76 
77 // ===========================================================================
78 // method definitions
79 // ===========================================================================
81  clear();
82 }
83 
84 
85 // ----------- Insertion/removal/retrieval of nodes
86 bool
87 NBNodeCont::insert(const std::string& id, const Position& position,
88  NBDistrict* district) {
89  NodeCont::iterator i = myNodes.find(id);
90  if (i != myNodes.end()) {
91  return false;
92  }
93  NBNode* node = new NBNode(id, position, district);
94  myNodes[id] = node;
95  const float pos[2] = {(float)position.x(), (float)position.y()};
96  myRTree.Insert(pos, pos, node);
97  return true;
98 }
99 
100 
101 bool
103  std::string id = node->getID();
104  NodeCont::iterator i = myNodes.find(id);
105  if (i != myNodes.end()) {
106  return false;
107  }
108  myNodes[id] = node;
109  const float pos[2] = {(float)node->getPosition().x(), (float)node->getPosition().y()};
110  myRTree.Insert(pos, pos, node);
111  return true;
112 }
113 
114 
115 NBNode*
116 NBNodeCont::retrieve(const std::string& id) const {
117  NodeCont::const_iterator i = myNodes.find(id);
118  if (i == myNodes.end()) {
119  return nullptr;
120  }
121  return (*i).second;
122 }
123 
124 
125 NBNode*
126 NBNodeCont::retrieve(const Position& position, const double offset) const {
127  const double extOffset = offset + POSITION_EPS;
128  const float cmin[2] = {(float)(position.x() - extOffset), (float)(position.y() - extOffset)};
129  const float cmax[2] = {(float)(position.x() + extOffset), (float)(position.y() + extOffset)};
130  std::set<const Named*> into;
131  Named::StoringVisitor sv(into);
132  myRTree.Search(cmin, cmax, sv);
133  for (const Named* namedNode : into) {
134  NBNode* node = const_cast<NBNode*>(dynamic_cast<const NBNode*>(namedNode));
135  if (fabs(node->getPosition().x() - position.x()) <= offset
136  &&
137  fabs(node->getPosition().y() - position.y()) <= offset) {
138  return node;
139  }
140  }
141  return nullptr;
142 }
143 
144 
145 bool
147  if (extract(node)) {
148  delete node;
149  return true;
150  } else {
151  return false;
152  }
153 }
154 
155 
156 bool
157 NBNodeCont::extract(NBNode* node, bool remember) {
158  NodeCont::iterator i = myNodes.find(node->getID());
159  if (i == myNodes.end()) {
160  return false;
161  }
162  myNodes.erase(i);
163  const float pos[2] = {(float)node->getPosition().x(), (float)node->getPosition().y()};
164  myRTree.Remove(pos, pos, node);
165  node->removeTrafficLights();
166  if (remember) {
167  myExtractedNodes[node->getID()] = node;
168  }
169  return true;
170 }
171 
172 
173 // ----------- Adapting the input
174 int
176  int no = 0;
177  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
178  no += (*i).second->removeSelfLoops(dc, ec, tc);
179  }
180  if (no != 0) {
181  WRITE_WARNING(toString(no) + " self-looping edge(s) removed.");
182  }
183  return no;
184 }
185 
186 
187 void
189  // magic values
190  const double distanceThreshold = 7.; // don't merge edges further apart
191  const double lengthThreshold = 0.10; // don't merge edges with higher relative length-difference
192 
193  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
194  // count the edges to other nodes outgoing from the current node
195  std::map<NBNode*, EdgeVector> connectionCount;
196  const EdgeVector& outgoing = (*i).second->getOutgoingEdges();
197  for (EdgeVector::const_iterator j = outgoing.begin(); j != outgoing.end(); j++) {
198  connectionCount[(*j)->getToNode()].push_back(*j);
199  }
200  // check whether more than a single edge connect another node and join them
201  std::map<NBNode*, EdgeVector>::iterator k;
202  for (k = connectionCount.begin(); k != connectionCount.end(); k++) {
203  // possibly we do not have anything to join...
204  if ((*k).second.size() < 2) {
205  continue;
206  }
207  // for the edges that seem to be a single street,
208  // check whether the geometry is similar
209  const EdgeVector& ev = (*k).second;
210  const NBEdge* const first = ev.front();
211  EdgeVector::const_iterator jci; // join candidate iterator
212  for (jci = ev.begin() + 1; jci != ev.end(); ++jci) {
213  const double relativeLengthDifference = fabs(first->getLoadedLength() - (*jci)->getLoadedLength()) / first->getLoadedLength();
214  if ((!first->isNearEnough2BeJoined2(*jci, distanceThreshold)) ||
215  (relativeLengthDifference > lengthThreshold) ||
216  (fabs(first->getSpeed() - (*jci)->getSpeed()) >= 0.01) || // output accuracy
217  (first->getPermissions() != (*jci)->getPermissions())
218  ) {
219  break;
220  }
221  }
222  // @bug If there are 3 edges of which 2 can be joined, no joining will
223  // take place with the current implementation
224  if (jci == ev.end()) {
225  if (removeDuplicates) {
226  for (int ei = 1; ei < (int)ev.size(); ei++) {
227  ec.extract(dc, ev[ei], true);
228  }
229  } else {
230  ec.joinSameNodeConnectingEdges(dc, tlc, ev);
231  }
232  }
233  }
234  }
235 }
236 
237 
238 int
240  int numRemovedEdges = 0;
241  // Warn of isolated edges, i.e. a single edge with no connection to another edge
242  const std::vector<std::string>& edgeNames = ec.getAllNames();
243  for (std::vector<std::string>::const_iterator it = edgeNames.begin(); it != edgeNames.end(); ++it) {
244  // Test whether this node starts at a dead end, i.e. it has only one adjacent node
245  // to which an edge exists and from which an edge may come.
246  NBEdge* e = ec.retrieve(*it);
247  if (e == nullptr) {
248  continue;
249  }
250  NBNode* from = e->getFromNode();
251  const EdgeVector& outgoingEdges = from->getOutgoingEdges();
252  if (outgoingEdges.size() != 1) {
253  // At this node, several edges or no edge start; so, this node is no dead end.
254  continue;
255  }
256  const EdgeVector& incomingEdges = from->getIncomingEdges();
257  if (incomingEdges.size() > 1) {
258  // At this node, several edges end; so, this node is no dead end.
259  continue;
260  } else if (incomingEdges.size() == 1) {
261  NBNode* fromNodeOfIncomingEdge = incomingEdges[0]->getFromNode();
262  NBNode* toNodeOfOutgoingEdge = outgoingEdges[0]->getToNode();
263  if (fromNodeOfIncomingEdge != toNodeOfOutgoingEdge) {
264  // At this node, an edge ends which is not the inverse direction of
265  // the starting node.
266  continue;
267  }
268  }
269  // Now we know that the edge e starts a dead end.
270  // Next we test if the dead end is isolated, i.e. does not lead to a junction
271  bool hasJunction = false;
272  EdgeVector road;
273  NBEdge* eOld = nullptr;
274  NBNode* to;
275  NodeSet adjacentNodes;
276  do {
277  road.push_back(e);
278  eOld = e;
279  from = e->getFromNode();
280  to = e->getToNode();
281  const EdgeVector& outgoingEdgesOfToNode = to->getOutgoingEdges();
282  const EdgeVector& incomingEdgesOfToNode = to->getIncomingEdges();
283  adjacentNodes.clear();
284  for (EdgeVector::const_iterator itOfOutgoings = outgoingEdgesOfToNode.begin(); itOfOutgoings != outgoingEdgesOfToNode.end(); ++itOfOutgoings) {
285  if ((*itOfOutgoings)->getToNode() != from // The back path
286  && (*itOfOutgoings)->getToNode() != to // A loop / dummy edge
287  ) {
288  e = *itOfOutgoings; // Probably the next edge
289  }
290  adjacentNodes.insert((*itOfOutgoings)->getToNode());
291  }
292  for (EdgeVector::const_iterator itOfIncomings = incomingEdgesOfToNode.begin(); itOfIncomings != incomingEdgesOfToNode.end(); ++itOfIncomings) {
293  adjacentNodes.insert((*itOfIncomings)->getFromNode());
294  }
295  adjacentNodes.erase(to); // Omit loops
296  if (adjacentNodes.size() > 2) {
297  hasJunction = true;
298  }
299  } while (!hasJunction && eOld != e);
300  if (!hasJunction) {
301  std::string warningString;
302  for (EdgeVector::iterator roadIt = road.begin(); roadIt != road.end(); ++roadIt) {
303  if (roadIt == road.begin()) {
304  warningString += (*roadIt)->getID();
305  } else {
306  warningString += "," + (*roadIt)->getID();
307  }
308 
309  NBNode* fromNode = (*roadIt)->getFromNode();
310  NBNode* toNode = (*roadIt)->getToNode();
311  ec.erase(dc, *roadIt);
312  numRemovedEdges++;
313  if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
314  // Node is empty; can be removed
315  erase(fromNode);
316  }
317  if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
318  // Node is empty; can be removed
319  erase(toNode);
320  }
321  }
322  WRITE_WARNINGF(TL("Removed a road without junctions: %."), warningString);
323  }
324  }
325  return numRemovedEdges;
326 }
327 
328 
329 int
330 NBNodeCont::removeComponents(NBDistrictCont& dc, NBEdgeCont& ec, const int numKeep, bool hasPTStops) {
331  myRailComponents.clear();
332  std::vector<std::set<NBEdge*> > components;
333  // need to use ids here to have the same ordering on all platforms
334  std::set<std::string> edgesLeft;
335  for (std::map<std::string, NBEdge*>::const_iterator edgeIt = ec.begin(); edgeIt != ec.end(); ++edgeIt) {
336  edgesLeft.insert(edgeIt->first);
337  }
338  EdgeVector queue;
339  std::set<NBEdge*> toRemove;
340  int foundComponents = 0;
341  int numRemoved = 0;
342  while (!edgesLeft.empty()) {
343  queue.push_back(ec.getByID(*edgesLeft.begin()));
344  std::set<NBEdge*> component;
345  while (!queue.empty()) {
346  NBEdge* const e = queue.back();
347  queue.pop_back();
348  component.insert(e);
349  std::vector<EdgeVector> edgeLists;
350  edgeLists.push_back(e->getFromNode()->getOutgoingEdges());
351  edgeLists.push_back(e->getFromNode()->getIncomingEdges());
352  edgeLists.push_back(e->getToNode()->getOutgoingEdges());
353  edgeLists.push_back(e->getToNode()->getIncomingEdges());
354  for (std::vector<EdgeVector>::const_iterator listIt = edgeLists.begin(); listIt != edgeLists.end(); ++listIt) {
355  for (EdgeVector::const_iterator edgeIt = listIt->begin(); edgeIt != listIt->end(); ++edgeIt) {
356  std::set<std::string>::iterator leftIt = edgesLeft.find((*edgeIt)->getID());
357  if (leftIt != edgesLeft.end()) {
358  queue.push_back(*edgeIt);
359  edgesLeft.erase(leftIt);
360  }
361  }
362  }
363  }
364  foundComponents++;
365  std::vector<std::set<NBEdge*> >::iterator cIt;
366  for (cIt = components.begin(); cIt != components.end(); ++cIt) {
367  if (cIt->size() < component.size()) {
368  break;
369  }
370  }
371  components.insert(cIt, component);
372  if ((int)components.size() > numKeep) {
373  bool recheck = false;
374  if (hasPTStops) {
375  for (NBEdge* e : components.back()) {
376  SVCPermissions permissions = e->getPermissions();
377  if (isRailway(permissions) || isWaterway(permissions)) {
378  // recheck for connection to other components via access definitions
379  recheck = true;
380  break;
381  }
382  }
383  }
384  if (!recheck) {
385  toRemove.insert(components.back().begin(), components.back().end());
386  numRemoved++;
387  } else {
388  std::vector<std::string> edgeIDs;
389  for (NBEdge* e : components.back()) {
390  edgeIDs.push_back(e->getID());
391  }
392  myRailComponents.push_back(edgeIDs);
393  }
394  components.pop_back();
395  }
396  }
397  ec.removeRoundaboutEdges(toRemove);
398  for (NBEdge* e : toRemove) {
399  NBNode* const fromNode = e->getFromNode();
400  NBNode* const toNode = e->getToNode();
401  ec.erase(dc, e);
402  if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
403  erase(fromNode);
404  }
405  if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
406  erase(toNode);
407  }
408  }
409  if (foundComponents > 1) {
410  WRITE_MESSAGEF(TL("Found % components and removed % (% edges)."), toString(foundComponents), toString(numRemoved), toString(toRemove.size()));
411  }
412  return (int)toRemove.size();
413 }
414 
415 
416 int
418  std::set<std::string> stopEdges;
419  for (const auto& item : sc.getStops()) {
420  stopEdges.insert(item.second->getEdgeId());
421  }
422  int numRemoved = 0;
423  int numRemovedEdges = 0;
424  for (auto& component : myRailComponents) {
425  bool keep = false;
426  for (std::string edgeID : component) {
427  if (stopEdges.count(edgeID) != 0) {
428  keep = true;
429  break;
430  }
431  }
432  if (!keep) {
433  numRemoved++;
434  numRemovedEdges += (int)component.size();
435  for (std::string edgeID : component) {
436  NBEdge* e = ec.retrieve(edgeID);
437  if (e != nullptr) {
438  NBNode* const fromNode = e->getFromNode();
439  NBNode* const toNode = e->getToNode();
440  ec.erase(dc, e);
441  if (fromNode->getIncomingEdges().size() == 0 && fromNode->getOutgoingEdges().size() == 0) {
442  erase(fromNode);
443  }
444  if (toNode->getIncomingEdges().size() == 0 && toNode->getOutgoingEdges().size() == 0) {
445  erase(toNode);
446  }
447  }
448  }
449  }
450  }
451  if (numRemoved > 0) {
452  WRITE_MESSAGEF(TL("Removed % railway components (% edges)."), toString(numRemoved), toString(numRemovedEdges));
453  }
454  return numRemoved;
455 }
456 
457 
458 int
461  NBPTLineCont& lc,
462  NBParkingCont& pc,
463  bool removeGeometryNodes) {
464  // load edges that shall not be modified
465  std::set<std::string> edges2keep;
466  if (removeGeometryNodes) {
467  const OptionsCont& oc = OptionsCont::getOptions();
468  if (oc.isSet("geometry.remove.keep-edges.input-file")) {
469  NBHelpers::loadEdgesFromFile(oc.getString("geometry.remove.keep-edges.input-file"), edges2keep);
470  }
471  if (oc.isSet("geometry.remove.keep-edges.explicit")) {
472  const std::vector<std::string> edges = oc.getStringVector("geometry.remove.keep-edges.explicit");
473  edges2keep.insert(edges.begin(), edges.end());
474  }
475  // no need to keep pt stop edges, they are remapped later
476  // no need to keep all pt route edges. They are validated again before writing
477  pc.addEdges2Keep(oc, edges2keep);
478  if (oc.exists("geometry.remove.keep-ptstops") && oc.getBool("geometry.remove.keep-ptstops")) {
479  sc.addEdges2Keep(oc, edges2keep);
480  }
481  }
482 
483  std::map<NBEdge*, std::set<NBTrafficLightDefinition*> > tlsLookup;
484  for (auto it = ec.begin(); it != ec.end(); it++) {
485  NBEdge* e = it->second;
486  NBNode* to = e->getToNode();
487  if (to->isTLControlled()) {
488  tlsLookup[e] = to->getControllingTLS();
489  }
490  }
491 
492  std::vector<NBNode*> toRemove;
493  for (const auto& i : myNodes) {
494  NBNode* const current = i.second;
495  bool remove = false;
496  // check for completely empty nodes and check for nodes which are only geometry nodes and ask the node whether to join
497  if (current->getEdges().empty() || (removeGeometryNodes && mySplit.count(current) == 0 && current->checkIsRemovable())) {
498  remove = true;
499  // check whether any of the edges must be kept
500  for (NBEdge* const it_edge : current->getEdges()) {
501  if (edges2keep.find(it_edge->getID()) != edges2keep.end()) {
502  remove = false;
503  break;
504  }
505  }
506  }
507  // remove the node and join the geometries when wished
508  if (!remove) {
509  continue;
510  }
511  for (const std::pair<NBEdge*, NBEdge*>& j : current->getEdgesToJoin()) {
512  NBEdge* const begin = j.first;
513  NBEdge* const continuation = j.second;
514  begin->append(continuation);
515  continuation->getToNode()->replaceIncoming(continuation, begin, 0);
516  auto itTL = tlsLookup.find(continuation);
517  if (itTL != tlsLookup.end()) {
518  for (NBTrafficLightDefinition* tls : itTL->second) {
519  tls->replaceRemoved(continuation, -1, begin, -1, true);
520  }
521  tlsLookup[begin] = itTL->second;
522  }
523  sc.replaceEdge(continuation->getID(), { begin });
524  lc.replaceEdge(continuation->getID(), { begin });
525  ec.extract(dc, continuation, true);
526  }
527  toRemove.push_back(current);
528  }
529  // erase all
530  for (NBNode* n : toRemove) {
531  extract(n, true);
532  }
533  return (int)toRemove.size();
534 }
535 
536 
537 void
539  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
540  (*i).second->avoidOverlap();
541  }
542 }
543 
544 
545 // ----------- (Helper) methods for joining nodes
546 void
547 NBNodeCont::generateNodeClusters(double maxDist, NodeClusters& into) const {
548  std::set<NBNode*> visited;
549  for (const auto& i : myNodes) {
550  if (visited.count(i.second) > 0) {
551  continue;
552  }
553  std::vector<NodeAndDist> toProc;
554  toProc.emplace_back(i.second, 0.);
555  NodeSet c;
556  while (!toProc.empty()) {
557  NBNode* const n = toProc.back().first;
558  const double dist = toProc.back().second;
559  toProc.pop_back();
560  if (visited.count(n) > 0) {
561  continue;
562  }
563  visited.insert(n);
564  bool pureRail = true;
565  bool railAndPeds = true;
566  for (NBEdge* e : n->getEdges()) {
567  if ((e->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_PEDESTRIAN)) != 0) {
568  railAndPeds = false;
569  pureRail = false;
570  break;
571  }
572  if ((e->getPermissions() & ~(SVC_RAIL_CLASSES)) != 0) {
573  pureRail = false;
574  }
575  }
576  if (pureRail) {
577  // do not join pure rail nodes
578  continue;
579  }
580  c.insert(n);
581  for (NBEdge* e : n->getEdges()) {
582  NBNode* s = n->hasIncoming(e) ? e->getFromNode() : e->getToNode();
583  const double length = e->getLoadedLength();
584 #ifdef DEBUG_JOINJUNCTIONS
585  if (DEBUGCOND(s)) {
586  std::cout << "generateNodeClusters: consider s=" << s->getID()
587  << " clusterNode=" << n->getID() << " edge=" << e->getID() << " dist=" << dist << " length=" << length << " with cluster " << joinNamedToString(c, ' ') << "\n";
588  }
589 #endif
590  if (railAndPeds && n->getType() != SumoXMLNodeType::RAIL_CROSSING) {
591  bool railAndPeds2 = true;
592  for (NBEdge* e2 : n->getEdges()) {
593  if ((e2->getPermissions() & ~(SVC_RAIL_CLASSES | SVC_PEDESTRIAN)) != 0) {
594  railAndPeds2 = false;
595  break;
596  }
597  }
598  if (railAndPeds2 && s->getType() != SumoXMLNodeType::RAIL_CROSSING) {
599  // do not join rail/ped nodes unless at a rail crossing
600  // (neither nodes nor the traffic lights)
601  continue;
602  }
603  }
604  const bool bothCrossing = n->getType() == SumoXMLNodeType::RAIL_CROSSING && s->getType() == SumoXMLNodeType::RAIL_CROSSING;
605  const bool joinPedCrossings = bothCrossing && e->getPermissions() == SVC_PEDESTRIAN;
606  if ( // never join pedestrian stuff (unless at a rail crossing
607  !joinPedCrossings && (
608  e->getPermissions() == SVC_PEDESTRIAN
609  // only join edges for regular passenger traffic or edges that are extremely short
610  || (length > 3 * POSITION_EPS
611  && (e->getPermissions() & (SVC_PASSENGER | SVC_TRAM)) == 0
613 #ifdef DEBUG_JOINJUNCTIONS
614  if (DEBUGCOND(s)) {
615  std::cout << " ignored s=" << s->getID() << " pedestrian edge=" << e->getID() << " cd=" << n->getPosition().distanceTo2D(s->getPosition()) << "\n";
616  }
617 #endif
618  continue;
619  }
620  // never join rail_crossings with other node types unless the crossing is only for tram
623  const SVCPermissions railNoTram = (SVC_RAIL_CLASSES & ~SVC_TRAM);
624  bool foundRail = false;
625  NBNode* crossingNode = n->getType() == SumoXMLNodeType::RAIL_CROSSING ? n : s;
626  for (NBEdge* e2 : crossingNode->getIncomingEdges()) {
627  if ((e2->getPermissions() & railNoTram) != 0) {
628  foundRail = true;
629  break;
630  }
631  }
632  if (foundRail) {
633  continue;
634  }
635  }
636  // never join rail_crossings via a rail edge
637  if (bothCrossing && (e->getPermissions() & ~SVC_RAIL_CLASSES) == 0) {
638  continue;
639  }
640  if (visited.find(s) != visited.end()) {
641  continue;
642  }
643  if (length + dist < maxDist) {
644  // don't add long "boring" appendages but always join the whole rail crossing or tls
645  const bool trueGeomLike = s->geometryLike();
646  if (trueGeomLike || geometryLikeForClass(s, SVC_VULNERABLE | SVC_DELIVERY)) {
647  const bool hasTLS = n->isTrafficLight() || s->isTrafficLight();
648  const double fullLength = e->getGeometry().length2D();
649  const double length2 = bothCrossing || hasTLS || trueGeomLike ? length : fullLength;
650  toProc.emplace_back(s, dist + length2);
651  } else {
652  toProc.emplace_back(s, 0.);
653  }
654  }
655  }
656  }
657  if (c.size() < 2) {
658  continue;
659  }
660 #ifdef DEBUG_JOINJUNCTIONS
661  std::cout << " DEBUG: consider cluster " << joinNamedToString(c, ' ') << "\n";
662 #endif
663  into.push_back(c);
664  }
665 }
666 
667 
668 bool
670  EdgeVector allowedIn;
671  EdgeVector allowedOut;
672  for (NBEdge* e : n->getIncomingEdges()) {
673  if ((e->getPermissions() & ~ignored) != 0) {
674  allowedIn.push_back(e);
675  }
676  }
677  for (NBEdge* e : n->getOutgoingEdges()) {
678  if ((e->getPermissions() & ~ignored) != 0) {
679  allowedOut.push_back(e);
680  }
681  }
682  if (allowedIn.size() > 0 && allowedOut.size() > 0) {
683  //std::cout << n->getID() << " geometryLikeForClass=" << n->geometryLike(allowedIn, allowedOut) << " in=" << toString(allowedIn) << " out=" << toString(allowedOut) << "\n";
684  return n->geometryLike(allowedIn, allowedOut);
685  }
686  return true;
687 }
688 
689 
690 void
691 NBNodeCont::addJoinExclusion(const std::vector<std::string>& ids) {
692  for (const std::string& nodeID : ids) {
693  // error handling has to take place here since joinExclusions could be
694  // loaded from multiple files / command line
695  if (myJoined.count(nodeID) > 0) {
696  WRITE_WARNINGF(TL("Ignoring join exclusion for junction '%' since it already occurred in a list of nodes to be joined."), nodeID);
697  } else {
698  myJoinExclusions.insert(nodeID);
699  }
700  }
701 }
702 
703 
704 std::string
705 NBNodeCont::createClusterId(const std::set<std::string>& cluster, const std::string& prefix) {
706  int maxIds = OptionsCont::getOptions().getInt("max-join-ids");
707  if (maxIds <= 0) {
708  maxIds = (int)cluster.size();
709  }
710  if ((int)cluster.size() > maxIds) {
711  auto clusterIt = cluster.begin();
712  std::string result = prefix + *clusterIt;
713  for (int i = 1; i < maxIds; i++) {
714  ++clusterIt;
715  result += "_" + *clusterIt;
716  }
717  return result + "_#" + toString((int)cluster.size() - maxIds) + "more";
718  }
719  return prefix + joinToString(cluster, "_");
720 }
721 
722 
723 void
724 NBNodeCont::addCluster2Join(const std::set<std::string>& cluster, NBNode* node) {
725  // error handling has to take place here since joins could be loaded from multiple files
726  std::set<std::string> validCluster;
727  for (std::string nodeID : cluster) {
728  if (myJoinExclusions.count(nodeID) > 0) {
729  WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' was already excluded from joining."), nodeID);
730  return;
731  } else if (myJoined.count(nodeID) > 0) {
732  WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' already occurred in another join-cluster."), nodeID);
733  return;
734  } else {
735  if (retrieve(nodeID) != nullptr) {
736  validCluster.insert(nodeID);
737  } else {
738  WRITE_ERRORF(TL("Unknown junction '%' in join-cluster."), nodeID);
739  }
740  }
741  }
742  if (validCluster.size() > 1) {
743  myJoined.insert(validCluster.begin(), validCluster.end());
744  myClusters2Join.push_back(std::make_pair(validCluster, node));
745  } else {
746  WRITE_WARNINGF(TL("Ignoring join-cluster '%' because it has size '%'."), node->getID(), validCluster.size());
747  }
748 }
749 
750 
751 int
753  int numJoined = 0;
754  for (auto& item : myClusters2Join) {
755  // verify loaded cluster
756  NodeSet cluster;
757  for (std::string nodeID : item.first) {
758  NBNode* node = retrieve(nodeID);
759  if (node == nullptr) {
760  WRITE_ERRORF(TL("unknown junction '%' while joining."), nodeID);
761  } else {
762  cluster.insert(node);
763  }
764  }
765  if (cluster.size() > 1) {
766  joinNodeCluster(cluster, dc, ec, tlc, item.second);
767  numJoined++;
768  myJoinExclusions.insert(item.second->getID());
769  }
770  }
771  myClusters2Join.clear(); // make save for recompute
772  return numJoined;
773 }
774 
775 
776 int
778 #ifdef DEBUG_JOINJUNCTIONS
779  std::cout << "joinJunctions...\n";
780 #endif
781  NodeClusters cands;
782  NodeClusters clusters;
783  std::map<const NBNode*, std::vector<NBNode*> > ptStopEnds;
784  // check for stop edges within the cluster
785  for (const auto& stopIt : sc.getStops()) {
786  NBEdge* edge = ec.retrieve(stopIt.second->getEdgeId());
787  if (edge != nullptr) {
788  ptStopEnds[edge->getFromNode()].push_back(edge->getToNode());
789  }
790  }
791  generateNodeClusters(maxDist, cands);
792  for (NodeSet& cluster : cands) {
793 #ifdef DEBUG_JOINJUNCTIONS
794  gDebugFlag1 = false;
795  for (NBNode* n : cluster) {
796  if (DEBUGCOND(n)) {
797  gDebugFlag1 = true;
798  }
799  }
800 #endif
801  // remove join exclusions
802  for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
803  NodeSet::iterator check = j;
804  ++j;
805  if (myJoinExclusions.count((*check)->getID()) > 0) {
806  cluster.erase(check);
807  }
808  }
809  std::string origCluster = joinNamedToString(cluster, ',');
810  // remove nodes that can be eliminated by geometry.remove
811  pruneClusterFringe(cluster, maxDist);
812  if (cluster.size() < 2) {
813  continue;
814  }
815  // remove nodes that are part of a bypass lane (typically for turning right without waiting at a traffic light)
816  pruneSlipLaneNodes(cluster, maxDist);
817  if (cluster.size() < 2) {
818  WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
819  continue;
820  }
821  std::string reason;
822  std::string origReason;
823  // pruneLongEdges might remove too much, so we check first to have a fallback with the circles
824  bool feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason);
825  if (feasible && ((int)cluster.size() - pruneLongEdges(cluster, maxDist, true) < 2)) {
826  origReason = "long edge";
827  feasible = false;
828  }
829  if (!feasible) {
830 #ifdef DEBUG_JOINJUNCTIONS
831  if (gDebugFlag1) {
832  std::cout << " try to reduce to 4-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
833  }
834 #endif
835  if (reduceToCircle(cluster, 4, cluster, maxDist)) {
836  feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason);
837  if (feasible) {
838  WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
839  }
840  }
841  }
842  if (!feasible) {
843 #ifdef DEBUG_JOINJUNCTIONS
844  if (gDebugFlag1) {
845  std::cout << " try to reduce to 2-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
846  }
847 #endif
848  origCluster = joinNamedToString(cluster, ',');
849  if (reduceToCircle(cluster, 2, cluster, maxDist)) {
850  feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason);
851  if (feasible) {
852  WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
853  }
854  }
855  }
856  // avoid removal of long edges (must have been added via an alternative path).
857  const int numPruned = pruneLongEdges(cluster, maxDist);
858  if (cluster.size() < 2) {
859  WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
860  continue;
861  }
862  // after pruning long edges we have to recheck
863  if (numPruned > 0) {
864  pruneClusterFringe(cluster, maxDist);
865  if (cluster.size() < 2) {
866  WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
867  continue;
868  }
869  pruneSlipLaneNodes(cluster, maxDist);
870  if (cluster.size() < 2) {
871  WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
872  continue;
873  }
874  }
875  origCluster = joinNamedToString(cluster, ',');
876  feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason);
877  if (!feasible) {
878  WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, origReason);
879  continue;
880  }
881  // compute all connected components of this cluster
882  // (may be more than 1 if intermediate nodes were removed)
883  NodeClusters components;
884  for (NBNode* current : cluster) {
885  // merge all connected components into newComp
886  NodeSet newComp;
887  //std::cout << "checking connectivity for " << current->getID() << "\n";
888  newComp.insert(current);
889  for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end();) {
890  NodeClusters::iterator check = it_comp;
891  //std::cout << " connected with " << toString(*check) << "?\n";
892  bool connected = false;
893  for (NBNode* k : *check) {
894  if (current->getConnectionTo(k) != nullptr || k->getConnectionTo(current) != nullptr) {
895  //std::cout << "joining with connected component " << toString(*check) << "\n";
896  newComp.insert((*check).begin(), (*check).end());
897  it_comp = components.erase(check);
898  connected = true;
899  break;
900  }
901  }
902  if (!connected) {
903  it_comp++;
904  }
905  }
906  //std::cout << "adding new component " << toString(newComp) << "\n";
907  components.push_back(newComp);
908  }
909  for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end(); ++it_comp) {
910  if ((*it_comp).size() > 1) {
911  //std::cout << "adding cluster " << toString(*it_comp) << "\n";
912  clusters.push_back(*it_comp);
913  }
914  }
915 #ifdef DEBUG_JOINJUNCTIONS
916  gDebugFlag1 = false;
917 #endif
918  }
919  joinNodeClusters(clusters, dc, ec, tlc);
920  return (int)clusters.size();
921 }
922 
923 
924 int
926 #ifdef DEBUG_JOINJUNCTIONS
927  std::cout << "joinSameJunctions...\n";
928 #endif
929  std::map<std::string, NodeSet> positions;
930  for (auto& item : myNodes) {
931  Position pos = item.second->getPosition();
932  std::string rounded = (OutputDevice::realString(pos.x(), gPrecision)
933  + "_" + OutputDevice::realString(pos.y(), gPrecision)
934  + "_" + OutputDevice::realString(pos.z(), gPrecision));
935  positions[rounded].insert(item.second);
936  }
937  NodeClusters clusters;
938  for (auto& item : positions) {
939  if (item.second.size() > 1) {
940  for (NBNode* n : item.second) {
941  if (myJoinExclusions.count(n->getID()) > 0) {
942  item.second.erase(n);
943  }
944  }
945  if (item.second.size() > 1) {
946  clusters.push_back(item.second);
947  }
948  }
949  }
950  joinNodeClusters(clusters, dc, ec, tlc, true);
951  return (int)clusters.size();
952 }
953 
954 void
955 NBNodeCont::pruneClusterFringe(NodeSet& cluster, double maxDist) const {
956 #ifdef DEBUG_JOINJUNCTIONS
957  if (gDebugFlag1) {
958  std::cout << "pruning cluster=" << joinNamedToString(cluster, ' ') << "\n";
959  }
960 #endif
961  // iteratively remove the fringe
962  bool pruneFringe = true;
963  bool pruneNoisyFringe = false;
964  // collect nodes that shall be joined due to distance but are not connected
965  // to the cluster for passenger traffic
966  while (pruneFringe) {
967  pruneFringe = false;
968  for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
969  NodeSet::iterator check = j;
970  NBNode* n = *check;
971  ++j;
972 
973  // compute clusterDist for node (length of shortest edge which connects this node to the cluster)
974  double clusterDist = std::numeric_limits<double>::max();
975  bool touchingCluster = false;
976  for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
977  NBNode* neighbor = (*it_edge)->getToNode();
978  if (cluster.count(neighbor) != 0) {
979  clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
980  touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
981  }
982  }
983  for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
984  NBNode* neighbor = (*it_edge)->getFromNode();
985  if (cluster.count(neighbor) != 0) {
986  clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
987  touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
988  }
989  }
990  // remove geometry-like nodes at fringe of the cluster
991  // (they have 1 neighbor in the cluster and at most 1 neighbor outside the cluster)
992  std::set<NBNode*> outsideNeighbors;
993  std::set<NBNode*> clusterNeighbors;
994  const double pedestrianFringeThreshold = 0.3;
995  for (NBEdge* e : n->getEdges()) {
996  NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
997  if (cluster.count(neighbor) == 0) {
998  if ((e->getPermissions() & SVC_PASSENGER) != 0
999  || isRailway(e->getPermissions()) // join railway crossings
1000  || (clusterDist <= pedestrianFringeThreshold
1001  && (!pruneNoisyFringe
1002  || isForVulnerableModes(e->getPermissions())
1003  // permit joining small opposite merges
1004  || getDiameter(cluster) < maxDist
1005  || cluster.size() == 2))
1006  || touchingCluster) {
1007  outsideNeighbors.insert(neighbor);
1008  }
1009  } else {
1010  clusterNeighbors.insert(neighbor);
1011  }
1012  }
1013 #ifdef DEBUG_JOINJUNCTIONS
1014  if (gDebugFlag1) std::cout << " check n=" << n->getID()
1015  << " clusterDist=" << clusterDist
1016  << " cd<th=" << (clusterDist <= pedestrianFringeThreshold)
1017  << " touching=" << touchingCluster
1018  << " out=" << joinNamedToString(outsideNeighbors, ',')
1019  << " in=" << joinNamedToString(clusterNeighbors, ',')
1020  << " dia=" << getDiameter(cluster)
1021  << "\n";
1022 #endif
1023  if (clusterNeighbors.size() == 0
1024  || (outsideNeighbors.size() <= 1
1025  && clusterNeighbors.size() == 1
1026  && !n->isTLControlled())) {
1027  cluster.erase(check);
1028  pruneFringe = true; // other nodes could belong to the fringe now
1029 #ifdef DEBUG_JOINJUNCTIONS
1030  if (gDebugFlag1) {
1031  std::cout << " pruned n=" << n->getID() << "\n";
1032  }
1033 #endif
1034  }
1035  }
1036  if (!pruneFringe && !pruneNoisyFringe) {
1037  // run once more and prune more things (with a look at cluster size)
1038  pruneFringe = true;
1039  pruneNoisyFringe = true;
1040 
1041  }
1042  }
1043 }
1044 
1045 double
1047  double result = 0;
1048  for (const NBNode* n1 : cluster) {
1049  for (const NBNode* n2 : cluster) {
1050  result = MAX2(result, n1->getPosition().distanceTo2D(n2->getPosition()));
1051  }
1052  }
1053  return result;
1054 }
1055 
1056 int
1057 NBNodeCont::pruneLongEdges(NodeSet& cluster, double maxDist, const bool dryRun) {
1058  std::set<NBNode*> toRemove;
1059  int maxPassengerLanes = 0;
1060  for (NBNode* n : cluster) {
1061  for (NBEdge* edge : n->getEdges()) {
1062  maxPassengerLanes = MAX2(maxPassengerLanes, edge->getNumLanesThatAllow(SVC_PASSENGER));
1063  }
1064  }
1065  for (NBNode* n : cluster) {
1066  for (NBEdge* edge : n->getOutgoingEdges()) {
1067  // we must track the edge length accross geometry like nodes
1068  // Also, intersecions that are geometry-like
1069  // from the perspective of passenger traffic should be tracked accross
1070  std::vector<NBNode*> passed;
1071  double length = 0;
1072  NBEdge* cur = edge;
1073  NBNode* to = edge->getToNode();
1074  while (cluster.count(to) != 0) {
1075  length += cur->getLoadedLength();
1076  bool goStraight = (std::find(passed.begin(), passed.end(), to) == passed.end()
1077  && (edge->getPermissions() & SVC_PASSENGER) != 0
1078  && to->geometryLike(
1081  passed.push_back(to);
1082  if (goStraight) {
1084  if (cur != nullptr) {
1085  to = cur->getToNode();
1086  } else {
1087  break;
1088  }
1089  } else {
1090  break;
1091  }
1092  }
1093  // allow higher threshold at larger junctions
1094  double longThreshold = maxDist + SUMO_const_laneWidth * MAX2(0, maxPassengerLanes - 1);
1095 #ifdef DEBUG_JOINJUNCTIONS
1096  if (gDebugFlag1) {
1097  std::cout << "check edge length " << edge->getID() << " (" << length << ", passed=" << passed.size() << ", max=" << longThreshold << ")\n";
1098  }
1099 #endif
1100  if (length > longThreshold) {
1101  // we found an edge that should not be removed. Maybe we can
1102  // still keep the start or end in the cluster
1103  // (keep the start if the end can be removed and vice versa)
1104  const bool keepStart = getClusterNeighbors(passed.back(), longThreshold, cluster).size() == 1;
1105  const bool keepEnd = !keepStart && getClusterNeighbors(n, longThreshold, cluster).size() == 1;
1106 #ifdef DEBUG_JOINJUNCTIONS
1107  if (gDebugFlag1) {
1108  std::cout << "node=" << n->getID() << " long edge " << edge->getID() << " (" << length << ", passed=" << toString(passed) << ", max=" << longThreshold << ") keepStart=" << keepStart << " keepEnd=" << keepEnd << "\n";
1109  }
1110 #endif
1111  if (!keepStart) {
1112  toRemove.insert(n);
1113  }
1114  toRemove.insert(passed.begin(), passed.end() - 1);
1115  if (!keepEnd) {
1116  toRemove.insert(passed.back());
1117  }
1118 
1119  }
1120  }
1121  }
1122  if (!dryRun) {
1123  for (std::set<NBNode*>::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
1124  cluster.erase(*j);
1125  }
1126  }
1127  return (int)toRemove.size();
1128 }
1129 
1130 
1131 NodeSet
1132 NBNodeCont::getClusterNeighbors(const NBNode* n, double longThreshold, NodeSet& cluster) {
1133  NodeSet result;
1134  for (NBEdge* e : n->getEdges()) {
1135  if (e->getLength() > longThreshold) {
1136  continue;
1137  }
1138  NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1139  if (cluster.count(neighbor) != 0) {
1140  result.insert(neighbor);
1141  }
1142  }
1143  return result;
1144 }
1145 
1146 
1147 void
1148 NBNodeCont::pruneSlipLaneNodes(NodeSet& cluster, double maxDist) const {
1149 #ifdef DEBUG_JOINJUNCTIONS
1150  if (gDebugFlag1) {
1151  std::cout << "pruning slip-lanes at cluster=" << joinNamedToString(cluster, ' ') << "\n";
1152  }
1153 #endif
1154  // fringe has already been removed
1155  if (cluster.size() <= 2) {
1156  return;
1157  }
1158  NodeSet toRemove;
1159  for (NBNode* n : cluster) {
1160  EdgeVector outgoing;
1161  double inAngle;
1162  // find slip lanes where the start is part of the cluster
1163  if (maybeSlipLaneStart(n, outgoing, inAngle)) {
1164  // potential slip lane start but we don't know which of the outgoing edges it is
1165 #ifdef DEBUG_JOINJUNCTIONS
1166  if (gDebugFlag1) {
1167  std::cout << " candidate slip-lane start=" << n->getID() << " outgoing=" << toString(outgoing) << "\n";
1168  }
1169 #endif
1170  for (NBEdge* contEdge : outgoing) {
1171  if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1172  continue;
1173  }
1174  double slipLength = contEdge->getLength();
1175  NBNode* cont = contEdge->getToNode();
1176  NodeSet cands;
1177  cands.insert(n);
1178  while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1179  if (cands.count(cont) != 0) {
1180  break; // circle, should not happen
1181  }
1182  cands.insert(cont);
1183 #ifdef DEBUG_JOINJUNCTIONS
1184  if (gDebugFlag1) {
1185  std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1186  }
1187 #endif
1188  NBEdge* next = cont->getOutgoingEdges().front();
1189  slipLength += next->getLength();
1190  cont = next->getToNode();
1191  }
1192 #ifdef DEBUG_JOINJUNCTIONS
1193  if (gDebugFlag1) {
1194  std::cout << " candidate slip-lane end=" << cont->getID() << " slipLength=" << slipLength << "\n";
1195  }
1196 #endif
1197  if (cont->getIncomingEdges().size() >= 2 && cont->getOutgoingEdges().size() == 1 &&
1198  // slip lanes are for turning so there needs to be a sufficient angle
1199  abs(NBHelpers::relAngle(inAngle, cont->getOutgoingEdges().front()->getAngleAtNode(cont))) > 45) {
1200  // check whether the other continuation at n is also connected to the sliplane end
1201  const NBEdge* const otherEdge = (contEdge == outgoing.front() ? outgoing.back() : outgoing.front());
1202  NodeSet visited;
1203  visited.insert(n);
1204  std::vector<NodeAndDist> toProc;
1205  toProc.push_back(std::make_pair(otherEdge->getToNode(), otherEdge->getLength()));
1206  bool found = false;
1207  while (!toProc.empty()) {
1208  NodeAndDist nodeAndDist = toProc.back();
1209  NBNode* cont2 = nodeAndDist.first;
1210  double dist = nodeAndDist.second;
1211 #ifdef DEBUG_JOINJUNCTIONS
1212  if (gDebugFlag1) {
1213  std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1214  }
1215 #endif
1216  toProc.pop_back();
1217  if (visited.find(cont2) != visited.end()) {
1218  continue;
1219  }
1220  visited.insert(cont2);
1221  if (cont2 == cont) {
1222  found = true;
1223  break;
1224  }
1225  for (NBEdge* e : cont2->getOutgoingEdges()) {
1226  const double dist2 = dist + e->getLength();
1227  if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1228  toProc.push_back(std::make_pair(e->getToNode(), dist2));
1229  }
1230  }
1231  }
1232  if (found) {
1233  // found slip lane
1234  cands.insert(cont);
1235  toRemove.insert(cands.begin(), cands.end());
1236 #ifdef DEBUG_JOINJUNCTIONS
1237  if (gDebugFlag1) {
1238  std::cout << " found slip-lane with nodes=" << joinNamedToString(cands, ' ') << "\n";
1239  }
1240 #endif
1241  }
1242  }
1243  }
1244  }
1245 
1246  EdgeVector incoming;
1247  double outAngle;
1248  // find slip lanes where the end is part of the cluster
1249  if (maybeSlipLaneEnd(n, incoming, outAngle)) {
1250  // potential slip lane end but we don't know which of the incoming edges it is
1251 #ifdef DEBUG_JOINJUNCTIONS
1252  if (gDebugFlag1) {
1253  std::cout << " candidate slip-lane end=" << n->getID() << " incoming=" << toString(incoming) << "\n";
1254  }
1255 #endif
1256  for (NBEdge* contEdge : incoming) {
1257  if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1258  continue;
1259  }
1260  double slipLength = contEdge->getLength();
1261  NBNode* cont = contEdge->getFromNode();
1262  NodeSet cands;
1263  cands.insert(n);
1264  while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1265  if (cands.count(cont) != 0) {
1266  break; // circle, should not happen
1267  }
1268  cands.insert(cont);
1269 #ifdef DEBUG_JOINJUNCTIONS
1270  if (gDebugFlag1) {
1271  std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1272  }
1273 #endif
1274  NBEdge* next = cont->getIncomingEdges().front();
1275  slipLength += next->getLength();
1276  cont = next->getFromNode();
1277  }
1278 #ifdef DEBUG_JOINJUNCTIONS
1279  if (gDebugFlag1) {
1280  std::cout << " candidate slip-lane start=" << cont->getID() << " slipLength=" << slipLength << "\n";
1281  }
1282 #endif
1283  if (cont->getOutgoingEdges().size() >= 2 && cont->getIncomingEdges().size() == 1 &&
1284  // slip lanes are for turning so there needs to be a sufficient angle
1285  abs(NBHelpers::relAngle(outAngle, cont->getIncomingEdges().front()->getAngleAtNode(cont))) > 45) {
1286  // check whether the other continuation at n is also connected to the sliplane end
1287  const NBEdge* const otherEdge = (contEdge == incoming.front() ? incoming.back() : incoming.front());
1288  NodeSet visited;
1289  visited.insert(n);
1290  std::vector<NodeAndDist> toProc;
1291  toProc.push_back(std::make_pair(otherEdge->getFromNode(), otherEdge->getLength()));
1292  bool found = false;
1293  while (!toProc.empty()) {
1294  NodeAndDist nodeAndDist = toProc.back();
1295  NBNode* cont2 = nodeAndDist.first;
1296  double dist = nodeAndDist.second;
1297 #ifdef DEBUG_JOINJUNCTIONS
1298  if (gDebugFlag1) {
1299  std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1300  }
1301 #endif
1302  toProc.pop_back();
1303  if (visited.find(cont2) != visited.end()) {
1304  continue;
1305  }
1306  visited.insert(cont2);
1307  if (cont2 == cont) {
1308  found = true;
1309  break;
1310  }
1311  for (NBEdge* e : cont2->getIncomingEdges()) {
1312  const double dist2 = dist + e->getLength();
1313  if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1314  toProc.push_back(std::make_pair(e->getFromNode(), dist2));
1315  }
1316  }
1317  }
1318  if (found) {
1319  // found slip lane
1320  cands.insert(cont);
1321  toRemove.insert(cands.begin(), cands.end());
1322 #ifdef DEBUG_JOINJUNCTIONS
1323  if (gDebugFlag1) {
1324  std::cout << " found slip-lane start with nodes=" << joinNamedToString(cands, ' ') << "\n";
1325  }
1326 #endif
1327  }
1328  }
1329  }
1330  }
1331 
1332 
1333 
1334  }
1335  int numRemoved = 0;
1336  for (NBNode* n : toRemove) {
1337  numRemoved += (int)cluster.erase(n);
1338  }
1339  if (numRemoved > 0) {
1340 #ifdef DEBUG_JOINJUNCTIONS
1341  if (gDebugFlag1) {
1342  std::cout << " removed " << numRemoved << " nodes from cluster: " << joinNamedToString(toRemove, ' ') << "\n";
1343  }
1344 #endif
1345  pruneClusterFringe(cluster, maxDist);
1346  }
1347 }
1348 
1349 
1350 bool
1352  return cont->getPassengerEdges(true).size() == 1 && cont->getPassengerEdges(false).size() == 1;
1353 }
1354 
1355 
1356 bool
1357 NBNodeCont::maybeSlipLaneStart(const NBNode* n, EdgeVector& outgoing, double& inAngle) const {
1358  EdgeVector inPE = n->getPassengerEdges(true);
1359  EdgeVector outPE = n->getPassengerEdges(false);
1360  if (inPE.size() == 1 && outPE.size() == 2) {
1361  outgoing.insert(outgoing.begin(), outPE.begin(), outPE.end());
1362  inAngle = inPE.front()->getAngleAtNode(n);
1363  return true;
1364  } else if (inPE.size() >= 2 && outPE.size() == 3) {
1365  // check if the incoming edges are going in opposite directions and then
1366  // use the incoming edge that has 2 almost-straight outgoing edges
1367  const double inRelAngle = fabs(NBHelpers::relAngle(inPE.front()->getAngleAtNode(n), inPE.back()->getAngleAtNode(n)));
1368  //std::cout << "n=" << n->getID() << " inRelAngle=" << inRelAngle << "\n";
1369  if (inRelAngle < 135) {
1370  return false; // not opposite incoming
1371  }
1372  for (NBEdge* in : inPE) {
1373  EdgeVector straight;
1374  int numReverse = 0;
1375  for (NBEdge* out : outPE) {
1376  const double outRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1377  if (outRelAngle <= 45) {
1378  straight.push_back(out);
1379  } else if (outRelAngle >= 135) {
1380  numReverse++;
1381  }
1382  }
1383  if (straight.size() == 2 && numReverse == 1) {
1384  outgoing.insert(outgoing.begin(), straight.begin(), straight.end());
1385  inAngle = in->getAngleAtNode(n);
1386  return true;
1387  }
1388  }
1389  }
1390  return false;
1391 }
1392 
1393 
1394 bool
1395 NBNodeCont::maybeSlipLaneEnd(const NBNode* n, EdgeVector& incoming, double& outAngle) const {
1396  EdgeVector inPE = n->getPassengerEdges(true);
1397  EdgeVector outPE = n->getPassengerEdges(false);
1398  if (inPE.size() == 2 && outPE.size() == 1) {
1399  incoming.insert(incoming.begin(), inPE.begin(), inPE.end());
1400  outAngle = outPE.front()->getAngleAtNode(n);
1401  return true;
1402  } else if (inPE.size() == 3 && outPE.size() >= 2) {
1403  // check if the outgoing edges are going in opposite directions and then
1404  // use the outgoing edge that has 2 almost-straight incoming edges
1405  const double outRelAngle = fabs(NBHelpers::relAngle(outPE.front()->getAngleAtNode(n), outPE.back()->getAngleAtNode(n)));
1406  //std::cout << "n=" << n->getID() << " outRelAngle=" << outRelAngle << "\n";
1407  if (outRelAngle < 135) {
1408  return false; // not opposite outgoing
1409  }
1410  for (NBEdge* out : outPE) {
1411  EdgeVector straight;
1412  int numReverse = 0;
1413  for (NBEdge* in : inPE) {
1414  const double inRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1415  if (inRelAngle <= 45) {
1416  straight.push_back(in);
1417  } else if (inRelAngle >= 135) {
1418  numReverse++;
1419  }
1420  }
1421  if (straight.size() == 2 && numReverse == 1) {
1422  incoming.insert(incoming.begin(), straight.begin(), straight.end());
1423  outAngle = out->getAngleAtNode(n);
1424  return true;
1425  }
1426  }
1427  }
1428  return false;
1429 }
1430 
1431 bool
1432 NBNodeCont::feasibleCluster(const NodeSet& cluster, const std::map<const NBNode*, std::vector<NBNode*> >& ptStopEnds,
1433  double maxDist, std::string& reason) const {
1434  // check for clusters which are to complex and probably won't work very well
1435  // we count the incoming edges of the final junction
1436  std::map<NBEdge*, double, ComparatorIdLess> finalIncomingAngles;
1437  std::map<NBEdge*, double, ComparatorIdLess> finalOutgoingAngles;
1438  for (NBNode* n : cluster) {
1439  for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
1440  NBEdge* edge = *it_edge;
1441  if (cluster.count(edge->getFromNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1442  // incoming edge, does not originate in the cluster
1443  finalIncomingAngles[edge] = edge->getAngleAtNode(edge->getToNode());
1444  }
1445  }
1446  for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
1447  NBEdge* edge = *it_edge;
1448  if (cluster.count(edge->getToNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1449  // outgoing edge, does not end in the cluster
1450  finalOutgoingAngles[edge] = edge->getAngleAtNode(edge->getFromNode());
1451  }
1452  }
1453 
1454  }
1455 #ifdef DEBUG_JOINJUNCTIONS
1456  for (NBNode* n : cluster) {
1457  if (DEBUGCOND(n)) {
1458  std::cout << "feasibleCluster c=" << joinNamedToString(cluster, ',')
1459  << "\n inAngles=" << joinNamedToString(finalIncomingAngles, ' ', ':')
1460  << "\n outAngles=" << joinNamedToString(finalOutgoingAngles, ' ', ':')
1461  << "\n";
1462  }
1463  }
1464 #endif
1465  if (finalIncomingAngles.size() > 5) {
1466  reason = toString(finalIncomingAngles.size()) + " incoming edges";
1467  return false;
1468  }
1469  // check for incoming parallel edges
1470  const double PARALLEL_THRESHOLD_SAME_NODE = 10;
1471  const double PARALLEL_THRESHOLD_DIFF_NODE = 30;
1472  bool foundParallel = false;
1473  for (auto j = finalIncomingAngles.begin(); j != finalIncomingAngles.end() && !foundParallel; ++j) {
1474  auto k = j;
1475  for (++k; k != finalIncomingAngles.end() && !foundParallel; ++k) {
1476  const double angleDiff = fabs(j->second - k->second);
1477  if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1478  // for edge targeting the same node, permit a narrower angle
1479  const double edgeDist = j->first->getLaneShape(0).back().distanceTo2D(k->first->getLaneShape(0).back());
1480 #ifdef DEBUG_JOINJUNCTIONS
1481  if (DEBUGCOND(j->first->getToNode())) {
1482  std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1483  }
1484 #endif
1485  if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1486  (j->first->getToNode() == k->first->getToNode()
1487  || (edgeDist < maxDist)))) {
1488  continue;
1489  }
1490  reason = "parallel incoming " + j->first->getID() + "," + k->first->getID();
1491  return false;
1492  }
1493  }
1494  }
1495  // check for outgoing parallel edges
1496  for (auto j = finalOutgoingAngles.begin(); j != finalOutgoingAngles.end() && !foundParallel; ++j) {
1497  auto k = j;
1498  for (++k; k != finalOutgoingAngles.end() && !foundParallel; ++k) {
1499  const double angleDiff = fabs(j->second - k->second);
1500  if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1501  // for edge leaving the same node, permit a narrower angle
1502  const double edgeDist = j->first->getLaneShape(0).front().distanceTo2D(k->first->getLaneShape(0).front());
1503 #ifdef DEBUG_JOINJUNCTIONS
1504  if (DEBUGCOND(j->first->getFromNode())) {
1505  std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1506  }
1507 #endif
1508  if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1509  (j->first->getFromNode() == k->first->getFromNode()
1510  || (edgeDist < maxDist)))) {
1511  continue;
1512  }
1513  reason = "parallel outgoing " + j->first->getID() + "," + k->first->getID();
1514  return false;
1515  }
1516  }
1517  }
1518  // check for stop edges and tls within the cluster
1519  bool hasTLS = false;
1520  for (NBNode* n : cluster) {
1521  if (n->isTLControlled()) {
1522  hasTLS = true;
1523  }
1524  const auto& stopEnds = ptStopEnds.find(n);
1525  if (stopEnds != ptStopEnds.end()) {
1526  for (NBNode* const to : stopEnds->second) {
1527  if (cluster.count(to) != 0) {
1528  reason = "it contains a pt stop edge";
1529  return false;
1530  }
1531  }
1532  }
1533  }
1534  // prevent removal of long edges unless there is weak circle or a traffic light
1535  if (cluster.size() > 2) {
1536  // find the nodes with the biggests physical distance between them
1537  double maxLength = -1;
1538  NBEdge* maxEdge = nullptr;
1539  for (NBNode* n1 : cluster) {
1540  for (NBNode* n2 : cluster) {
1541  NBEdge* e1 = n1->getConnectionTo(n2);
1542  NBEdge* e2 = n2->getConnectionTo(n1);
1543  if (e1 != nullptr && e1->getLoadedLength() > maxLength) {
1544  maxLength = e1->getLoadedLength();
1545  maxEdge = e1;
1546  }
1547  if (e2 != nullptr && e2->getLoadedLength() > maxLength) {
1548  maxLength = e2->getLoadedLength();
1549  maxEdge = e2;
1550  }
1551  }
1552  }
1553 #ifdef DEBUG_JOINJUNCTIONS
1554  for (NBNode* n : cluster) {
1555  if (DEBUGCOND(n)) {
1556  std::cout << "feasible hasTLS=" << hasTLS << " maxLength=" << maxLength << " maxEdge=" << maxEdge->getID() << "\n";
1557  }
1558  }
1559 #endif
1560  if (!hasTLS && maxLength > 5) {
1561  // find a weak circle within cluster that does not use maxEdge
1562  std::vector<NBNode*> toCheck;
1563  std::set<NBNode*> visited;
1564  toCheck.push_back(maxEdge->getToNode());
1565  bool foundCircle = false;
1566  while (!toCheck.empty()) {
1567  NBNode* n = toCheck.back();
1568  if (n == maxEdge->getFromNode()) {
1569  foundCircle = true;
1570  break;
1571  }
1572  toCheck.pop_back();
1573  visited.insert(n);
1574  for (NBEdge* e : n->getEdges()) {
1575  if (e != maxEdge) {
1576  NBNode* cand = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1577  if (visited.count(cand) == 0 && cluster.count(cand) != 0) {
1578  toCheck.push_back(cand);
1579  }
1580  }
1581  }
1582  }
1583  if (!foundCircle) {
1584  reason = "not compact (maxEdge=" + maxEdge->getID() + " length=" + toString(maxLength) + ")";
1585  return false;
1586  }
1587  }
1588  }
1589  // prevent joining of simple merging/spreading structures
1590  if (!hasTLS && cluster.size() >= 2) {
1591  int entryNodes = 0;
1592  int exitNodes = 0;
1593  int outsideIncoming = 0;
1594  int outsideOutgoing = 0;
1595  int edgesWithin = 0;
1596  for (NBNode* n : cluster) {
1597  bool foundOutsideIncoming = false;
1598  for (NBEdge* e : n->getIncomingEdges()) {
1599  if (cluster.count(e->getFromNode()) == 0) {
1600  // edge entering from outside the cluster
1601  outsideIncoming++;
1602  foundOutsideIncoming = true;
1603  } else {
1604  edgesWithin++;
1605  }
1606  }
1607  if (foundOutsideIncoming) {
1608  entryNodes++;
1609  }
1610  bool foundOutsideOutgoing = false;
1611  for (NBEdge* e : n->getOutgoingEdges()) {
1612  if (cluster.count(e->getToNode()) == 0) {
1613  // edge leaving cluster
1614  outsideOutgoing++;
1615  foundOutsideOutgoing = true;
1616  }
1617  }
1618  if (foundOutsideOutgoing) {
1619  exitNodes++;
1620  }
1621  }
1622  if (entryNodes < 2) {
1623  reason = "only 1 entry node";
1624  return false;
1625  }
1626  if (exitNodes < 2) {
1627  reason = "only 1 exit node";
1628  return false;
1629  }
1630  if (cluster.size() == 2) {
1631  if (edgesWithin == 1 && outsideIncoming < 3 && outsideOutgoing < 3) {
1632  reason = "only 1 edge within and no cross-traffic";
1633  return false;
1634  }
1635  }
1636  }
1637  return true;
1638 }
1639 
1640 
1641 bool
1642 NBNodeCont::reduceToCircle(NodeSet& cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector<NBNode*> cands) const {
1643  //std::cout << "reduceToCircle cs=" << circleSize << " cands=" << toString(cands, ',') << " startNodes=" << joinNamedToString(startNodes, ',') << "\n";
1644  assert(circleSize >= 2);
1645  if ((int)cands.size() == circleSize) {
1646  if (cands.back()->getConnectionTo(cands.front()) != nullptr) {
1647  // cluster found
1648  NodeSet candCluster;
1649  candCluster.insert(cands.begin(), cands.end());
1650  pruneClusterFringe(candCluster, maxDist);
1651  const bool feasible = (int)candCluster.size() == circleSize;
1652  if (feasible) {
1653  cluster.clear();
1654  cluster.insert(cands.begin(), cands.end());
1655  }
1656  return feasible;
1657  } else {
1658  return false;
1659  }
1660  }
1661  if ((int)cluster.size() <= circleSize || startNodes.size() == 0) {
1662  // no reduction possible
1663  //std::cout << " abort\n";
1664  return false;
1665  }
1666  if (cands.size() == 0) {
1667  // try to find a circle starting from another start node
1668  NBEdge* e = shortestEdge(cluster, startNodes, cands);
1669  if (e != nullptr) {
1670  cands.push_back(e->getFromNode());
1671  startNodes.erase(e->getFromNode());
1672  if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands)) {
1673  return true;
1674  } else {
1675  // try another start node
1676  return reduceToCircle(cluster, circleSize, startNodes, maxDist);
1677  }
1678  }
1679  } else {
1680  NodeSet singleStart;
1681  singleStart.insert(cands.back());
1682  NBEdge* e = shortestEdge(cluster, singleStart, cands);
1683  if (e != nullptr) {
1684  std::vector<NBNode*> cands2(cands);
1685  cands2.push_back(e->getToNode());
1686  if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands2)) {
1687  return true;
1688  }
1689  }
1690  }
1691  //std::cout << " abort2\n";
1692  return false;
1693 }
1694 
1695 
1696 NBEdge*
1697 NBNodeCont::shortestEdge(const NodeSet& cluster, const NodeSet& startNodes, const std::vector<NBNode*>& exclude) const {
1698  double minDist = std::numeric_limits<double>::max();
1699  NBEdge* result = nullptr;
1700  for (NBNode* n : startNodes) {
1701  for (NBEdge* e : n->getOutgoingEdges()) {
1702  NBNode* neigh = e->getToNode();
1703  if (cluster.count(neigh) != 0 && std::find(exclude.begin(), exclude.end(), neigh) == exclude.end()) {
1704  const double dist = n->getPosition().distanceTo2D(neigh->getPosition());
1705  //std::cout << " e=" << e->getID() << " dist=" << dist << " minD=" << minDist << "\n";
1706  if (dist < minDist) {
1707  minDist = dist;
1708  result = e;
1709  }
1710  }
1711  }
1712  }
1713  //std::cout << "closestNeighbor startNodes=" << toString(startNodes) << " result=" << Named::getIDSecure(result) << "\n";
1714  return result;
1715 }
1716 
1717 void
1719  NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, bool resetConnections) {
1720  for (NodeSet cluster : clusters) {
1721  joinNodeCluster(cluster, dc, ec, tlc, nullptr, resetConnections);
1722  }
1723 }
1724 
1725 
1726 void
1727 NBNodeCont::joinNodeCluster(NodeSet cluster, NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, NBNode* predefined, bool resetConnections) {
1728  const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1729  assert(cluster.size() > 1);
1730  std::string id = "cluster_";
1731  Position pos;
1732  bool setTL = false;
1735  // collect edges
1736  std::set<NBEdge*, ComparatorIdLess> allEdges;
1737  for (NBNode* n : cluster) {
1738  const EdgeVector& edges = n->getEdges();
1739  allEdges.insert(edges.begin(), edges.end());
1740  }
1741  // determine edges with are incoming or fully inside
1742  std::set<NBEdge*, ComparatorIdLess> clusterIncoming;
1743  std::set<NBEdge*, ComparatorIdLess> inside;
1744  for (NBEdge* e : allEdges) {
1745  if (cluster.count(e->getToNode()) > 0) {
1746  if (cluster.count(e->getFromNode()) > 0) {
1747  inside.insert(e);
1748  if (e->getSignalPosition() != Position::INVALID) {
1749  setTL = true;
1750  nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
1751  }
1752  } else {
1753  clusterIncoming.insert(e);
1754  }
1755  }
1756  }
1757 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1758  std::cout << "joining cluster " << joinNamedToString(cluster, ' ')
1759  << " resetConnections=" << resetConnections << "\n"
1760  << " incoming=" << joinNamedToString(clusterIncoming, ' ') << "\n"
1761  << " inside=" << joinNamedToString(inside, ' ') << "\n";
1762 #endif
1763  analyzeCluster(cluster, id, pos, setTL, type, nodeType);
1764  NBNode* newNode = nullptr;
1765  if (predefined != nullptr) {
1766  newNode = predefined;
1767  } else {
1768  if (!insert(id, pos)) {
1769  // should not fail
1770  WRITE_WARNINGF(TL("Could not join junctions %."), id);
1771  return;
1772  }
1773  newNode = retrieve(id);
1774  }
1775  std::string tlID = id;
1776  if (predefined != nullptr) {
1777  if (predefined->getType() != SumoXMLNodeType::UNKNOWN) {
1778  nodeType = predefined->getType();
1779  }
1780  Position ppos = predefined->getPosition();
1781  if (ppos.x() != Position::INVALID.x()) {
1782  pos.setx(ppos.x());
1783  }
1784  if (ppos.y() != Position::INVALID.y()) {
1785  pos.sety(ppos.y());
1786  }
1787  if (ppos.z() != Position::INVALID.z()) {
1788  pos.setz(ppos.z());
1789  }
1790  }
1791  newNode->reinit(pos, nodeType);
1792  if (origNames) {
1793  newNode->setParameter(SUMO_PARAM_ORIGID, joinNamedToString(cluster, ' '));
1794  }
1795  if (setTL && !newNode->isTLControlled()) {
1796  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(tlID, newNode, 0, type);
1797  if (!tlc.insert(tlDef)) {
1798  // actually, nothing should fail here
1799  delete tlDef;
1800  throw ProcessError(TLF("Could not allocate tls '%'.", id));
1801  }
1802  }
1803 
1804  // determine possible connectivity from outside edges
1805  std::map<NBEdge*, EdgeSet> reachable;
1806  std::map<std::pair<NBEdge*, NBEdge*>, SVCPermissions> conPermissions;
1807  EdgeSet specialPermissions;
1808  for (NBEdge* const e : clusterIncoming) {
1809  EdgeVector open;
1810  EdgeSet seen;
1811  open.push_back(e);
1812  while (open.size() > 0) {
1813  NBEdge* const cur = open.back();
1814  const SVCPermissions pCur = conPermissions.count({e, cur}) == 0 ? cur->getPermissions() : conPermissions[ {e, cur}];
1815 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1816  std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1817  std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1818 #endif
1819  seen.insert(cur);
1820  open.pop_back();
1821  if (cluster.count(cur->getToNode()) == 0) {
1822  //std::cout << " continue\n";
1823  continue;
1824  }
1825  const auto& cons = cur->getConnections();
1826  if (cons.size() == 0 || ec.hasPostProcessConnection(cur->getID()) || cur->getStep() == NBEdge::EdgeBuildingStep::INIT) {
1827  // check permissions to determine reachability
1828  for (NBEdge* out : cur->getToNode()->getOutgoingEdges()) {
1829  if (allEdges.count(out) != 0) {
1830  const SVCPermissions p = pCur & out->getPermissions();
1831  if (seen.count(out) == 0 || (~conPermissions[ {e, out}] & p) != 0) {
1832  if ((p & ~SVC_PEDESTRIAN) != 0) {
1833  open.push_back(out);
1834  conPermissions[ {e, out}] |= p;
1835 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1836  std::cout << " e=" << e->getID() << " out=" << out->getID() << " pOut=" << getVehicleClassNames(out->getPermissions()) << "\n p=" << getVehicleClassNames(p) << "\n q=" << getVehicleClassNames(conPermissions[ {e, out}]) << "\n";
1837 #endif
1838  }
1839  }
1840  }
1841  }
1842  } else {
1843  // check existing connections
1844  for (const auto& con : cons) {
1845  if (con.toEdge != nullptr && allEdges.count(con.toEdge) != 0) {
1846  SVCPermissions p = pCur & con.toEdge->getPermissions();
1847  if (con.permissions != SVC_UNSPECIFIED) {
1848  p &= con.permissions;
1849  }
1850  if (seen.count(con.toEdge) == 0 || (~conPermissions[ {e, con.toEdge}] & p) != 0) {
1851  open.push_back(con.toEdge);
1852  conPermissions[ {e, con.toEdge}] |= p;
1853  //std::cout << " e=" << e->getID() << " con.toEdge=" << con.toEdge->getID() << " pSpecial=" << toString(con.permissions) << " pOut=" << getVehicleClassNames(con.toEdge->getPermissions()) << "\n p=" << getVehicleClassNames(p) << "\n q=" << getVehicleClassNames(conPermissions[{e, con.toEdge}]) << "\n";
1854  }
1855  }
1856  }
1857  }
1858  }
1859  seen.erase(e);
1860  for (NBEdge* reached : seen) {
1861  // filter out inside edges from reached
1862  if (inside.count(reached) == 0) {
1863  if (e->getStep() > NBEdge::EdgeBuildingStep::INIT && reached->getFromNode() == e->getToNode() && !e->isConnectedTo(reached)) {
1864  // also filter out edges that are outgoing of the to-node of edge but aren't currently connected
1865  continue;
1866  }
1867  reachable[e].insert(reached);
1868  const SVCPermissions pDefault = e->getPermissions() & reached->getPermissions();
1869  if (conPermissions[ {e, reached}] != pDefault) {
1870  specialPermissions.insert(e);
1871 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1872  std::cout << "e=" << e->getID() << " out=" << reached->getID() << " special=" << getVehicleClassNames(conPermissions[ {e, reached}]) << "\n";
1873 #endif
1874  }
1875  }
1876  }
1877 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1878  std::cout << " reachable e=" << e->getID() << " seen=" << toString(seen) << " reachable=" << toString(reachable[e]) << "\n";
1879 #endif
1880  }
1881 
1882  // remap and remove edges which are completely within the new intersection
1883  if (origNames) {
1884  newNode->setParameter("origEdgeIds", joinNamedToString(inside, ' '));
1885  }
1886  for (NBEdge* e : inside) {
1887  for (NBEdge* e2 : allEdges) {
1888  if (e != e2) {
1889  e2->replaceInConnections(e, e->getConnections());
1890  }
1891  }
1892  ec.extract(dc, e, true);
1893  allEdges.erase(e);
1894  }
1895 
1896  // remap edges which are incoming / outgoing
1897  for (NBEdge* e : allEdges) {
1898  const bool outgoing = cluster.count(e->getFromNode()) > 0;
1899  NBNode* from = outgoing ? newNode : e->getFromNode();
1900  NBNode* to = outgoing ? e->getToNode() : newNode;
1901  if (origNames) {
1902  if (outgoing) {
1903  e->setParameter("origFrom", e->getFromNode()->getID());
1904  } else {
1905  e->setParameter("origTo", e->getToNode()->getID());
1906  }
1907  }
1908  if (e->getTurnSignTarget() != "") {
1909  for (NBNode* n : cluster) {
1910  if (e->getTurnSignTarget() == n->getID()) {
1911  e->setTurnSignTarget(to->getID());
1912  break;
1913  }
1914  }
1915  }
1916  e->reinitNodes(from, to);
1917  // re-add connections which previously existed and may still valid.
1918  // connections to removed edges will be ignored
1919  std::vector<NBEdge::Connection> conns = e->getConnections();
1920  for (std::vector<NBEdge::Connection>::iterator k = conns.begin(); k != conns.end(); ++k) {
1921  if ((*k).toEdge == nullptr) {
1922  // edge explicitly set to have no connections
1923  continue;
1924  }
1925  e->addLane2LaneConnection((*k).fromLane, (*k).toEdge, (*k).toLane, NBEdge::Lane2LaneInfoType::USER, false, (*k).mayDefinitelyPass);
1926  if ((*k).fromLane >= 0 && (*k).fromLane < e->getNumLanes() && e->getLaneStruct((*k).fromLane).connectionsDone) {
1927  // @note (see NIImporter_DlrNavteq::ConnectedLanesHandler)
1928  e->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::INIT);
1929 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1930  std::cout << " e=" << e->getID() << " declareConnectionsAsLoaded\n";
1931 #endif
1932  }
1933  }
1934  }
1935  if (!resetConnections) {
1936  // disable connections that were impossible with the old topology
1937  // if connectivity has special permissions, set edge to edge connections explicitly
1938  for (NBEdge* in : newNode->getIncomingEdges()) {
1939  for (NBEdge* out : newNode->getOutgoingEdges()) {
1940  if (reachable[in].count(out) == 0) {
1941  if (!ec.hasPostProcessConnection(in->getID(), out->getID())) {
1942  //std::cout << " removeUnreachable in=" << in->getID() << " out=" << out->getID() << "\n";
1943  in->removeFromConnections(out, -1, -1, true, false, true);
1944  } else {
1945  //std::cout << " hasPostProcessConnection in=" << in->getID() << " out=" << out->getID() << "\n";
1946  }
1947  } else if (specialPermissions.count(in) != 0) {
1948  SVCPermissions pDefault = in->getPermissions() & out->getPermissions();
1949  SVCPermissions p = conPermissions[ {in, out}] == 0 ? pDefault : conPermissions[ {in, out}];
1950  in->addEdge2EdgeConnection(out, true, p == pDefault ? SVC_UNSPECIFIED : p);
1951  //std::cout << " addEdge2Edge in=" << in->getID() << " out=" << out->getID() << "\n";
1952  }
1953  }
1954  }
1955  } else {
1956  for (NBEdge* in : newNode->getIncomingEdges()) {
1957  in->invalidateConnections(true);
1958  }
1959  }
1960 
1961  // remove original nodes
1962  registerJoinedCluster(cluster);
1963  for (NBNode* n : cluster) {
1964  erase(n);
1965  }
1966 }
1967 
1968 
1969 void
1971  std::set<std::string> ids;
1972  for (NBNode* n : cluster) {
1973  ids.insert(n->getID());
1974  }
1975  myJoinedClusters.push_back(ids);
1976 }
1977 
1978 void
1979 NBNodeCont::registerJoinedCluster(const std::set<std::string>& cluster) {
1980  myJoinedClusters.push_back(cluster);
1981 }
1982 
1983 void
1984 NBNodeCont::unregisterJoinedCluster(const std::set<std::string>& cluster) {
1985  auto it = std::find(myJoinedClusters.begin(), myJoinedClusters.end(), cluster);
1986  if (it != myJoinedClusters.end()) {
1987  myJoinedClusters.erase(it);
1988  }
1989 }
1990 
1991 
1992 void
1993 NBNodeCont::analyzeCluster(NodeSet cluster, std::string& id, Position& pos,
1994  bool& hasTLS, TrafficLightType& type, SumoXMLNodeType& nodeType) {
1995  id = createClusterId(cluster, id);
1996  bool ambiguousType = false;
1997  for (NBNode* j : cluster) {
1998  pos.add(j->getPosition());
1999  // add a traffic light if any of the cluster members was controlled
2000  if (j->isTLControlled()) {
2001  if (!hasTLS) {
2002  // init type
2003  type = (*j->getControllingTLS().begin())->getType();
2004  } else if (type != (*j->getControllingTLS().begin())->getType()) {
2005  ambiguousType = true;
2006  }
2007  hasTLS = true;
2008  }
2009  SumoXMLNodeType otherType = j->getType();
2010  if (nodeType == SumoXMLNodeType::UNKNOWN) {
2011  nodeType = otherType;
2012  } else if (nodeType != otherType) {
2013  if (hasTLS) {
2014  nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
2015  } else if (otherType != SumoXMLNodeType::UNKNOWN) {
2016  if ((nodeType != SumoXMLNodeType::PRIORITY && (nodeType != SumoXMLNodeType::NOJUNCTION || otherType != SumoXMLNodeType::PRIORITY))
2017  || (otherType != SumoXMLNodeType::NOJUNCTION && otherType != SumoXMLNodeType::UNKNOWN && otherType != SumoXMLNodeType::PRIORITY)) {
2018  WRITE_WARNINGF("Ambiguous node type for node cluster '%' (%,%), setting to '" + toString(SumoXMLNodeType::PRIORITY) + "'.", id, toString(nodeType), toString(otherType));
2019  }
2020  nodeType = SumoXMLNodeType::PRIORITY;
2021  }
2022  }
2023  }
2024  pos.mul(1. / (double)cluster.size());
2025  if (ambiguousType) {
2026  type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
2027  WRITE_WARNINGF(TL("Ambiguous traffic light type for node cluster '%', setting to '%'."), id, toString(type));
2028  }
2029 }
2030 
2031 
2032 // ----------- (Helper) methods for guessing/computing traffic lights
2033 bool
2034 NBNodeCont::shouldBeTLSControlled(const NodeSet& c, double laneSpeedThreshold, bool recheck) const {
2035  bool tooFast = false;
2036  double laneSpeedSum = 0;
2037  std::set<NBEdge*> seen;
2038  for (NBNode* j : c) {
2039  for (const NBEdge* e : j->getEdges()) {
2040  if (c.find(e->getFromNode()) != c.end() && c.find(e->getToNode()) != c.end()) {
2041  // edges fully within the cluster do not count
2042  continue;
2043  }
2044  if (j->hasIncoming(e)) {
2045  if (recheck && !j->hasConflict(e)) {
2046  // edges without conflict do not count
2047  // we can only check this after connections have been computed
2048  continue;
2049  }
2050  laneSpeedSum += (double)e->getNumLanes() * e->getLaneSpeed(0);
2051  }
2052  if (e->getLaneSpeed(0) * 3.6 > 79) {
2053  tooFast = true;
2054  }
2055  }
2056  }
2057  //std::cout << " c=" << joinNamedToString(c, ' ') << " f=" << f << " size=" << c.size() << " thresh=" << laneSpeedThreshold << " tooFast=" << tooFast << "\n";
2058  return !tooFast && laneSpeedSum >= laneSpeedThreshold && c.size() != 0;
2059 }
2060 
2061 bool
2063  // check whether all component nodes are solely pedestrian crossings
2064  // (these work fine without joining)
2065  for (NBNode* node : c) {
2066  EdgeVector nonPedIncoming;
2067  EdgeVector nonPedOutgoing;
2068  for (NBEdge* e : node->getIncomingEdges()) {
2069  if (e->getPermissions() != SVC_PEDESTRIAN) {
2070  nonPedIncoming.push_back(e);
2071  }
2072  }
2073  for (NBEdge* e : node->getOutgoingEdges()) {
2074  if (e->getPermissions() != SVC_PEDESTRIAN) {
2075  nonPedOutgoing.push_back(e);
2076  }
2077  }
2078  if (!node->geometryLike(nonPedIncoming, nonPedOutgoing)) {
2079  //for (NBNode* node : c) {
2080  // if (node->getID() == "2480337678") {
2081  // std::cout << " node=" << node->getID() << " nonPedIncoming=" << toString(nonPedIncoming) << " nonPedOutgoing=" << toString(nonPedOutgoing) << "\n";
2082  // }
2083  //}
2084  return false;
2085  }
2086  }
2087  return true;
2088 }
2089 
2090 
2091 bool
2093  for (NBNode* node : c) {
2094  if (node->isTLControlled()) {
2095  NBTrafficLightDefinition* tl = (*node->getControllingTLS().begin());
2096  if (tl->getNodes().size() > 1) {
2097  // joined tls also imply a customID
2098  return true;
2099  }
2100  const std::string tlID = tl->getID();
2101  if (tlID != node->getID()
2102  && !StringUtils::startsWith(tlID, "joinedS_")
2103  && !StringUtils::startsWith(tlID, "joinedG_")
2104  && !StringUtils::startsWith(tlID, "GS")) {
2105  return true;
2106  }
2107  }
2108  }
2109  return false;
2110 }
2111 
2112 
2113 void
2115  myGuessedTLS.clear();
2116  // build list of definitely not tls-controlled junctions
2117  const double laneSpeedThreshold = oc.getFloat("tls.guess.threshold");
2118  if (oc.isSet("tls.unset")) {
2119  std::vector<std::string> notTLControlledNodes = oc.getStringVector("tls.unset");
2120  for (std::vector<std::string>::const_iterator i = notTLControlledNodes.begin(); i != notTLControlledNodes.end(); ++i) {
2121  NBNode* n = NBNodeCont::retrieve(*i);
2122  if (n == nullptr) {
2123  throw ProcessError(TLF(" The junction '%' to set as not-controlled is not known.", *i));
2124  }
2125  std::set<NBTrafficLightDefinition*> tls = n->getControllingTLS();
2126  for (std::set<NBTrafficLightDefinition*>::const_iterator j = tls.begin(); j != tls.end(); ++j) {
2127  (*j)->removeNode(n);
2128  }
2129  n->removeTrafficLights();
2130  myUnsetTLS.insert(n);
2131  }
2132  }
2133 
2135  // loop#1 checking whether the node shall be tls controlled,
2136  // because it is assigned to a district
2137  if (oc.exists("tls.taz-nodes") && oc.getBool("tls.taz-nodes")) {
2138  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2139  NBNode* cur = (*i).second;
2140  if (cur->isNearDistrict() && myUnsetTLS.count(cur) == 0) {
2141  setAsTLControlled(cur, tlc, type);
2142  }
2143  }
2144  }
2145 
2146  // figure out which nodes mark the locations of TLS signals
2147  // This assumes nodes are already joined
2148  if (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals")) {
2149  // prepare candidate edges
2150  const double signalDist = oc.getFloat("tls.guess-signals.dist");
2151  for (const auto& item : myNodes) {
2152  const NBNode* node = item.second;
2153  if (node->isTLControlled() && (node->getIncomingEdges().size() == 1 || node->geometryLike())) {
2154 #ifdef DEBUG_GUESSSIGNALS
2155  if (DEBUGCOND(node) || true) {
2156  std::cout << " propagate TLS from " << node->getID() << " downstream\n";
2157  }
2158 #endif
2159  for (NBEdge* edge : node->getOutgoingEdges()) {
2160  // do not overwrite closer signals
2161  if (edge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET) {
2162  edge->setSignalPosition(node->getPosition(), node);
2163  }
2164  }
2165  }
2166  }
2167  std::set<NBEdge*> seen;
2168  std::set<NBEdge*> check;
2169  for (const auto& item : myNodes) {
2170  for (NBEdge* edge : item.second->getOutgoingEdges()) {
2171  if (edge->getSignalPosition() != Position::INVALID) {
2172  check.insert(edge);
2173  seen.insert(edge);
2174 #ifdef DEBUG_GUESSSIGNALS
2175  if (DEBUGCOND(edge->getSignalNode()) || true) {
2176  std::cout << " primary signalPosition edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2177  }
2178 #endif
2179  }
2180  }
2181  }
2182  // propagate signal position until the next real intersection
2183  while (check.size() > 0) {
2184  NBEdge* const edge = *check.begin();
2185  check.erase(check.begin());
2186  seen.insert(edge);
2187  NBNode* const nextNode = edge->getToNode();
2188  if (nextNode->geometryLike() && !nextNode->isTLControlled()) {
2189  for (NBEdge* const outEdge : nextNode->getOutgoingEdges()) {
2190  if (seen.count(outEdge) == 0) {
2191  outEdge->setSignalPosition(edge->getSignalPosition(), edge->getSignalNode());
2192 #ifdef DEBUG_GUESSSIGNALS
2193  if (DEBUGCOND(edge->getSignalNode()) || true) {
2194  std::cout << " setSignalPosition edge=" << outEdge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2195  }
2196 #endif
2197  check.insert(outEdge);
2198  }
2199  }
2200  }
2201  }
2202 
2203  // check which nodes should be controlled
2204  const int slack = oc.getInt("tls.guess-signals.slack");
2205  for (std::map<std::string, NBNode*>::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2206  NBNode* node = i->second;
2207  if (myUnsetTLS.count(node) != 0) {
2208  continue;
2209  }
2210  const EdgeVector& incoming = node->getIncomingEdges();
2211  const EdgeVector& outgoing = node->getOutgoingEdges();
2212  if (!node->isTLControlled() && incoming.size() > 1 && !node->geometryLike()
2214  && node->getType() != SumoXMLNodeType::RAIL_CROSSING) {
2215  std::vector<const NBNode*> signals;
2216  int foundSignals = 0;
2217  int missingSignals = 0;
2218  // check if there is a signal at every incoming edge
2219  for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2220  const NBEdge* inEdge = *it_i;
2222  if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2223 #ifdef DEBUG_GUESSSIGNALS
2224  if (DEBUGCOND(node)) {
2225  std::cout << " noTLS, edge=" << inEdge->getID() << "\n";
2226  }
2227 #endif
2228  missingSignals++;
2229  if (missingSignals > slack) {
2230  break;
2231  }
2232  }
2233  } else {
2234  foundSignals++;
2235  }
2236  }
2237  missingSignals = 0;
2238  int foundSignalsAtDist = 0;
2239  if (foundSignals > 1 && missingSignals <= slack && missingSignals < foundSignals) {
2240  node->updateSurroundingGeometry();
2241  // check if all signals are within the required distance
2242  // (requires detailed geometry computation)
2243  for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2244  const NBEdge* inEdge = *it_i;
2245  if (inEdge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET || inEdge->getSignalOffset() > signalDist) {
2246  if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2247 #ifdef DEBUG_GUESSSIGNALS
2248  if (DEBUGCOND(node)) {
2249  std::cout << " noTLS, edge=" << inEdge->getID() << " offset=" << inEdge->getSignalOffset() << " tlsPos=" << inEdge->getSignalPosition() << "\n";
2250  }
2251 #endif
2252  missingSignals++;
2253  if (missingSignals > slack) {
2254  break;
2255  }
2256  }
2257  } else {
2258  foundSignalsAtDist++;
2259  }
2260  const NBNode* signal = inEdge->getSignalNode();
2261  if (signal != nullptr) {
2262  signals.push_back(signal);
2263  }
2264  }
2265  // outgoing edges may be tagged with pedestrian crossings. These
2266  // should also be merged into the main TLS
2267  for (const NBEdge* outEdge : outgoing) {
2268  NBNode* cand = outEdge->getToNode();
2269  if (cand->isTLControlled() && cand->geometryLike() && outEdge->getLength() <= signalDist) {
2270 #ifdef DEBUG_GUESSSIGNALS
2271  if (DEBUGCOND(node)) {
2272  std::cout << " node=" << node->getID() << " outEdge=" << outEdge->getID() << " signalNode=" << cand->getID() << " len=" << outEdge->getLength() << "\n";
2273  }
2274 #endif
2275  signals.push_back(cand);
2276  }
2277  }
2278  }
2279  if (foundSignalsAtDist > 1 && missingSignals <= slack && missingSignals < foundSignalsAtDist) {
2280  for (const NBNode* s : signals) {
2281  std::set<NBTrafficLightDefinition*> tls = s->getControllingTLS();
2282  const_cast<NBNode*>(s)->reinit(s->getPosition(), SumoXMLNodeType::PRIORITY);
2283  for (std::set<NBTrafficLightDefinition*>::iterator k = tls.begin(); k != tls.end(); ++k) {
2284  tlc.removeFully(s->getID());
2285  }
2286  }
2287  //if (true) std::cout << " node=" << node->getID() << " signals=" << toString(signals) << "\n";
2288  NBTrafficLightDefinition* tlDef = new NBOwnTLDef("GS_" + node->getID(), node, 0, type);
2289  // @todo patch endOffset for all incoming lanes according to the signal positions
2290  if (!tlc.insert(tlDef)) {
2291  // actually, nothing should fail here
2292  WRITE_WARNINGF(TL("Could not build joined tls '%'."), node->getID());
2293  delete tlDef;
2294  return;
2295  }
2296  }
2297  }
2298  }
2299  }
2300 
2301  // guess joined tls first, if wished
2302  if (oc.getBool("tls.guess.joining")) {
2303  // get node clusters
2304  NodeClusters cands;
2305  generateNodeClusters(oc.getFloat("tls.join-dist"), cands);
2306  // check these candidates (clusters) whether they should be controlled by a tls
2307  for (NodeClusters::iterator i = cands.begin(); i != cands.end();) {
2308  NodeSet& c = (*i);
2309  // regard only junctions which are not yet controlled and are not
2310  // forbidden to be controlled
2311  for (NodeSet::iterator j = c.begin(); j != c.end();) {
2312  if ((*j)->isTLControlled() || myUnsetTLS.count(*j) != 0) {
2313  c.erase(j++);
2314  } else {
2315  ++j;
2316  }
2317  }
2318  // check whether the cluster should be controlled
2319  // to avoid gigantic clusters, assume that at most 4 nodes should be needed for a guessed-joined-tls
2320  if (c.size() == 0 || !shouldBeTLSControlled(c, laneSpeedThreshold * (double)c.size() / MIN2((double)c.size(), 4.))) {
2321  i = cands.erase(i);
2322  } else {
2323  ++i;
2324  }
2325  }
2326  // cands now only contain sets of junctions that shall be joined into being tls-controlled
2327  for (auto nodeSet : cands) {
2328  std::vector<NBNode*> nodes;
2329  for (NBNode* node : nodeSet) {
2330  nodes.push_back(node);
2331  myGuessedTLS.insert(node);
2332  }
2333  const std::string& id = createClusterId(nodeSet, "joinedG_");
2334  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2335  if (!tlc.insert(tlDef)) {
2336  // actually, nothing should fail here
2337  WRITE_WARNING(TL("Could not build guessed, joined tls."));
2338  delete tlDef;
2339  return;
2340  }
2341  }
2342  }
2343 
2344  // guess single tls
2345  if (oc.getBool("tls.guess")) {
2346  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2347  NBNode* cur = (*i).second;
2348  // do nothing if already is tl-controlled
2349  if (cur->isTLControlled()) {
2350  continue;
2351  }
2352  // do nothing if in the list of explicit non-controlled junctions
2353  if (myUnsetTLS.count(cur) != 0) {
2354  continue;
2355  }
2356  NodeSet c;
2357  c.insert(cur);
2358  if (!shouldBeTLSControlled(c, laneSpeedThreshold) || cur->geometryLike()) {
2359  continue;
2360  }
2361  setAsTLControlled(cur, tlc, type);
2362  myGuessedTLS.insert(cur);
2363  }
2364  }
2365 }
2366 
2367 
2368 void
2370  std::set<NBTrafficLightDefinition*> recompute;
2371  for (NBNode* node : myGuessedTLS) {
2372  if (!node->hasConflict() || !recheckTLSThreshold(node)) {
2373  const std::set<NBTrafficLightDefinition*>& tlDefs = node->getControllingTLS();
2374  recompute.insert(tlDefs.begin(), tlDefs.end());
2375  node->removeTrafficLights(true);
2376  for (NBEdge* edge : node->getIncomingEdges()) {
2377  edge->clearControllingTLInformation();
2378  }
2379  }
2380  }
2381  for (NBTrafficLightDefinition* def : recompute) {
2382  if (def->getNodes().size() == 0) {
2383  tlc.removeFully(def->getID());
2384  } else {
2385  def->setParticipantsInformation();
2386  def->setTLControllingInformation();
2388  }
2389  }
2390 }
2391 
2392 
2393 bool
2395  if (!node->isTLControlled()) {
2396  return false;
2397  }
2398  if ((*node->getControllingTLS().begin())->getNodes().size() != 1) {
2399  // unable to perform check for a joined tls
2400  return true;
2401  }
2402  NodeSet c;
2403  c.insert(node);
2404  const double laneSpeedThreshold = OptionsCont::getOptions().getFloat("tls.guess.threshold");
2405  return shouldBeTLSControlled(c, laneSpeedThreshold, true);
2406 }
2407 
2408 
2409 void
2411  for (const auto& item : myNodes) {
2412  item.second->computeKeepClear();
2413  }
2414 }
2415 
2416 
2417 void
2419  const std::vector<std::string> excludeList = OptionsCont::getOptions().getStringVector("tls.join-exclude");
2420  for (const std::string& tlsID : excludeList) {
2421  if (!tlc.exist(tlsID, false)) {
2422  WRITE_WARNINGF("Unknown tls ID '%' in option tls.join-exclude", tlsID);
2423  }
2424  }
2425  std::set<std::string> exclude(excludeList.begin(), excludeList.end());
2426  NodeClusters cands;
2427  generateNodeClusters(maxdist, cands);
2428  for (NodeSet& c : cands) {
2429  for (NodeSet::iterator j = c.begin(); j != c.end();) {
2430  if (!(*j)->isTLControlled() || exclude.count((*(*j)->getControllingTLS().begin())->getID()) != 0) {
2431  c.erase(j++);
2432  } else {
2433  ++j;
2434  }
2435  }
2436  if (c.size() < 2 || onlyCrossings(c) || customTLID(c)) {
2437  continue;
2438  }
2439  // figure out type of the joined TLS
2440  Position dummyPos;
2441  bool dummySetTL = false;
2442  std::string id = "joinedS_"; // prefix (see #3871)
2443  TrafficLightType type;
2445  analyzeCluster(c, id, dummyPos, dummySetTL, type, nodeType);
2446  for (NBNode* j : c) {
2447  std::set<NBTrafficLightDefinition*> tls = j->getControllingTLS();
2448  j->removeTrafficLights();
2449  for (NBTrafficLightDefinition* k : tls) {
2450  tlc.removeFully(k->getID());
2451  }
2452  }
2453  std::vector<NBNode*> nodes;
2454  for (NBNode* j : c) {
2455  nodes.push_back(j);
2456  }
2457  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2458  if (!tlc.insert(tlDef)) {
2459  // actually, nothing should fail here
2460  WRITE_WARNING(TL("Could not build a joined tls."));
2461  delete tlDef;
2462  return;
2463  }
2464  }
2465 }
2466 
2467 
2468 void
2470  TrafficLightType type, std::string id) {
2471  if (id == "") {
2472  id = node->getID();
2473  }
2474  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, node, 0, type);
2475  if (!tlc.insert(tlDef)) {
2476  // actually, nothing should fail here
2477  WRITE_WARNINGF(TL("Building a tl-logic for junction '%' twice is not possible."), id);
2478  delete tlDef;
2479  return;
2480  }
2481 }
2482 
2483 
2484 // -----------
2485 void
2487  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2488  (*i).second->computeLanes2Lanes();
2489  }
2490 }
2491 
2492 
2493 // computes the "wheel" of incoming and outgoing edges for every node
2494 void
2496  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2497  (*i).second->computeLogic(ec);
2498  }
2499 }
2500 
2501 
2502 void
2504  std::set<NBNode*> roundaboutNodes;
2505  const bool checkLaneFoesAll = oc.getBool("check-lane-foes.all");
2506  const bool checkLaneFoesRoundabout = !checkLaneFoesAll && oc.getBool("check-lane-foes.roundabout");
2507  if (checkLaneFoesRoundabout) {
2508  const std::set<EdgeSet>& roundabouts = ec.getRoundabouts();
2509  for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
2510  for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
2511  roundaboutNodes.insert((*j)->getToNode());
2512  }
2513  }
2514  }
2515  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2516  const bool checkLaneFoes = checkLaneFoesAll || (checkLaneFoesRoundabout && roundaboutNodes.count((*i).second) > 0);
2517  (*i).second->computeLogic2(checkLaneFoes);
2518  }
2519 }
2520 
2521 
2522 void
2524  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2525  delete ((*i).second);
2526  }
2527  myNodes.clear();
2528  for (auto& item : myExtractedNodes) {
2529  delete item.second;
2530  }
2531  myExtractedNodes.clear();
2532 }
2533 
2534 
2535 std::string
2537  int counter = 0;
2538  std::string freeID = "SUMOGenerated" + toString<int>(counter);
2539  // While there is a node with id equal to freeID
2540  while (retrieve(freeID) != nullptr) {
2541  // update counter and generate a new freeID
2542  counter++;
2543  freeID = "SUMOGenerated" + toString<int>(counter);
2544  }
2545  return freeID;
2546 }
2547 
2548 
2549 void
2550 NBNodeCont::computeNodeShapes(double mismatchThreshold) {
2551  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2552  (*i).second->computeNodeShape(mismatchThreshold);
2553  }
2554 }
2555 
2556 
2557 void
2559  WRITE_MESSAGE(TL("-----------------------------------------------------"));
2560  WRITE_MESSAGE(TL("Summary:"));
2561 
2562  int numUnregulatedJunctions = 0;
2563  int numDeadEndJunctions = 0;
2564  int numTrafficLightJunctions = 0;
2565  int numPriorityJunctions = 0;
2566  int numRightBeforeLeftJunctions = 0;
2567  int numLeftBeforeRightJunctions = 0;
2568  int numAllWayStopJunctions = 0;
2569  int numZipperJunctions = 0;
2570  int numDistrictJunctions = 0;
2571  int numRailCrossing = 0;
2572  int numRailSignals = 0;
2573  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2574  switch ((*i).second->getType()) {
2576  ++numUnregulatedJunctions;
2577  break;
2579  ++numDeadEndJunctions;
2580  break;
2584  ++numTrafficLightJunctions;
2585  break;
2588  ++numPriorityJunctions;
2589  break;
2591  ++numRightBeforeLeftJunctions;
2592  break;
2594  ++numLeftBeforeRightJunctions;
2595  break;
2597  ++numAllWayStopJunctions;
2598  break;
2600  ++numZipperJunctions;
2601  break;
2603  ++numDistrictJunctions;
2604  break;
2606  ++numRailCrossing;
2607  break;
2609  ++numRailSignals;
2610  break;
2612  // should not happen
2613  break;
2614  default:
2615  break;
2616  }
2617  }
2618  WRITE_MESSAGE(TL(" Node type statistics:"));
2619  WRITE_MESSAGE(" Unregulated junctions : " + toString(numUnregulatedJunctions));
2620  if (numDeadEndJunctions > 0) {
2621  WRITE_MESSAGE(" Dead-end junctions : " + toString(numDeadEndJunctions));
2622  }
2623  WRITE_MESSAGE(" Priority junctions : " + toString(numPriorityJunctions));
2624  WRITE_MESSAGE(" Right-before-left junctions : " + toString(numRightBeforeLeftJunctions));
2625  if (numLeftBeforeRightJunctions > 0) {
2626  WRITE_MESSAGE(" Left-before-right junctions : " + toString(numLeftBeforeRightJunctions));
2627  }
2628  if (numTrafficLightJunctions > 0) {
2629  WRITE_MESSAGE(" Traffic light junctions : " + toString(numTrafficLightJunctions));
2630  }
2631  if (numAllWayStopJunctions > 0) {
2632  WRITE_MESSAGE(" All-way stop junctions : " + toString(numAllWayStopJunctions));
2633  }
2634  if (numZipperJunctions > 0) {
2635  WRITE_MESSAGE(" Zipper-merge junctions : " + toString(numZipperJunctions));
2636  }
2637  if (numRailCrossing > 0) {
2638  WRITE_MESSAGE(" Rail crossing junctions : " + toString(numRailCrossing));
2639  }
2640  if (numRailSignals > 0) {
2641  WRITE_MESSAGE(" Rail signal junctions : " + toString(numRailSignals));
2642  }
2643  if (numDistrictJunctions > 0) {
2644  WRITE_MESSAGE(" District junctions : " + toString(numDistrictJunctions));
2645  }
2646  const GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
2647  WRITE_MESSAGE(TL(" Network boundaries:"));
2648  WRITE_MESSAGE(" Original boundary : " + toString(geoConvHelper.getOrigBoundary()));
2649  WRITE_MESSAGE(" Applied offset : " + toString(geoConvHelper.getOffsetBase()));
2650  WRITE_MESSAGE(" Converted boundary : " + toString(geoConvHelper.getConvBoundary()));
2651  WRITE_MESSAGE(TL("-----------------------------------------------------"));
2652 }
2653 
2654 
2655 std::vector<std::string>
2657  std::vector<std::string> ret;
2658  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2659  ret.push_back((*i).first);
2660  }
2661  return ret;
2662 }
2663 
2664 
2665 void
2666 NBNodeCont::addPrefix(const std::string& prefix) {
2667  // make a copy of node containers
2668  const auto nodeContainerCopy = myNodes;
2669  myNodes.clear();
2670  for (const auto& node : nodeContainerCopy) {
2671  node.second->setID(prefix + node.second->getID());
2672  myNodes[node.second->getID()] = node.second;
2673  }
2674 }
2675 
2676 
2677 void
2678 NBNodeCont::rename(NBNode* node, const std::string& newID) {
2679  if (myNodes.count(newID) != 0) {
2680  throw ProcessError(TLF("Attempt to rename node using existing id '%'", newID));
2681  }
2682  myNodes.erase(node->getID());
2683  node->setID(newID);
2684  myNodes[newID] = node;
2685 }
2686 
2687 
2688 void
2689 NBNodeCont::discardTrafficLights(NBTrafficLightLogicCont& tlc, bool geometryLike, bool guessSignals) {
2690  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2691  NBNode* node = i->second;
2692  if (node->isTLControlled() && (!geometryLike || node->geometryLike())) {
2693  // make a copy of tldefs
2694  const std::set<NBTrafficLightDefinition*> tldefs = node->getControllingTLS();
2695  if (geometryLike && (*tldefs.begin())->getNodes().size() > 1) {
2696  // do not remove joined tls when only removing geometry-like tls
2697  continue;
2698  }
2699  if (guessSignals && node->isTLControlled() && node->geometryLike()) {
2700  // record signal location
2701  for (NBEdge* edge : node->getOutgoingEdges()) {
2702  edge->setSignalPosition(node->getPosition(), nullptr);
2703 #ifdef DEBUG_GUESSSIGNALS
2704  std::cout << " discard-simple " << node->getID() << " edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2705 #endif
2706  }
2707  }
2708  for (std::set<NBTrafficLightDefinition*>::const_iterator it = tldefs.begin(); it != tldefs.end(); ++it) {
2709  NBTrafficLightDefinition* tlDef = *it;
2710  node->removeTrafficLight(tlDef);
2711  tlc.extract(tlDef);
2712  }
2714  node->reinit(node->getPosition(), newType);
2715  }
2716  }
2717 }
2718 
2719 
2720 void
2722  for (auto& item : myNodes) {
2723  NBNode* node = item.second;
2724  if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
2726  }
2727  }
2728 }
2729 
2730 
2731 int
2732 NBNodeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBTrafficLightLogicCont& tlc) {
2733  bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.node-start");
2734  if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
2735  return 0;
2736  }
2737  std::vector<std::string> avoid;
2738  if (startGiven) {
2739  avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.node-start") - 1));
2740  } else {
2741  avoid = getAllNames();
2742  }
2743  std::set<std::string> reserve;
2744  if (reservedIDs) {
2745  NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "node:", reserve); // backward compatibility
2746  NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "junction:", reserve); // selection format
2747  avoid.insert(avoid.end(), reserve.begin(), reserve.end());
2748  }
2749  IDSupplier idSupplier("", avoid);
2750  NodeSet toChange;
2751  for (NodeCont::iterator it = myNodes.begin(); it != myNodes.end(); it++) {
2752  if (startGiven) {
2753  toChange.insert(it->second);
2754  continue;
2755  }
2756  if (numericaIDs) {
2757  try {
2758  StringUtils::toLong(it->first);
2759  } catch (NumberFormatException&) {
2760  toChange.insert(it->second);
2761  }
2762  }
2763  if (reservedIDs && reserve.count(it->first) > 0) {
2764  toChange.insert(it->second);
2765  }
2766  }
2767  const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
2768  for (NBNode* node : toChange) {
2769  myNodes.erase(node->getID());
2770  }
2771  for (NBNode* node : toChange) {
2772  if (origNames && node->getParameter(SUMO_PARAM_ORIGID) == "") {
2773  node->setParameter(SUMO_PARAM_ORIGID, node->getID());
2774  }
2775  node->setID(idSupplier.getNext());
2776  for (NBTrafficLightDefinition* tlDef : node->getControllingTLS()) {
2777  tlc.rename(tlDef, node->getID());
2778  }
2779  myNodes[node->getID()] = node;
2780  }
2781  if (prefix.empty()) {
2782  return (int)toChange.size();
2783  } else {
2784  int renamed = 0;
2785  // make a copy because we will modify the map
2786  auto oldNodes = myNodes;
2787  for (auto item : oldNodes) {
2788  if (!StringUtils::startsWith(item.first, prefix)) {
2789  rename(item.second, prefix + item.first);
2790  for (NBTrafficLightDefinition* tlDef : item.second->getControllingTLS()) {
2791  if (!StringUtils::startsWith(tlDef->getID(), prefix)) {
2792  tlc.rename(tlDef, prefix + tlDef->getID());
2793  }
2794  }
2795  renamed++;
2796  }
2797  }
2798  return renamed;
2799  }
2800 }
2801 
2802 
2803 int
2805  // guess outer fringe by topology and being on the pareto-boundary
2806  NodeSet topRightFront;
2807  NodeSet topLeftFront;
2808  NodeSet bottomRightFront;
2809  NodeSet bottomLeftFront;
2810  for (const auto& item : myNodes) {
2811  paretoCheck(item.second, topRightFront, 1, 1);
2812  paretoCheck(item.second, topLeftFront, -1, 1);
2813  paretoCheck(item.second, bottomRightFront, 1, -1);
2814  paretoCheck(item.second, bottomLeftFront, -1, -1);
2815  }
2816  NodeSet front;
2817  front.insert(topRightFront.begin(), topRightFront.end());
2818  front.insert(topLeftFront.begin(), topLeftFront.end());
2819  front.insert(bottomRightFront.begin(), bottomRightFront.end());
2820  front.insert(bottomLeftFront.begin(), bottomLeftFront.end());
2821  int numFringe = 0;
2822  for (NBNode* n : front) {
2823  const int in = (int)n->getIncomingEdges().size();
2824  const int out = (int)n->getOutgoingEdges().size();
2825  if ((in <= 1 && out <= 1) &&
2826  (in == 0 || out == 0
2827  || n->getIncomingEdges().front()->isTurningDirectionAt(n->getOutgoingEdges().front()))) {
2828  n->setFringeType(FringeType::OUTER);
2829  numFringe++;
2830  }
2831  }
2832  // guess outer fringe by topology and speed
2833  const double speedThreshold = OptionsCont::getOptions().getFloat("fringe.guess.speed-threshold");
2834  for (const auto& item : myNodes) {
2835  NBNode* n = item.second;
2836  if (front.count(n) != 0) {
2837  continue;
2838  }
2839  if (n->getEdges().size() == 1 && n->getEdges().front()->getSpeed() > speedThreshold) {
2841  numFringe++;
2842  }
2843  }
2844  return numFringe;
2845 }
2846 
2847 
2848 void
2849 NBNodeCont::paretoCheck(NBNode* node, NodeSet& frontier, int xSign, int ySign) {
2850  const double x = node->getPosition().x() * xSign;
2851  const double y = node->getPosition().y() * ySign;
2852  std::vector<NBNode*> dominated;
2853  for (NBNode* fn : frontier) {
2854  const double x2 = fn->getPosition().x() * xSign;
2855  const double y2 = fn->getPosition().y() * ySign;
2856  if (x2 >= x && y2 >= y) {
2857  return;
2858  } else if (x2 <= x && y2 <= y) {
2859  dominated.push_back(fn);
2860  }
2861  }
2862  frontier.insert(node);
2863  for (NBNode* r : dominated) {
2864  frontier.erase(r);
2865  }
2866 }
2867 
2868 
2869 void
2871  for (const auto& item : myNodes) {
2872  NBNode* n = item.second;
2873  if (n->isTLControlled() && n->getRightOfWay() == RightOfWay::DEFAULT) {
2874  bool hasNEMA = false;
2875  for (NBTrafficLightDefinition* tl : n->getControllingTLS()) {
2876  if (tl->getType() == TrafficLightType::NEMA) {
2877  hasNEMA = true;
2878  break;
2879  }
2880  }
2881  if (hasNEMA) {
2882  // NEMA controller defaults to allway_stop behavior when switched off
2884  }
2885  }
2886  }
2887 }
2888 
2889 
2890 bool
2892  bool hadShapes = false;
2893  for (const auto& item : myNodes) {
2894  if (item.second->getShape().size() > 0 && !item.second->hasCustomShape()) {
2895  hadShapes = true;
2896  item.second->resetShape();
2897  }
2898  }
2899  return hadShapes;
2900 }
2901 /****************************************************************************/
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition: MsgHandler.h:298
#define WRITE_ERRORF(...)
Definition: MsgHandler.h:305
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:297
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:295
#define TL(string)
Definition: MsgHandler.h:315
#define TLF(string,...)
Definition: MsgHandler.h:317
std::set< NBNode *, ComparatorIdLess > NodeSet
Definition: NBCont.h:52
std::set< NBEdge * > EdgeSet
container for unique edges
Definition: NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition: NBCont.h:35
#define DEBUGCOND(obj)
Definition: NBNodeCont.cpp:73
#define MAX_SLIPLANE_LENGTH
Definition: NBNodeCont.cpp:61
bool isForVulnerableModes(SVCPermissions permissions)
Returns whether an edge with the given permissions allows only vulnerable road users.
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a railway edge.
const SVCPermissions SVC_UNSPECIFIED
permissions not specified
bool isWaterway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a waterway edge.
const std::string & getVehicleClassNames(SVCPermissions permissions, bool expand)
Returns the ids of the given classes, divided using a ' '.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_VULNERABLE
@ SVC_TRAM
vehicle is a light rail
@ SVC_PEDESTRIAN
pedestrian
TrafficLightType
const std::string SUMO_PARAM_ORIGID
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
int gPrecision
the precision for floating point outputs
Definition: StdDefs.cpp:26
bool gDebugFlag1
global utility flags for debugging
Definition: StdDefs.cpp:37
const double SUMO_const_laneWidth
Definition: StdDefs.h:48
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string joinNamedToString(const std::set< T *, C > &ns, const T_BETWEEN &between)
Definition: ToString.h:317
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition: ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
static methods for processing the coordinates conversion for the current net
Definition: GeoConvHelper.h:53
static GeoConvHelper & getProcessing()
the coordinate transformation to use for input conversion and processing
Definition: GeoConvHelper.h:84
const Boundary & getOrigBoundary() const
Returns the original boundary.
const Position getOffsetBase() const
Returns the network base.
const Boundary & getConvBoundary() const
Returns the converted boundary.
std::string getNext()
Returns the next id.
Definition: IDSupplier.cpp:51
A container for districts.
A class representing a single district.
Definition: NBDistrict.h:62
Storage for edges, including some functionality operating on multiple edges.
Definition: NBEdgeCont.h:59
std::map< std::string, NBEdge * >::const_iterator begin() const
Returns the pointer to the begin of the stored edges.
Definition: NBEdgeCont.h:171
NBEdge * getByID(const std::string &edgeID) const
Returns the edge with id if it exists.
const std::set< EdgeSet > getRoundabouts() const
Returns the determined roundabouts.
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition: NBEdgeCont.h:178
void extract(NBDistrictCont &dc, NBEdge *edge, bool remember=false)
Removes the given edge from the container like erase but does not delete it.
Definition: NBEdgeCont.cpp:418
void erase(NBDistrictCont &dc, NBEdge *edge)
Removes the given edge from the container (deleting it)
Definition: NBEdgeCont.cpp:411
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
Definition: NBEdgeCont.cpp:281
bool hasPostProcessConnection(const std::string &from, const std::string &to="")
add post process connections
void removeRoundaboutEdges(const EdgeSet &toRemove)
remove edges from all stored roundabouts
void joinSameNodeConnectingEdges(NBDistrictCont &dc, NBTrafficLightLogicCont &tlc, EdgeVector edges)
Joins the given edges because they connect the same nodes.
std::vector< std::string > getAllNames() const
Returns all ids of known edges.
Definition: NBEdgeCont.cpp:758
The representation of a single edge during network building.
Definition: NBEdge.h:92
double getLength() const
Returns the computed length of the edge.
Definition: NBEdge.h:589
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition: NBEdge.cpp:4368
double getLoadedLength() const
Returns the length was set explicitly or the computed length if it wasn't set.
Definition: NBEdge.h:598
static EdgeVector filterByPermissions(const EdgeVector &edges, SVCPermissions permissions)
return only those edges that permit at least one of the give permissions
Definition: NBEdge.cpp:4836
const Position & getSignalPosition() const
Returns the position of a traffic signal on this edge.
Definition: NBEdge.h:708
EdgeBuildingStep getStep() const
The building step of this edge.
Definition: NBEdge.h:631
NBEdge * getStraightContinuation(SVCPermissions permissions) const
return the straightest follower edge for the given permissions or nullptr (never returns turn-arounds...
Definition: NBEdge.cpp:4847
const NBNode * getSignalNode() const
Returns the node that (possibly) represents a traffic signal controlling at the end of this edge.
Definition: NBEdge.h:713
const std::string & getID() const
Definition: NBEdge.h:1524
bool isNearEnough2BeJoined2(NBEdge *e, double threshold) const
Check if edge is near enought to be joined to another edge.
Definition: NBEdge.cpp:4012
NBNode * getToNode() const
Returns the destination node of the edge.
Definition: NBEdge.h:542
@ INIT
The edge has been loaded, nothing is computed yet.
double getSpeed() const
Returns the speed allowed on this edge.
Definition: NBEdge.h:615
static const double UNSPECIFIED_SIGNAL_OFFSET
unspecified signal offset
Definition: NBEdge.h:363
@ USER
The connection was given by the user.
double getAngleAtNode(const NBNode *const node) const
Returns the angle of the edge's geometry at the given node.
Definition: NBEdge.cpp:2148
double getSignalOffset() const
Returns the offset of a traffic signal from the end of this edge.
Definition: NBEdge.cpp:4415
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1037
NBNode * getFromNode() const
Returns the origin node of the edge.
Definition: NBEdge.h:535
EdgeVector getIncomingEdges() const
Returns the list of incoming edges unsorted.
Definition: NBEdge.cpp:1386
static void loadPrefixedIDsFomFile(const std::string &file, const std::string prefix, std::set< std::string > &into)
Add prefixed ids defined in file.
Definition: NBHelpers.cpp:104
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition: NBHelpers.cpp:45
static void loadEdgesFromFile(const std::string &file, std::set< std::string > &into)
Add edge ids defined in file (either ID or edge:ID per line) into the given set.
Definition: NBHelpers.cpp:86
void clear()
deletes all nodes
std::set< std::string > myJoinExclusions
set of node ids which should not be joined
Definition: NBNodeCont.h:452
std::vector< std::vector< std::string > > myRailComponents
network components that must be removed if not connected to the road network via stop access
Definition: NBNodeCont.h:476
void pruneClusterFringe(NodeSet &cluster, double maxDist) const
remove geometry-like fringe nodes from cluster
Definition: NBNodeCont.cpp:955
static double getDiameter(const NodeSet &cluster)
compute the maximum distance between any two cluster nodes
NamedRTree myRTree
node positions for faster lookup
Definition: NBNodeCont.h:473
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition: NBNodeCont.h:113
void avoidOverlap()
fix overlap
Definition: NBNodeCont.cpp:538
int removeRailComponents(NBDistrictCont &dc, NBEdgeCont &ec, NBPTStopCont &sc)
Definition: NBNodeCont.cpp:417
bool onlyCrossings(const NodeSet &c) const
check wheter the set of nodes only contains pedestrian crossings
std::vector< std::pair< std::set< std::string >, NBNode * > > myClusters2Join
loaded sets of node ids to join (cleared after use)
Definition: NBNodeCont.h:455
std::string createClusterId(const NodeSet &cluster, const std::string &prefix="cluster_")
generate id from cluster node ids
Definition: NBNodeCont.h:136
void recheckGuessedTLS(NBTrafficLightLogicCont &tlc)
recheck myGuessedTLS after node logics are computed
std::vector< NodeSet > NodeClusters
Definition of a node cluster container.
Definition: NBNodeCont.h:61
void computeKeepClear()
compute keepClear status for all connections
NodeCont myNodes
The map of names to nodes.
Definition: NBNodeCont.h:446
bool reduceToCircle(NodeSet &cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector< NBNode * > cands=std::vector< NBNode * >()) const
try to find a joinable subset (recursively)
bool feasibleCluster(const NodeSet &cluster, const std::map< const NBNode *, std::vector< NBNode * > > &ptStopEnds, double maxDist, std::string &reason) const
determine wether the cluster is not too complex for joining
void registerJoinedCluster(const NodeSet &cluster)
gets all joined clusters (see doc for myClusters2Join)
std::string getFreeID()
generates a new node ID
int removeSelfLoops(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tc)
Removes self-loop edges (edges where the source and the destination node are the same)
Definition: NBNodeCont.cpp:175
bool recheckTLSThreshold(NBNode *node)
check whether a specific guessed tls should keep its type
void paretoCheck(NBNode *node, NodeSet &frontier, int xSign, int ySign)
update pareto frontier with the given node
bool maybeSlipLaneStart(const NBNode *n, EdgeVector &outgoing, double &inAngle) const
check whether the given node maybe the start of a slip lane
void addJoinExclusion(const std::vector< std::string > &ids)
Definition: NBNodeCont.cpp:691
bool erase(NBNode *node)
Removes the given node, deleting it.
Definition: NBNodeCont.cpp:146
int joinLoadedClusters(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc)
Joins loaded junction clusters (see NIXMLNodesHandler)
Definition: NBNodeCont.cpp:752
static bool geometryLikeForClass(const NBNode *n, SVCPermissions permissions)
check whether the node is geometryLike when only considering edges that support the given permissions
Definition: NBNodeCont.cpp:669
void applyConditionalDefaults()
apply default values after loading
void pruneSlipLaneNodes(NodeSet &cluster, double maxDist) const
remove nodes that form a slip lane from cluster
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
Definition: NBNodeCont.cpp:87
std::set< const NBNode * > myUnsetTLS
nodes that are excluded from tls-guessing
Definition: NBNodeCont.h:470
int remapIDs(bool numericaIDs, bool reservedIDs, const std::string &prefix, NBTrafficLightLogicCont &tlc)
remap node IDs according to options –numerical-ids and –reserved-ids
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
Definition: NBNodeCont.cpp:116
NodeCont myExtractedNodes
The extracted nodes which are kept for reference.
Definition: NBNodeCont.h:449
void joinTLS(NBTrafficLightLogicCont &tlc, double maxdist)
Builds clusters of tls-controlled junctions and joins the control if possible.
int removeUnwishedNodes(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, NBPTStopCont &sc, NBPTLineCont &lc, NBParkingCont &pc, bool removeGeometryNodes)
Removes "unwished" nodes.
Definition: NBNodeCont.cpp:459
~NBNodeCont()
Destructor.
Definition: NBNodeCont.cpp:80
bool extract(NBNode *node, bool remember=false)
Removes the given node but does not delete it.
Definition: NBNodeCont.cpp:157
int removeComponents(NBDistrictCont &dc, NBEdgeCont &ec, const int numKeep, bool hasPTStops)
Checks the network for weak connectivity and removes all but the largest components....
Definition: NBNodeCont.cpp:330
std::vector< std::string > getAllNames() const
get all node names
void computeLogics2(const NBEdgeCont &ec, OptionsCont &oc)
compute right-of-way logic for all lane-to-lane connections
bool shouldBeTLSControlled(const NodeSet &c, double laneSpeedThreshold, bool recheck=false) const
Returns whethe the given node cluster should be controlled by a tls.
void rename(NBNode *node, const std::string &newID)
Renames the node. Throws exception if newID already exists.
void joinSimilarEdges(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, bool removeDuplicates)
Joins edges connecting the same nodes.
Definition: NBNodeCont.cpp:188
int removeIsolatedRoads(NBDistrictCont &dc, NBEdgeCont &ec)
Removes sequences of edges that are not connected with a junction. Simple roads without junctions som...
Definition: NBNodeCont.cpp:239
void joinNodeClusters(NodeClusters clusters, NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, bool resetConnections=false)
joins the given node clusters
void discardRailSignals()
discards rail signals
void addPrefix(const std::string &prefix)
add prefix to all nodes
void printBuiltNodesStatistics() const
Prints statistics about built nodes.
void setAsTLControlled(NBNode *node, NBTrafficLightLogicCont &tlc, TrafficLightType type, std::string id="")
Sets the given node as being controlled by a tls.
std::set< const NBNode * > mySplit
nodes that were created when splitting an edge
Definition: NBNodeCont.h:464
static NodeSet getClusterNeighbors(const NBNode *n, double longThreshold, NodeSet &cluster)
return all cluster neighbors for the given node
void computeLogics(const NBEdgeCont &ec)
build the list of outgoing edges and lanes
void joinNodeCluster(NodeSet clusters, NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, NBNode *predefined=nullptr, bool resetConnections=false)
void unregisterJoinedCluster(const std::set< std::string > &cluster)
remove cluster from list (on netedit-undo)
void generateNodeClusters(double maxDist, NodeClusters &into) const
Builds node clusters.
Definition: NBNodeCont.cpp:547
static bool isSlipLaneContinuation(const NBNode *cont)
whether the given node may continue a slip lane
void computeNodeShapes(double mismatchThreshold=-1)
Compute the junction shape for this node.
std::vector< std::set< std::string > > myJoinedClusters
sets of node ids which were joined
Definition: NBNodeCont.h:458
NBEdge * shortestEdge(const NodeSet &cluster, const NodeSet &startNodes, const std::vector< NBNode * > &exclude) const
find closest neighbor for building circle
std::pair< NBNode *, double > NodeAndDist
Definition: NBNodeCont.h:62
void guessTLs(OptionsCont &oc, NBTrafficLightLogicCont &tlc)
Guesses which junctions or junction clusters shall be controlled by tls.
int guessFringe()
guess and mark fringe nodes
int joinJunctions(double maxDist, NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, NBPTStopCont &sc)
Joins junctions that are very close together.
Definition: NBNodeCont.cpp:777
void computeLanes2Lanes()
divides the incoming lanes on outgoing lanes
void discardTrafficLights(NBTrafficLightLogicCont &tlc, bool geometryLike, bool guessSignals)
std::set< NBNode *, ComparatorIdLess > myGuessedTLS
nodes that received a traffic light due to guessing (–tls.guess)
Definition: NBNodeCont.h:467
std::set< std::string > myJoined
ids found in loaded join clusters used for error checking
Definition: NBNodeCont.h:461
int joinSameJunctions(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc)
Joins junctions with the same coordinates regardless of topology.
Definition: NBNodeCont.cpp:925
void analyzeCluster(NodeSet cluster, std::string &id, Position &pos, bool &hasTLS, TrafficLightType &type, SumoXMLNodeType &nodeType)
void addCluster2Join(const std::set< std::string > &cluster, NBNode *node)
add ids of nodes which shall be joined into a single node
Definition: NBNodeCont.cpp:724
bool customTLID(const NodeSet &c) const
check wheter the set of nodes contains traffic lights with custom id
bool resetNodeShapes()
reset all node shapes
static int pruneLongEdges(NodeSet &cluster, double maxDist, const bool dryRun=false)
avoid removal of long edges when joining junction clusters
bool maybeSlipLaneEnd(const NBNode *n, EdgeVector &incoming, double &outAngle) const
check whether the given node maybe the end of a slip lane
Represents a single node (junction) during network building.
Definition: NBNode.h:66
bool hasIncoming(const NBEdge *const e) const
Returns whether the given edge ends at this node.
Definition: NBNode.cpp:1856
RightOfWay getRightOfWay() const
Returns hint on how to compute right of way.
Definition: NBNode.h:300
void reinit(const Position &position, SumoXMLNodeType type, bool updateEdgeGeometries=false)
Resets initial values.
Definition: NBNode.cpp:338
SumoXMLNodeType getType() const
Returns the type of this node.
Definition: NBNode.h:285
void setRightOfWay(RightOfWay rightOfWay)
set method for computing right-of-way
Definition: NBNode.h:569
static bool isTrafficLight(SumoXMLNodeType type)
return whether the given type is a traffic light
Definition: NBNode.cpp:4017
std::vector< std::pair< NBEdge *, NBEdge * > > getEdgesToJoin() const
get edges to join
Definition: NBNode.cpp:2578
void removeTrafficLights(bool setAsPriority=false)
Removes all references to traffic lights that control this tls.
Definition: NBNode.cpp:413
const EdgeVector & getOutgoingEdges() const
Returns this node's outgoing edges (The edges which start at this node)
Definition: NBNode.h:273
const EdgeVector & getEdges() const
Returns all edges which participate in this node (Edges that start or end at this node)
Definition: NBNode.h:278
const EdgeVector & getIncomingEdges() const
Returns this node's incoming edges (The edges which yield in this node)
Definition: NBNode.h:268
void replaceIncoming(NBEdge *which, NBEdge *by, int laneOff)
Replaces occurrences of the first edge within the list of incoming by the second Connections are rema...
Definition: NBNode.cpp:1750
EdgeVector getPassengerEdges(bool incoming) const
return edges that permit passengers (either incoming or outgoing)
Definition: NBNode.cpp:2347
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition: NBNode.h:336
void removeTrafficLight(NBTrafficLightDefinition *tlDef)
Removes the given traffic light from this node.
Definition: NBNode.cpp:406
void updateSurroundingGeometry()
update geometry of node and surrounding edges
Definition: NBNode.cpp:1142
const Position & getPosition() const
Definition: NBNode.h:260
bool checkIsRemovable() const
check if node is removable
Definition: NBNode.cpp:2495
void setFringeType(FringeType fringeType)
set method for computing right-of-way
Definition: NBNode.h:574
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition: NBNode.cpp:3781
bool isNearDistrict() const
@chech if node is near district
Definition: NBNode.cpp:2643
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition: NBNode.h:331
static bool isRailwayNode(const NBNode *n)
whether the given node only has rail edges
A traffic light logics which must be computed (only nodes/edges are given)
Definition: NBOwnTLDef.h:44
void replaceEdge(const std::string &edgeID, const EdgeVector &replacement)
replace the edge with the given edge list in all lines
Container for public transport stops during the net building process.
Definition: NBPTStopCont.h:44
void replaceEdge(const std::string &edgeID, const std::vector< NBEdge * > &replacement)
replace the edge with the closes edge on the given edge list in all stops
const std::map< std::string, std::shared_ptr< NBPTStop > > & getStops() const
Returns an unmodifiable reference to the stored pt stops.
Definition: NBPTStopCont.h:62
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
Definition: NBParking.cpp:78
The base class for traffic light logic definitions.
const std::vector< NBNode * > & getNodes() const
Returns the list of controlled nodes.
virtual void replaceRemoved(NBEdge *removed, int removedLane, NBEdge *by, int byLane, bool incoming)=0
Replaces a removed edge/lane.
A container for traffic light definitions and built programs.
bool exist(const std::string &newID, bool requireComputed=true) const
check if exists a definition with the given ID
void rename(NBTrafficLightDefinition *tlDef, const std::string &newID)
rename traffic light
bool computeSingleLogic(OptionsCont &oc, NBTrafficLightDefinition *def)
Computes a specific traffic light logic (using by netedit)
bool removeFully(const std::string id)
Removes a logic definition (and all programs) from the dictionary.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
void extract(NBTrafficLightDefinition *definition)
Extracts a traffic light definition from myDefinitions but keeps it in myExtracted for eventual * del...
Allows to store the object; used as context while traveling the rtree in TraCI.
Definition: Named.h:90
Base class for objects which have an id.
Definition: Named.h:54
virtual void setID(const std::string &newID)
resets the id
Definition: Named.h:82
const std::string & getID() const
Returns the id.
Definition: Named.h:74
void Remove(const float a_min[2], const float a_max[2], Named *const &a_data)
Remove entry.
Definition: NamedRTree.h:90
void Insert(const float a_min[2], const float a_max[2], Named *const &a_data)
Insert entry.
Definition: NamedRTree.h:79
int Search(const float a_min[2], const float a_max[2], const Named::StoringVisitor &c) const
Find all within search rectangle.
Definition: NamedRTree.h:112
A storage for options typed value containers)
Definition: OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
static OptionsCont & getOptions()
Retrieves the options.
Definition: OptionsCont.cpp:60
static std::string realString(const double v, const int precision=gPrecision)
Helper method for string formatting.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
A point in 2D or 3D with translation and scaling methods.
Definition: Position.h:37
void setx(double x)
set position x
Definition: Position.h:70
static const Position INVALID
used to indicate that a position is valid
Definition: Position.h:322
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:276
double x() const
Returns the x-position.
Definition: Position.h:55
void add(const Position &pos)
Adds the given position to this one.
Definition: Position.h:132
void setz(double z)
set position z
Definition: Position.h:80
void mul(double val)
Multiplies position with the given value.
Definition: Position.h:105
double z() const
Returns the z-position.
Definition: Position.h:65
void sety(double y)
set position y
Definition: Position.h:75
double y() const
Returns the y-position.
Definition: Position.h:60
static StringBijection< TrafficLightType > TrafficLightTypes
traffic light types
T get(const std::string &str) const
static long long int toLong(const std::string &sData)
converts a string into the long value described by it by calling the char-type converter,...
static bool startsWith(const std::string &str, const std::string prefix)
Checks whether a given string starts with the prefix.
static double fn[10]
Definition: odrSpiral.cpp:87