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 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_WEAK | 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  || ((e->getPermissions() & SVC_WEAK) != 0 &&
1003  (e->getPermissions() & ~SVC_WEAK) == 0)
1004  // permit joining small opposite merges
1005  || getDiameter(cluster) < maxDist
1006  || cluster.size() == 2))
1007  || touchingCluster) {
1008  outsideNeighbors.insert(neighbor);
1009  }
1010  } else {
1011  clusterNeighbors.insert(neighbor);
1012  }
1013  }
1014 #ifdef DEBUG_JOINJUNCTIONS
1015  if (gDebugFlag1) std::cout << " check n=" << n->getID()
1016  << " clusterDist=" << clusterDist
1017  << " cd<th=" << (clusterDist <= pedestrianFringeThreshold)
1018  << " touching=" << touchingCluster
1019  << " out=" << joinNamedToString(outsideNeighbors, ',')
1020  << " in=" << joinNamedToString(clusterNeighbors, ',')
1021  << " dia=" << getDiameter(cluster)
1022  << "\n";
1023 #endif
1024  if (clusterNeighbors.size() == 0
1025  || (outsideNeighbors.size() <= 1
1026  && clusterNeighbors.size() == 1
1027  && !n->isTLControlled())) {
1028  cluster.erase(check);
1029  pruneFringe = true; // other nodes could belong to the fringe now
1030 #ifdef DEBUG_JOINJUNCTIONS
1031  if (gDebugFlag1) {
1032  std::cout << " pruned n=" << n->getID() << "\n";
1033  }
1034 #endif
1035  }
1036  }
1037  if (!pruneFringe && !pruneNoisyFringe) {
1038  // run once more and prune more things (with a look at cluster size)
1039  pruneFringe = true;
1040  pruneNoisyFringe = true;
1041 
1042  }
1043  }
1044 }
1045 
1046 double
1048  double result = 0;
1049  for (const NBNode* n1 : cluster) {
1050  for (const NBNode* n2 : cluster) {
1051  result = MAX2(result, n1->getPosition().distanceTo2D(n2->getPosition()));
1052  }
1053  }
1054  return result;
1055 }
1056 
1057 int
1058 NBNodeCont::pruneLongEdges(NodeSet& cluster, double maxDist, const bool dryRun) {
1059  std::set<NBNode*> toRemove;
1060  int maxPassengerLanes = 0;
1061  for (NBNode* n : cluster) {
1062  for (NBEdge* edge : n->getEdges()) {
1063  maxPassengerLanes = MAX2(maxPassengerLanes, edge->getNumLanesThatAllow(SVC_PASSENGER));
1064  }
1065  }
1066  for (NBNode* n : cluster) {
1067  for (NBEdge* edge : n->getOutgoingEdges()) {
1068  // we must track the edge length accross geometry like nodes
1069  // Also, intersecions that are geometry-like
1070  // from the perspective of passenger traffic should be tracked accross
1071  std::vector<NBNode*> passed;
1072  double length = 0;
1073  NBEdge* cur = edge;
1074  NBNode* to = edge->getToNode();
1075  while (cluster.count(to) != 0) {
1076  length += cur->getLoadedLength();
1077  bool goStraight = (std::find(passed.begin(), passed.end(), to) == passed.end()
1078  && (edge->getPermissions() & SVC_PASSENGER) != 0
1079  && to->geometryLike(
1082  passed.push_back(to);
1083  if (goStraight) {
1085  if (cur != nullptr) {
1086  to = cur->getToNode();
1087  } else {
1088  break;
1089  }
1090  } else {
1091  break;
1092  }
1093  }
1094  // allow higher threshold at larger junctions
1095  double longThreshold = maxDist + SUMO_const_laneWidth * MAX2(0, maxPassengerLanes - 1);
1096 #ifdef DEBUG_JOINJUNCTIONS
1097  if (gDebugFlag1) {
1098  std::cout << "check edge length " << edge->getID() << " (" << length << ", passed=" << passed.size() << ", max=" << longThreshold << ")\n";
1099  }
1100 #endif
1101  if (length > longThreshold) {
1102  // we found an edge that should not be removed. Maybe we can
1103  // still keep the start or end in the cluster
1104  // (keep the start if the end can be removed and vice versa)
1105  const bool keepStart = getClusterNeighbors(passed.back(), longThreshold, cluster).size() == 1;
1106  const bool keepEnd = !keepStart && getClusterNeighbors(n, longThreshold, cluster).size() == 1;
1107 #ifdef DEBUG_JOINJUNCTIONS
1108  if (gDebugFlag1) {
1109  std::cout << "node=" << n->getID() << " long edge " << edge->getID() << " (" << length << ", passed=" << toString(passed) << ", max=" << longThreshold << ") keepStart=" << keepStart << " keepEnd=" << keepEnd << "\n";
1110  }
1111 #endif
1112  if (!keepStart) {
1113  toRemove.insert(n);
1114  }
1115  toRemove.insert(passed.begin(), passed.end() - 1);
1116  if (!keepEnd) {
1117  toRemove.insert(passed.back());
1118  }
1119 
1120  }
1121  }
1122  }
1123  if (!dryRun) {
1124  for (std::set<NBNode*>::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
1125  cluster.erase(*j);
1126  }
1127  }
1128  return (int)toRemove.size();
1129 }
1130 
1131 
1132 NodeSet
1133 NBNodeCont::getClusterNeighbors(const NBNode* n, double longThreshold, NodeSet& cluster) {
1134  NodeSet result;
1135  for (NBEdge* e : n->getEdges()) {
1136  if (e->getLength() > longThreshold) {
1137  continue;
1138  }
1139  NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1140  if (cluster.count(neighbor) != 0) {
1141  result.insert(neighbor);
1142  }
1143  }
1144  return result;
1145 }
1146 
1147 
1148 void
1149 NBNodeCont::pruneSlipLaneNodes(NodeSet& cluster, double maxDist) const {
1150 #ifdef DEBUG_JOINJUNCTIONS
1151  if (gDebugFlag1) {
1152  std::cout << "pruning slip-lanes at cluster=" << joinNamedToString(cluster, ' ') << "\n";
1153  }
1154 #endif
1155  // fringe has already been removed
1156  if (cluster.size() <= 2) {
1157  return;
1158  }
1159  NodeSet toRemove;
1160  for (NBNode* n : cluster) {
1161  EdgeVector outgoing;
1162  double inAngle;
1163  // find slip lanes where the start is part of the cluster
1164  if (maybeSlipLaneStart(n, outgoing, inAngle)) {
1165  // potential slip lane start but we don't know which of the outgoing edges it is
1166 #ifdef DEBUG_JOINJUNCTIONS
1167  if (gDebugFlag1) {
1168  std::cout << " candidate slip-lane start=" << n->getID() << " outgoing=" << toString(outgoing) << "\n";
1169  }
1170 #endif
1171  for (NBEdge* contEdge : outgoing) {
1172  if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1173  continue;
1174  }
1175  double slipLength = contEdge->getLength();
1176  NBNode* cont = contEdge->getToNode();
1177  NodeSet cands;
1178  cands.insert(n);
1179  while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1180  if (cands.count(cont) != 0) {
1181  break; // circle, should not happen
1182  }
1183  cands.insert(cont);
1184 #ifdef DEBUG_JOINJUNCTIONS
1185  if (gDebugFlag1) {
1186  std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1187  }
1188 #endif
1189  NBEdge* next = cont->getOutgoingEdges().front();
1190  slipLength += next->getLength();
1191  cont = next->getToNode();
1192  }
1193 #ifdef DEBUG_JOINJUNCTIONS
1194  if (gDebugFlag1) {
1195  std::cout << " candidate slip-lane end=" << cont->getID() << " slipLength=" << slipLength << "\n";
1196  }
1197 #endif
1198  if (cont->getIncomingEdges().size() >= 2 && cont->getOutgoingEdges().size() == 1 &&
1199  // slip lanes are for turning so there needs to be a sufficient angle
1200  abs(NBHelpers::relAngle(inAngle, cont->getOutgoingEdges().front()->getAngleAtNode(cont))) > 45) {
1201  // check whether the other continuation at n is also connected to the sliplane end
1202  const NBEdge* const otherEdge = (contEdge == outgoing.front() ? outgoing.back() : outgoing.front());
1203  NodeSet visited;
1204  visited.insert(n);
1205  std::vector<NodeAndDist> toProc;
1206  toProc.push_back(std::make_pair(otherEdge->getToNode(), otherEdge->getLength()));
1207  bool found = false;
1208  while (!toProc.empty()) {
1209  NodeAndDist nodeAndDist = toProc.back();
1210  NBNode* cont2 = nodeAndDist.first;
1211  double dist = nodeAndDist.second;
1212 #ifdef DEBUG_JOINJUNCTIONS
1213  if (gDebugFlag1) {
1214  std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1215  }
1216 #endif
1217  toProc.pop_back();
1218  if (visited.find(cont2) != visited.end()) {
1219  continue;
1220  }
1221  visited.insert(cont2);
1222  if (cont2 == cont) {
1223  found = true;
1224  break;
1225  }
1226  for (NBEdge* e : cont2->getOutgoingEdges()) {
1227  const double dist2 = dist + e->getLength();
1228  if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1229  toProc.push_back(std::make_pair(e->getToNode(), dist2));
1230  }
1231  }
1232  }
1233  if (found) {
1234  // found slip lane
1235  cands.insert(cont);
1236  toRemove.insert(cands.begin(), cands.end());
1237 #ifdef DEBUG_JOINJUNCTIONS
1238  if (gDebugFlag1) {
1239  std::cout << " found slip-lane with nodes=" << joinNamedToString(cands, ' ') << "\n";
1240  }
1241 #endif
1242  }
1243  }
1244  }
1245  }
1246 
1247  EdgeVector incoming;
1248  double outAngle;
1249  // find slip lanes where the end is part of the cluster
1250  if (maybeSlipLaneEnd(n, incoming, outAngle)) {
1251  // potential slip lane end but we don't know which of the incoming edges it is
1252 #ifdef DEBUG_JOINJUNCTIONS
1253  if (gDebugFlag1) {
1254  std::cout << " candidate slip-lane end=" << n->getID() << " incoming=" << toString(incoming) << "\n";
1255  }
1256 #endif
1257  for (NBEdge* contEdge : incoming) {
1258  if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1259  continue;
1260  }
1261  double slipLength = contEdge->getLength();
1262  NBNode* cont = contEdge->getFromNode();
1263  NodeSet cands;
1264  cands.insert(n);
1265  while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1266  if (cands.count(cont) != 0) {
1267  break; // circle, should not happen
1268  }
1269  cands.insert(cont);
1270 #ifdef DEBUG_JOINJUNCTIONS
1271  if (gDebugFlag1) {
1272  std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1273  }
1274 #endif
1275  NBEdge* next = cont->getIncomingEdges().front();
1276  slipLength += next->getLength();
1277  cont = next->getFromNode();
1278  }
1279 #ifdef DEBUG_JOINJUNCTIONS
1280  if (gDebugFlag1) {
1281  std::cout << " candidate slip-lane start=" << cont->getID() << " slipLength=" << slipLength << "\n";
1282  }
1283 #endif
1284  if (cont->getOutgoingEdges().size() >= 2 && cont->getIncomingEdges().size() == 1 &&
1285  // slip lanes are for turning so there needs to be a sufficient angle
1286  abs(NBHelpers::relAngle(outAngle, cont->getIncomingEdges().front()->getAngleAtNode(cont))) > 45) {
1287  // check whether the other continuation at n is also connected to the sliplane end
1288  const NBEdge* const otherEdge = (contEdge == incoming.front() ? incoming.back() : incoming.front());
1289  NodeSet visited;
1290  visited.insert(n);
1291  std::vector<NodeAndDist> toProc;
1292  toProc.push_back(std::make_pair(otherEdge->getFromNode(), otherEdge->getLength()));
1293  bool found = false;
1294  while (!toProc.empty()) {
1295  NodeAndDist nodeAndDist = toProc.back();
1296  NBNode* cont2 = nodeAndDist.first;
1297  double dist = nodeAndDist.second;
1298 #ifdef DEBUG_JOINJUNCTIONS
1299  if (gDebugFlag1) {
1300  std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1301  }
1302 #endif
1303  toProc.pop_back();
1304  if (visited.find(cont2) != visited.end()) {
1305  continue;
1306  }
1307  visited.insert(cont2);
1308  if (cont2 == cont) {
1309  found = true;
1310  break;
1311  }
1312  for (NBEdge* e : cont2->getIncomingEdges()) {
1313  const double dist2 = dist + e->getLength();
1314  if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1315  toProc.push_back(std::make_pair(e->getFromNode(), dist2));
1316  }
1317  }
1318  }
1319  if (found) {
1320  // found slip lane
1321  cands.insert(cont);
1322  toRemove.insert(cands.begin(), cands.end());
1323 #ifdef DEBUG_JOINJUNCTIONS
1324  if (gDebugFlag1) {
1325  std::cout << " found slip-lane start with nodes=" << joinNamedToString(cands, ' ') << "\n";
1326  }
1327 #endif
1328  }
1329  }
1330  }
1331  }
1332 
1333 
1334 
1335  }
1336  int numRemoved = 0;
1337  for (NBNode* n : toRemove) {
1338  numRemoved += (int)cluster.erase(n);
1339  }
1340  if (numRemoved > 0) {
1341 #ifdef DEBUG_JOINJUNCTIONS
1342  if (gDebugFlag1) {
1343  std::cout << " removed " << numRemoved << " nodes from cluster: " << joinNamedToString(toRemove, ' ') << "\n";
1344  }
1345 #endif
1346  pruneClusterFringe(cluster, maxDist);
1347  }
1348 }
1349 
1350 
1351 bool
1353  return cont->getPassengerEdges(true).size() == 1 && cont->getPassengerEdges(false).size() == 1;
1354 }
1355 
1356 
1357 bool
1358 NBNodeCont::maybeSlipLaneStart(const NBNode* n, EdgeVector& outgoing, double& inAngle) const {
1359  EdgeVector inPE = n->getPassengerEdges(true);
1360  EdgeVector outPE = n->getPassengerEdges(false);
1361  if (inPE.size() == 1 && outPE.size() == 2) {
1362  outgoing.insert(outgoing.begin(), outPE.begin(), outPE.end());
1363  inAngle = inPE.front()->getAngleAtNode(n);
1364  return true;
1365  } else if (inPE.size() >= 2 && outPE.size() == 3) {
1366  // check if the incoming edges are going in opposite directions and then
1367  // use the incoming edge that has 2 almost-straight outgoing edges
1368  const double inRelAngle = fabs(NBHelpers::relAngle(inPE.front()->getAngleAtNode(n), inPE.back()->getAngleAtNode(n)));
1369  //std::cout << "n=" << n->getID() << " inRelAngle=" << inRelAngle << "\n";
1370  if (inRelAngle < 135) {
1371  return false; // not opposite incoming
1372  }
1373  for (NBEdge* in : inPE) {
1374  EdgeVector straight;
1375  int numReverse = 0;
1376  for (NBEdge* out : outPE) {
1377  const double outRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1378  if (outRelAngle <= 45) {
1379  straight.push_back(out);
1380  } else if (outRelAngle >= 135) {
1381  numReverse++;
1382  }
1383  }
1384  if (straight.size() == 2 && numReverse == 1) {
1385  outgoing.insert(outgoing.begin(), straight.begin(), straight.end());
1386  inAngle = in->getAngleAtNode(n);
1387  return true;
1388  }
1389  }
1390  }
1391  return false;
1392 }
1393 
1394 
1395 bool
1396 NBNodeCont::maybeSlipLaneEnd(const NBNode* n, EdgeVector& incoming, double& outAngle) const {
1397  EdgeVector inPE = n->getPassengerEdges(true);
1398  EdgeVector outPE = n->getPassengerEdges(false);
1399  if (inPE.size() == 2 && outPE.size() == 1) {
1400  incoming.insert(incoming.begin(), inPE.begin(), inPE.end());
1401  outAngle = outPE.front()->getAngleAtNode(n);
1402  return true;
1403  } else if (inPE.size() == 3 && outPE.size() >= 2) {
1404  // check if the outgoing edges are going in opposite directions and then
1405  // use the outgoing edge that has 2 almost-straight incoming edges
1406  const double outRelAngle = fabs(NBHelpers::relAngle(outPE.front()->getAngleAtNode(n), outPE.back()->getAngleAtNode(n)));
1407  //std::cout << "n=" << n->getID() << " outRelAngle=" << outRelAngle << "\n";
1408  if (outRelAngle < 135) {
1409  return false; // not opposite outgoing
1410  }
1411  for (NBEdge* out : outPE) {
1412  EdgeVector straight;
1413  int numReverse = 0;
1414  for (NBEdge* in : inPE) {
1415  const double inRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1416  if (inRelAngle <= 45) {
1417  straight.push_back(in);
1418  } else if (inRelAngle >= 135) {
1419  numReverse++;
1420  }
1421  }
1422  if (straight.size() == 2 && numReverse == 1) {
1423  incoming.insert(incoming.begin(), straight.begin(), straight.end());
1424  outAngle = out->getAngleAtNode(n);
1425  return true;
1426  }
1427  }
1428  }
1429  return false;
1430 }
1431 
1432 bool
1433 NBNodeCont::feasibleCluster(const NodeSet& cluster, const std::map<const NBNode*, std::vector<NBNode*> >& ptStopEnds,
1434  double maxDist, std::string& reason) const {
1435  // check for clusters which are to complex and probably won't work very well
1436  // we count the incoming edges of the final junction
1437  std::map<NBEdge*, double, ComparatorIdLess> finalIncomingAngles;
1438  std::map<NBEdge*, double, ComparatorIdLess> finalOutgoingAngles;
1439  for (NBNode* n : cluster) {
1440  for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
1441  NBEdge* edge = *it_edge;
1442  if (cluster.count(edge->getFromNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1443  // incoming edge, does not originate in the cluster
1444  finalIncomingAngles[edge] = edge->getAngleAtNode(edge->getToNode());
1445  }
1446  }
1447  for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
1448  NBEdge* edge = *it_edge;
1449  if (cluster.count(edge->getToNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1450  // outgoing edge, does not end in the cluster
1451  finalOutgoingAngles[edge] = edge->getAngleAtNode(edge->getFromNode());
1452  }
1453  }
1454 
1455  }
1456 #ifdef DEBUG_JOINJUNCTIONS
1457  for (NBNode* n : cluster) {
1458  if (DEBUGCOND(n)) {
1459  std::cout << "feasibleCluster c=" << joinNamedToString(cluster, ',')
1460  << "\n inAngles=" << joinNamedToString(finalIncomingAngles, ' ', ':')
1461  << "\n outAngles=" << joinNamedToString(finalOutgoingAngles, ' ', ':')
1462  << "\n";
1463  }
1464  }
1465 #endif
1466  if (finalIncomingAngles.size() > 5) {
1467  reason = toString(finalIncomingAngles.size()) + " incoming edges";
1468  return false;
1469  }
1470  // check for incoming parallel edges
1471  const double PARALLEL_THRESHOLD_SAME_NODE = 10;
1472  const double PARALLEL_THRESHOLD_DIFF_NODE = 30;
1473  bool foundParallel = false;
1474  for (auto j = finalIncomingAngles.begin(); j != finalIncomingAngles.end() && !foundParallel; ++j) {
1475  auto k = j;
1476  for (++k; k != finalIncomingAngles.end() && !foundParallel; ++k) {
1477  const double angleDiff = fabs(j->second - k->second);
1478  if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1479  // for edge targeting the same node, permit a narrower angle
1480  const double edgeDist = j->first->getLaneShape(0).back().distanceTo2D(k->first->getLaneShape(0).back());
1481 #ifdef DEBUG_JOINJUNCTIONS
1482  if (DEBUGCOND(j->first->getToNode())) {
1483  std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1484  }
1485 #endif
1486  if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1487  (j->first->getToNode() == k->first->getToNode()
1488  || (edgeDist < maxDist)))) {
1489  continue;
1490  }
1491  reason = "parallel incoming " + j->first->getID() + "," + k->first->getID();
1492  return false;
1493  }
1494  }
1495  }
1496  // check for outgoing parallel edges
1497  for (auto j = finalOutgoingAngles.begin(); j != finalOutgoingAngles.end() && !foundParallel; ++j) {
1498  auto k = j;
1499  for (++k; k != finalOutgoingAngles.end() && !foundParallel; ++k) {
1500  const double angleDiff = fabs(j->second - k->second);
1501  if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1502  // for edge leaving the same node, permit a narrower angle
1503  const double edgeDist = j->first->getLaneShape(0).front().distanceTo2D(k->first->getLaneShape(0).front());
1504 #ifdef DEBUG_JOINJUNCTIONS
1505  if (DEBUGCOND(j->first->getFromNode())) {
1506  std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1507  }
1508 #endif
1509  if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1510  (j->first->getFromNode() == k->first->getFromNode()
1511  || (edgeDist < maxDist)))) {
1512  continue;
1513  }
1514  reason = "parallel outgoing " + j->first->getID() + "," + k->first->getID();
1515  return false;
1516  }
1517  }
1518  }
1519  // check for stop edges and tls within the cluster
1520  bool hasTLS = false;
1521  for (NBNode* n : cluster) {
1522  if (n->isTLControlled()) {
1523  hasTLS = true;
1524  }
1525  const auto& stopEnds = ptStopEnds.find(n);
1526  if (stopEnds != ptStopEnds.end()) {
1527  for (NBNode* const to : stopEnds->second) {
1528  if (cluster.count(to) != 0) {
1529  reason = "it contains a pt stop edge";
1530  return false;
1531  }
1532  }
1533  }
1534  }
1535  // prevent removal of long edges unless there is weak circle or a traffic light
1536  if (cluster.size() > 2) {
1537  // find the nodes with the biggests physical distance between them
1538  double maxLength = -1;
1539  NBEdge* maxEdge = nullptr;
1540  for (NBNode* n1 : cluster) {
1541  for (NBNode* n2 : cluster) {
1542  NBEdge* e1 = n1->getConnectionTo(n2);
1543  NBEdge* e2 = n2->getConnectionTo(n1);
1544  if (e1 != nullptr && e1->getLoadedLength() > maxLength) {
1545  maxLength = e1->getLoadedLength();
1546  maxEdge = e1;
1547  }
1548  if (e2 != nullptr && e2->getLoadedLength() > maxLength) {
1549  maxLength = e2->getLoadedLength();
1550  maxEdge = e2;
1551  }
1552  }
1553  }
1554 #ifdef DEBUG_JOINJUNCTIONS
1555  for (NBNode* n : cluster) {
1556  if (DEBUGCOND(n)) {
1557  std::cout << "feasible hasTLS=" << hasTLS << " maxLength=" << maxLength << " maxEdge=" << maxEdge->getID() << "\n";
1558  }
1559  }
1560 #endif
1561  if (!hasTLS && maxLength > 5) {
1562  // find a weak circle within cluster that does not use maxEdge
1563  std::vector<NBNode*> toCheck;
1564  std::set<NBNode*> visited;
1565  toCheck.push_back(maxEdge->getToNode());
1566  bool foundCircle = false;
1567  while (!toCheck.empty()) {
1568  NBNode* n = toCheck.back();
1569  if (n == maxEdge->getFromNode()) {
1570  foundCircle = true;
1571  break;
1572  }
1573  toCheck.pop_back();
1574  visited.insert(n);
1575  for (NBEdge* e : n->getEdges()) {
1576  if (e != maxEdge) {
1577  NBNode* cand = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1578  if (visited.count(cand) == 0 && cluster.count(cand) != 0) {
1579  toCheck.push_back(cand);
1580  }
1581  }
1582  }
1583  }
1584  if (!foundCircle) {
1585  reason = "not compact (maxEdge=" + maxEdge->getID() + " length=" + toString(maxLength) + ")";
1586  return false;
1587  }
1588  }
1589  }
1590  // prevent joining of simple merging/spreading structures
1591  if (!hasTLS && cluster.size() >= 2) {
1592  int entryNodes = 0;
1593  int exitNodes = 0;
1594  int outsideIncoming = 0;
1595  int outsideOutgoing = 0;
1596  int edgesWithin = 0;
1597  for (NBNode* n : cluster) {
1598  bool foundOutsideIncoming = false;
1599  for (NBEdge* e : n->getIncomingEdges()) {
1600  if (cluster.count(e->getFromNode()) == 0) {
1601  // edge entering from outside the cluster
1602  outsideIncoming++;
1603  foundOutsideIncoming = true;
1604  } else {
1605  edgesWithin++;
1606  }
1607  }
1608  if (foundOutsideIncoming) {
1609  entryNodes++;
1610  }
1611  bool foundOutsideOutgoing = false;
1612  for (NBEdge* e : n->getOutgoingEdges()) {
1613  if (cluster.count(e->getToNode()) == 0) {
1614  // edge leaving cluster
1615  outsideOutgoing++;
1616  foundOutsideOutgoing = true;
1617  }
1618  }
1619  if (foundOutsideOutgoing) {
1620  exitNodes++;
1621  }
1622  }
1623  if (entryNodes < 2) {
1624  reason = "only 1 entry node";
1625  return false;
1626  }
1627  if (exitNodes < 2) {
1628  reason = "only 1 exit node";
1629  return false;
1630  }
1631  if (cluster.size() == 2) {
1632  if (edgesWithin == 1 && outsideIncoming < 3 && outsideOutgoing < 3) {
1633  reason = "only 1 edge within and no cross-traffic";
1634  return false;
1635  }
1636  }
1637  }
1638  return true;
1639 }
1640 
1641 
1642 bool
1643 NBNodeCont::reduceToCircle(NodeSet& cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector<NBNode*> cands) const {
1644  //std::cout << "reduceToCircle cs=" << circleSize << " cands=" << toString(cands, ',') << " startNodes=" << joinNamedToString(startNodes, ',') << "\n";
1645  assert(circleSize >= 2);
1646  if ((int)cands.size() == circleSize) {
1647  if (cands.back()->getConnectionTo(cands.front()) != nullptr) {
1648  // cluster found
1649  NodeSet candCluster;
1650  candCluster.insert(cands.begin(), cands.end());
1651  pruneClusterFringe(candCluster, maxDist);
1652  const bool feasible = (int)candCluster.size() == circleSize;
1653  if (feasible) {
1654  cluster.clear();
1655  cluster.insert(cands.begin(), cands.end());
1656  }
1657  return feasible;
1658  } else {
1659  return false;
1660  }
1661  }
1662  if ((int)cluster.size() <= circleSize || startNodes.size() == 0) {
1663  // no reduction possible
1664  //std::cout << " abort\n";
1665  return false;
1666  }
1667  if (cands.size() == 0) {
1668  // try to find a circle starting from another start node
1669  NBEdge* e = shortestEdge(cluster, startNodes, cands);
1670  if (e != nullptr) {
1671  cands.push_back(e->getFromNode());
1672  startNodes.erase(e->getFromNode());
1673  if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands)) {
1674  return true;
1675  } else {
1676  // try another start node
1677  return reduceToCircle(cluster, circleSize, startNodes, maxDist);
1678  }
1679  }
1680  } else {
1681  NodeSet singleStart;
1682  singleStart.insert(cands.back());
1683  NBEdge* e = shortestEdge(cluster, singleStart, cands);
1684  if (e != nullptr) {
1685  std::vector<NBNode*> cands2(cands);
1686  cands2.push_back(e->getToNode());
1687  if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands2)) {
1688  return true;
1689  }
1690  }
1691  }
1692  //std::cout << " abort2\n";
1693  return false;
1694 }
1695 
1696 
1697 NBEdge*
1698 NBNodeCont::shortestEdge(const NodeSet& cluster, const NodeSet& startNodes, const std::vector<NBNode*>& exclude) const {
1699  double minDist = std::numeric_limits<double>::max();
1700  NBEdge* result = nullptr;
1701  for (NBNode* n : startNodes) {
1702  for (NBEdge* e : n->getOutgoingEdges()) {
1703  NBNode* neigh = e->getToNode();
1704  if (cluster.count(neigh) != 0 && std::find(exclude.begin(), exclude.end(), neigh) == exclude.end()) {
1705  const double dist = n->getPosition().distanceTo2D(neigh->getPosition());
1706  //std::cout << " e=" << e->getID() << " dist=" << dist << " minD=" << minDist << "\n";
1707  if (dist < minDist) {
1708  minDist = dist;
1709  result = e;
1710  }
1711  }
1712  }
1713  }
1714  //std::cout << "closestNeighbor startNodes=" << toString(startNodes) << " result=" << Named::getIDSecure(result) << "\n";
1715  return result;
1716 }
1717 
1718 void
1720  NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, bool resetConnections) {
1721  for (NodeSet cluster : clusters) {
1722  joinNodeCluster(cluster, dc, ec, tlc, nullptr, resetConnections);
1723  }
1724 }
1725 
1726 
1727 void
1728 NBNodeCont::joinNodeCluster(NodeSet cluster, NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, NBNode* predefined, bool resetConnections) {
1729  const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1730  assert(cluster.size() > 1);
1731  std::string id = "cluster_";
1732  Position pos;
1733  bool setTL = false;
1736  // collect edges
1737  std::set<NBEdge*, ComparatorIdLess> allEdges;
1738  for (NBNode* n : cluster) {
1739  const EdgeVector& edges = n->getEdges();
1740  allEdges.insert(edges.begin(), edges.end());
1741  }
1742  // determine edges with are incoming or fully inside
1743  std::set<NBEdge*, ComparatorIdLess> clusterIncoming;
1744  std::set<NBEdge*, ComparatorIdLess> inside;
1745  for (NBEdge* e : allEdges) {
1746  if (cluster.count(e->getToNode()) > 0) {
1747  if (cluster.count(e->getFromNode()) > 0) {
1748  inside.insert(e);
1749  if (e->getSignalPosition() != Position::INVALID) {
1750  setTL = true;
1751  nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
1752  }
1753  } else {
1754  clusterIncoming.insert(e);
1755  }
1756  }
1757  }
1758 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1759  std::cout << "joining cluster " << joinNamedToString(cluster, ' ')
1760  << " resetConnections=" << resetConnections << "\n"
1761  << " incoming=" << joinNamedToString(clusterIncoming, ' ') << "\n"
1762  << " inside=" << joinNamedToString(inside, ' ') << "\n";
1763 #endif
1764  analyzeCluster(cluster, id, pos, setTL, type, nodeType);
1765  NBNode* newNode = nullptr;
1766  if (predefined != nullptr) {
1767  newNode = predefined;
1768  } else {
1769  if (!insert(id, pos)) {
1770  // should not fail
1771  WRITE_WARNINGF(TL("Could not join junctions %."), id);
1772  return;
1773  }
1774  newNode = retrieve(id);
1775  }
1776  std::string tlID = id;
1777  if (predefined != nullptr) {
1778  if (predefined->getType() != SumoXMLNodeType::UNKNOWN) {
1779  nodeType = predefined->getType();
1780  }
1781  Position ppos = predefined->getPosition();
1782  if (ppos.x() != Position::INVALID.x()) {
1783  pos.setx(ppos.x());
1784  }
1785  if (ppos.y() != Position::INVALID.y()) {
1786  pos.sety(ppos.y());
1787  }
1788  if (ppos.z() != Position::INVALID.z()) {
1789  pos.setz(ppos.z());
1790  }
1791  }
1792  newNode->reinit(pos, nodeType);
1793  if (origNames) {
1794  newNode->setParameter(SUMO_PARAM_ORIGID, joinNamedToString(cluster, ' '));
1795  }
1796  if (setTL && !newNode->isTLControlled()) {
1797  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(tlID, newNode, 0, type);
1798  if (!tlc.insert(tlDef)) {
1799  // actually, nothing should fail here
1800  delete tlDef;
1801  throw ProcessError(TLF("Could not allocate tls '%'.", id));
1802  }
1803  }
1804 
1805  // determine possible connectivity from outside edges
1806  std::map<NBEdge*, EdgeSet> reachable;
1807  std::map<std::pair<NBEdge*, NBEdge*>, SVCPermissions> conPermissions;
1808  EdgeSet specialPermissions;
1809  for (NBEdge* const e : clusterIncoming) {
1810  EdgeVector open;
1811  EdgeSet seen;
1812  open.push_back(e);
1813  while (open.size() > 0) {
1814  NBEdge* const cur = open.back();
1815  const SVCPermissions pCur = conPermissions.count({e, cur}) == 0 ? cur->getPermissions() : conPermissions[ {e, cur}];
1816 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1817  std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1818  std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1819 #endif
1820  seen.insert(cur);
1821  open.pop_back();
1822  if (cluster.count(cur->getToNode()) == 0) {
1823  //std::cout << " continue\n";
1824  continue;
1825  }
1826  const auto& cons = cur->getConnections();
1827  if (cons.size() == 0 || ec.hasPostProcessConnection(cur->getID()) || cur->getStep() == NBEdge::EdgeBuildingStep::INIT) {
1828  // check permissions to determine reachability
1829  for (NBEdge* out : cur->getToNode()->getOutgoingEdges()) {
1830  if (allEdges.count(out) != 0) {
1831  const SVCPermissions p = pCur & out->getPermissions();
1832  if (seen.count(out) == 0 || (~conPermissions[ {e, out}] & p) != 0) {
1833  if ((p & ~SVC_PEDESTRIAN) != 0) {
1834  open.push_back(out);
1835  conPermissions[ {e, out}] |= p;
1836 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1837  std::cout << " e=" << e->getID() << " out=" << out->getID() << " pOut=" << getVehicleClassNames(out->getPermissions()) << "\n p=" << getVehicleClassNames(p) << "\n q=" << getVehicleClassNames(conPermissions[ {e, out}]) << "\n";
1838 #endif
1839  }
1840  }
1841  }
1842  }
1843  } else {
1844  // check existing connections
1845  for (const auto& con : cons) {
1846  if (con.toEdge != nullptr && allEdges.count(con.toEdge) != 0) {
1847  SVCPermissions p = pCur & con.toEdge->getPermissions();
1848  if (con.permissions != SVC_UNSPECIFIED) {
1849  p &= con.permissions;
1850  }
1851  if (seen.count(con.toEdge) == 0 || (~conPermissions[ {e, con.toEdge}] & p) != 0) {
1852  open.push_back(con.toEdge);
1853  conPermissions[ {e, con.toEdge}] |= p;
1854  //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";
1855  }
1856  }
1857  }
1858  }
1859  }
1860  seen.erase(e);
1861  for (NBEdge* reached : seen) {
1862  // filter out inside edges from reached
1863  if (inside.count(reached) == 0) {
1864  if (e->getStep() > NBEdge::EdgeBuildingStep::INIT && reached->getFromNode() == e->getToNode() && !e->isConnectedTo(reached)) {
1865  // also filter out edges that are outgoing of the to-node of edge but aren't currently connected
1866  continue;
1867  }
1868  reachable[e].insert(reached);
1869  const SVCPermissions pDefault = e->getPermissions() & reached->getPermissions();
1870  if (conPermissions[ {e, reached}] != pDefault) {
1871  specialPermissions.insert(e);
1872 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1873  std::cout << "e=" << e->getID() << " out=" << reached->getID() << " special=" << getVehicleClassNames(conPermissions[ {e, reached}]) << "\n";
1874 #endif
1875  }
1876  }
1877  }
1878 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1879  std::cout << " reachable e=" << e->getID() << " seen=" << toString(seen) << " reachable=" << toString(reachable[e]) << "\n";
1880 #endif
1881  }
1882 
1883  // remap and remove edges which are completely within the new intersection
1884  if (origNames) {
1885  newNode->setParameter("origEdgeIds", joinNamedToString(inside, ' '));
1886  }
1887  for (NBEdge* e : inside) {
1888  for (NBEdge* e2 : allEdges) {
1889  if (e != e2) {
1890  e2->replaceInConnections(e, e->getConnections());
1891  }
1892  }
1893  ec.extract(dc, e, true);
1894  allEdges.erase(e);
1895  }
1896 
1897  // remap edges which are incoming / outgoing
1898  for (NBEdge* e : allEdges) {
1899  const bool outgoing = cluster.count(e->getFromNode()) > 0;
1900  NBNode* from = outgoing ? newNode : e->getFromNode();
1901  NBNode* to = outgoing ? e->getToNode() : newNode;
1902  if (origNames) {
1903  if (outgoing) {
1904  e->setParameter("origFrom", e->getFromNode()->getID());
1905  } else {
1906  e->setParameter("origTo", e->getToNode()->getID());
1907  }
1908  }
1909  if (e->getTurnSignTarget() != "") {
1910  for (NBNode* n : cluster) {
1911  if (e->getTurnSignTarget() == n->getID()) {
1912  e->setTurnSignTarget(to->getID());
1913  break;
1914  }
1915  }
1916  }
1917  e->reinitNodes(from, to);
1918  // re-add connections which previously existed and may still valid.
1919  // connections to removed edges will be ignored
1920  std::vector<NBEdge::Connection> conns = e->getConnections();
1921  for (std::vector<NBEdge::Connection>::iterator k = conns.begin(); k != conns.end(); ++k) {
1922  if ((*k).toEdge == nullptr) {
1923  // edge explicitly set to have no connections
1924  continue;
1925  }
1926  e->addLane2LaneConnection((*k).fromLane, (*k).toEdge, (*k).toLane, NBEdge::Lane2LaneInfoType::USER, false, (*k).mayDefinitelyPass);
1927  if ((*k).fromLane >= 0 && (*k).fromLane < e->getNumLanes() && e->getLaneStruct((*k).fromLane).connectionsDone) {
1928  // @note (see NIImporter_DlrNavteq::ConnectedLanesHandler)
1929  e->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::INIT);
1930 #ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1931  std::cout << " e=" << e->getID() << " declareConnectionsAsLoaded\n";
1932 #endif
1933  }
1934  }
1935  }
1936  if (!resetConnections) {
1937  // disable connections that were impossible with the old topology
1938  // if connectivity has special permissions, set edge to edge connections explicitly
1939  for (NBEdge* in : newNode->getIncomingEdges()) {
1940  for (NBEdge* out : newNode->getOutgoingEdges()) {
1941  if (reachable[in].count(out) == 0) {
1942  if (!ec.hasPostProcessConnection(in->getID(), out->getID())) {
1943  //std::cout << " removeUnreachable in=" << in->getID() << " out=" << out->getID() << "\n";
1944  in->removeFromConnections(out, -1, -1, true, false, true);
1945  } else {
1946  //std::cout << " hasPostProcessConnection in=" << in->getID() << " out=" << out->getID() << "\n";
1947  }
1948  } else if (specialPermissions.count(in) != 0) {
1949  SVCPermissions pDefault = in->getPermissions() & out->getPermissions();
1950  SVCPermissions p = conPermissions[ {in, out}] == 0 ? pDefault : conPermissions[ {in, out}];
1951  in->addEdge2EdgeConnection(out, true, p == pDefault ? SVC_UNSPECIFIED : p);
1952  //std::cout << " addEdge2Edge in=" << in->getID() << " out=" << out->getID() << "\n";
1953  }
1954  }
1955  }
1956  } else {
1957  for (NBEdge* in : newNode->getIncomingEdges()) {
1958  in->invalidateConnections(true);
1959  }
1960  }
1961 
1962  // remove original nodes
1963  registerJoinedCluster(cluster);
1964  for (NBNode* n : cluster) {
1965  erase(n);
1966  }
1967 }
1968 
1969 
1970 void
1972  std::set<std::string> ids;
1973  for (NBNode* n : cluster) {
1974  ids.insert(n->getID());
1975  }
1976  myJoinedClusters.push_back(ids);
1977 }
1978 
1979 void
1980 NBNodeCont::registerJoinedCluster(const std::set<std::string>& cluster) {
1981  myJoinedClusters.push_back(cluster);
1982 }
1983 
1984 void
1985 NBNodeCont::unregisterJoinedCluster(const std::set<std::string>& cluster) {
1986  auto it = std::find(myJoinedClusters.begin(), myJoinedClusters.end(), cluster);
1987  if (it != myJoinedClusters.end()) {
1988  myJoinedClusters.erase(it);
1989  }
1990 }
1991 
1992 
1993 void
1994 NBNodeCont::analyzeCluster(NodeSet cluster, std::string& id, Position& pos,
1995  bool& hasTLS, TrafficLightType& type, SumoXMLNodeType& nodeType) {
1996  id = createClusterId(cluster, id);
1997  bool ambiguousType = false;
1998  for (NBNode* j : cluster) {
1999  pos.add(j->getPosition());
2000  // add a traffic light if any of the cluster members was controlled
2001  if (j->isTLControlled()) {
2002  if (!hasTLS) {
2003  // init type
2004  type = (*j->getControllingTLS().begin())->getType();
2005  } else if (type != (*j->getControllingTLS().begin())->getType()) {
2006  ambiguousType = true;
2007  }
2008  hasTLS = true;
2009  }
2010  SumoXMLNodeType otherType = j->getType();
2011  if (nodeType == SumoXMLNodeType::UNKNOWN) {
2012  nodeType = otherType;
2013  } else if (nodeType != otherType) {
2014  if (hasTLS) {
2015  nodeType = SumoXMLNodeType::TRAFFIC_LIGHT;
2016  } else if (otherType != SumoXMLNodeType::UNKNOWN) {
2017  if ((nodeType != SumoXMLNodeType::PRIORITY && (nodeType != SumoXMLNodeType::NOJUNCTION || otherType != SumoXMLNodeType::PRIORITY))
2018  || (otherType != SumoXMLNodeType::NOJUNCTION && otherType != SumoXMLNodeType::UNKNOWN && otherType != SumoXMLNodeType::PRIORITY)) {
2019  WRITE_WARNINGF("Ambiguous node type for node cluster '%' (%,%), setting to '" + toString(SumoXMLNodeType::PRIORITY) + "'.", id, toString(nodeType), toString(otherType));
2020  }
2021  nodeType = SumoXMLNodeType::PRIORITY;
2022  }
2023  }
2024  }
2025  pos.mul(1. / (double)cluster.size());
2026  if (ambiguousType) {
2027  type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
2028  WRITE_WARNINGF(TL("Ambiguous traffic light type for node cluster '%', setting to '%'."), id, toString(type));
2029  }
2030 }
2031 
2032 
2033 // ----------- (Helper) methods for guessing/computing traffic lights
2034 bool
2035 NBNodeCont::shouldBeTLSControlled(const NodeSet& c, double laneSpeedThreshold, bool recheck) const {
2036  bool tooFast = false;
2037  double laneSpeedSum = 0;
2038  std::set<NBEdge*> seen;
2039  for (NBNode* j : c) {
2040  for (const NBEdge* e : j->getEdges()) {
2041  if (c.find(e->getFromNode()) != c.end() && c.find(e->getToNode()) != c.end()) {
2042  // edges fully within the cluster do not count
2043  continue;
2044  }
2045  if (j->hasIncoming(e)) {
2046  if (recheck && !j->hasConflict(e)) {
2047  // edges without conflict do not count
2048  // we can only check this after connections have been computed
2049  continue;
2050  }
2051  laneSpeedSum += (double)e->getNumLanes() * e->getLaneSpeed(0);
2052  }
2053  if (e->getLaneSpeed(0) * 3.6 > 79) {
2054  tooFast = true;
2055  }
2056  }
2057  }
2058  //std::cout << " c=" << joinNamedToString(c, ' ') << " f=" << f << " size=" << c.size() << " thresh=" << laneSpeedThreshold << " tooFast=" << tooFast << "\n";
2059  return !tooFast && laneSpeedSum >= laneSpeedThreshold && c.size() != 0;
2060 }
2061 
2062 bool
2064  // check whether all component nodes are solely pedestrian crossings
2065  // (these work fine without joining)
2066  for (NBNode* node : c) {
2067  EdgeVector nonPedIncoming;
2068  EdgeVector nonPedOutgoing;
2069  for (NBEdge* e : node->getIncomingEdges()) {
2070  if (e->getPermissions() != SVC_PEDESTRIAN) {
2071  nonPedIncoming.push_back(e);
2072  }
2073  }
2074  for (NBEdge* e : node->getOutgoingEdges()) {
2075  if (e->getPermissions() != SVC_PEDESTRIAN) {
2076  nonPedOutgoing.push_back(e);
2077  }
2078  }
2079  if (!node->geometryLike(nonPedIncoming, nonPedOutgoing)) {
2080  //for (NBNode* node : c) {
2081  // if (node->getID() == "2480337678") {
2082  // std::cout << " node=" << node->getID() << " nonPedIncoming=" << toString(nonPedIncoming) << " nonPedOutgoing=" << toString(nonPedOutgoing) << "\n";
2083  // }
2084  //}
2085  return false;
2086  }
2087  }
2088  return true;
2089 }
2090 
2091 
2092 bool
2094  for (NBNode* node : c) {
2095  if (node->isTLControlled()) {
2096  NBTrafficLightDefinition* tl = (*node->getControllingTLS().begin());
2097  if (tl->getNodes().size() > 1) {
2098  // joined tls also imply a customID
2099  return true;
2100  }
2101  const std::string tlID = tl->getID();
2102  if (tlID != node->getID()
2103  && !StringUtils::startsWith(tlID, "joinedS_")
2104  && !StringUtils::startsWith(tlID, "joinedG_")
2105  && !StringUtils::startsWith(tlID, "GS")) {
2106  return true;
2107  }
2108  }
2109  }
2110  return false;
2111 }
2112 
2113 
2114 void
2116  myGuessedTLS.clear();
2117  // build list of definitely not tls-controlled junctions
2118  const double laneSpeedThreshold = oc.getFloat("tls.guess.threshold");
2119  if (oc.isSet("tls.unset")) {
2120  std::vector<std::string> notTLControlledNodes = oc.getStringVector("tls.unset");
2121  for (std::vector<std::string>::const_iterator i = notTLControlledNodes.begin(); i != notTLControlledNodes.end(); ++i) {
2122  NBNode* n = NBNodeCont::retrieve(*i);
2123  if (n == nullptr) {
2124  throw ProcessError(TLF(" The junction '%' to set as not-controlled is not known.", *i));
2125  }
2126  std::set<NBTrafficLightDefinition*> tls = n->getControllingTLS();
2127  for (std::set<NBTrafficLightDefinition*>::const_iterator j = tls.begin(); j != tls.end(); ++j) {
2128  (*j)->removeNode(n);
2129  }
2130  n->removeTrafficLights();
2131  myUnsetTLS.insert(n);
2132  }
2133  }
2134 
2136  // loop#1 checking whether the node shall be tls controlled,
2137  // because it is assigned to a district
2138  if (oc.exists("tls.taz-nodes") && oc.getBool("tls.taz-nodes")) {
2139  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2140  NBNode* cur = (*i).second;
2141  if (cur->isNearDistrict() && myUnsetTLS.count(cur) == 0) {
2142  setAsTLControlled(cur, tlc, type);
2143  }
2144  }
2145  }
2146 
2147  // figure out which nodes mark the locations of TLS signals
2148  // This assumes nodes are already joined
2149  if (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals")) {
2150  // prepare candidate edges
2151  const double signalDist = oc.getFloat("tls.guess-signals.dist");
2152  for (const auto& item : myNodes) {
2153  const NBNode* node = item.second;
2154  if (node->isTLControlled() && (node->getIncomingEdges().size() == 1 || node->geometryLike())) {
2155 #ifdef DEBUG_GUESSSIGNALS
2156  if (DEBUGCOND(node) || true) {
2157  std::cout << " propagate TLS from " << node->getID() << " downstream\n";
2158  }
2159 #endif
2160  for (NBEdge* edge : node->getOutgoingEdges()) {
2161  // do not overwrite closer signals
2162  if (edge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET) {
2163  edge->setSignalPosition(node->getPosition(), node);
2164  }
2165  }
2166  }
2167  }
2168  std::set<NBEdge*> seen;
2169  std::set<NBEdge*> check;
2170  for (const auto& item : myNodes) {
2171  for (NBEdge* edge : item.second->getOutgoingEdges()) {
2172  if (edge->getSignalPosition() != Position::INVALID) {
2173  check.insert(edge);
2174  seen.insert(edge);
2175 #ifdef DEBUG_GUESSSIGNALS
2176  if (DEBUGCOND(edge->getSignalNode()) || true) {
2177  std::cout << " primary signalPosition edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2178  }
2179 #endif
2180  }
2181  }
2182  }
2183  // propagate signal position until the next real intersection
2184  while (check.size() > 0) {
2185  NBEdge* const edge = *check.begin();
2186  check.erase(check.begin());
2187  seen.insert(edge);
2188  NBNode* const nextNode = edge->getToNode();
2189  if (nextNode->geometryLike() && !nextNode->isTLControlled()) {
2190  for (NBEdge* const outEdge : nextNode->getOutgoingEdges()) {
2191  if (seen.count(outEdge) == 0) {
2192  outEdge->setSignalPosition(edge->getSignalPosition(), edge->getSignalNode());
2193 #ifdef DEBUG_GUESSSIGNALS
2194  if (DEBUGCOND(edge->getSignalNode()) || true) {
2195  std::cout << " setSignalPosition edge=" << outEdge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2196  }
2197 #endif
2198  check.insert(outEdge);
2199  }
2200  }
2201  }
2202  }
2203 
2204  // check which nodes should be controlled
2205  const int slack = oc.getInt("tls.guess-signals.slack");
2206  for (std::map<std::string, NBNode*>::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2207  NBNode* node = i->second;
2208  if (myUnsetTLS.count(node) != 0) {
2209  continue;
2210  }
2211  const EdgeVector& incoming = node->getIncomingEdges();
2212  const EdgeVector& outgoing = node->getOutgoingEdges();
2213  if (!node->isTLControlled() && incoming.size() > 1 && !node->geometryLike()
2215  && node->getType() != SumoXMLNodeType::RAIL_CROSSING) {
2216  std::vector<const NBNode*> signals;
2217  int foundSignals = 0;
2218  int missingSignals = 0;
2219  // check if there is a signal at every incoming edge
2220  for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2221  const NBEdge* inEdge = *it_i;
2223  if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2224 #ifdef DEBUG_GUESSSIGNALS
2225  if (DEBUGCOND(node)) {
2226  std::cout << " noTLS, edge=" << inEdge->getID() << "\n";
2227  }
2228 #endif
2229  missingSignals++;
2230  if (missingSignals > slack) {
2231  break;
2232  }
2233  }
2234  } else {
2235  foundSignals++;
2236  }
2237  }
2238  missingSignals = 0;
2239  int foundSignalsAtDist = 0;
2240  if (foundSignals > 1 && missingSignals <= slack && missingSignals < foundSignals) {
2241  node->updateSurroundingGeometry();
2242  // check if all signals are within the required distance
2243  // (requires detailed geometry computation)
2244  for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2245  const NBEdge* inEdge = *it_i;
2246  if (inEdge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET || inEdge->getSignalOffset() > signalDist) {
2247  if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2248 #ifdef DEBUG_GUESSSIGNALS
2249  if (DEBUGCOND(node)) {
2250  std::cout << " noTLS, edge=" << inEdge->getID() << " offset=" << inEdge->getSignalOffset() << " tlsPos=" << inEdge->getSignalPosition() << "\n";
2251  }
2252 #endif
2253  missingSignals++;
2254  if (missingSignals > slack) {
2255  break;
2256  }
2257  }
2258  } else {
2259  foundSignalsAtDist++;
2260  }
2261  const NBNode* signal = inEdge->getSignalNode();
2262  if (signal != nullptr) {
2263  signals.push_back(signal);
2264  }
2265  }
2266  // outgoing edges may be tagged with pedestrian crossings. These
2267  // should also be merged into the main TLS
2268  for (const NBEdge* outEdge : outgoing) {
2269  NBNode* cand = outEdge->getToNode();
2270  if (cand->isTLControlled() && cand->geometryLike() && outEdge->getLength() <= signalDist) {
2271 #ifdef DEBUG_GUESSSIGNALS
2272  if (DEBUGCOND(node)) {
2273  std::cout << " node=" << node->getID() << " outEdge=" << outEdge->getID() << " signalNode=" << cand->getID() << " len=" << outEdge->getLength() << "\n";
2274  }
2275 #endif
2276  signals.push_back(cand);
2277  }
2278  }
2279  }
2280  if (foundSignalsAtDist > 1 && missingSignals <= slack && missingSignals < foundSignalsAtDist) {
2281  for (const NBNode* s : signals) {
2282  std::set<NBTrafficLightDefinition*> tls = s->getControllingTLS();
2283  const_cast<NBNode*>(s)->reinit(s->getPosition(), SumoXMLNodeType::PRIORITY);
2284  for (std::set<NBTrafficLightDefinition*>::iterator k = tls.begin(); k != tls.end(); ++k) {
2285  tlc.removeFully(s->getID());
2286  }
2287  }
2288  //if (true) std::cout << " node=" << node->getID() << " signals=" << toString(signals) << "\n";
2289  NBTrafficLightDefinition* tlDef = new NBOwnTLDef("GS_" + node->getID(), node, 0, type);
2290  // @todo patch endOffset for all incoming lanes according to the signal positions
2291  if (!tlc.insert(tlDef)) {
2292  // actually, nothing should fail here
2293  WRITE_WARNINGF(TL("Could not build joined tls '%'."), node->getID());
2294  delete tlDef;
2295  return;
2296  }
2297  }
2298  }
2299  }
2300  }
2301 
2302  // guess joined tls first, if wished
2303  if (oc.getBool("tls.guess.joining")) {
2304  // get node clusters
2305  NodeClusters cands;
2306  generateNodeClusters(oc.getFloat("tls.join-dist"), cands);
2307  // check these candidates (clusters) whether they should be controlled by a tls
2308  for (NodeClusters::iterator i = cands.begin(); i != cands.end();) {
2309  NodeSet& c = (*i);
2310  // regard only junctions which are not yet controlled and are not
2311  // forbidden to be controlled
2312  for (NodeSet::iterator j = c.begin(); j != c.end();) {
2313  if ((*j)->isTLControlled() || myUnsetTLS.count(*j) != 0) {
2314  c.erase(j++);
2315  } else {
2316  ++j;
2317  }
2318  }
2319  // check whether the cluster should be controlled
2320  // to avoid gigantic clusters, assume that at most 4 nodes should be needed for a guessed-joined-tls
2321  if (c.size() == 0 || !shouldBeTLSControlled(c, laneSpeedThreshold * (double)c.size() / MIN2((double)c.size(), 4.))) {
2322  i = cands.erase(i);
2323  } else {
2324  ++i;
2325  }
2326  }
2327  // cands now only contain sets of junctions that shall be joined into being tls-controlled
2328  for (auto nodeSet : cands) {
2329  std::vector<NBNode*> nodes;
2330  for (NBNode* node : nodeSet) {
2331  nodes.push_back(node);
2332  myGuessedTLS.insert(node);
2333  }
2334  const std::string& id = createClusterId(nodeSet, "joinedG_");
2335  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2336  if (!tlc.insert(tlDef)) {
2337  // actually, nothing should fail here
2338  WRITE_WARNING(TL("Could not build guessed, joined tls."));
2339  delete tlDef;
2340  return;
2341  }
2342  }
2343  }
2344 
2345  // guess single tls
2346  if (oc.getBool("tls.guess")) {
2347  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2348  NBNode* cur = (*i).second;
2349  // do nothing if already is tl-controlled
2350  if (cur->isTLControlled()) {
2351  continue;
2352  }
2353  // do nothing if in the list of explicit non-controlled junctions
2354  if (myUnsetTLS.count(cur) != 0) {
2355  continue;
2356  }
2357  NodeSet c;
2358  c.insert(cur);
2359  if (!shouldBeTLSControlled(c, laneSpeedThreshold) || cur->geometryLike()) {
2360  continue;
2361  }
2362  setAsTLControlled(cur, tlc, type);
2363  myGuessedTLS.insert(cur);
2364  }
2365  }
2366 }
2367 
2368 
2369 void
2371  std::set<NBTrafficLightDefinition*> recompute;
2372  for (NBNode* node : myGuessedTLS) {
2373  if (!node->hasConflict() || !recheckTLSThreshold(node)) {
2374  const std::set<NBTrafficLightDefinition*>& tlDefs = node->getControllingTLS();
2375  recompute.insert(tlDefs.begin(), tlDefs.end());
2376  node->removeTrafficLights(true);
2377  for (NBEdge* edge : node->getIncomingEdges()) {
2378  edge->clearControllingTLInformation();
2379  }
2380  }
2381  }
2382  for (NBTrafficLightDefinition* def : recompute) {
2383  if (def->getNodes().size() == 0) {
2384  tlc.removeFully(def->getID());
2385  } else {
2386  def->setParticipantsInformation();
2387  def->setTLControllingInformation();
2389  }
2390  }
2391 }
2392 
2393 
2394 bool
2396  if (!node->isTLControlled()) {
2397  return false;
2398  }
2399  if ((*node->getControllingTLS().begin())->getNodes().size() != 1) {
2400  // unable to perform check for a joined tls
2401  return true;
2402  }
2403  NodeSet c;
2404  c.insert(node);
2405  const double laneSpeedThreshold = OptionsCont::getOptions().getFloat("tls.guess.threshold");
2406  return shouldBeTLSControlled(c, laneSpeedThreshold, true);
2407 }
2408 
2409 
2410 void
2412  for (const auto& item : myNodes) {
2413  item.second->computeKeepClear();
2414  }
2415 }
2416 
2417 
2418 void
2420  const std::vector<std::string> excludeList = OptionsCont::getOptions().getStringVector("tls.join-exclude");
2421  for (const std::string& tlsID : excludeList) {
2422  if (!tlc.exist(tlsID, false)) {
2423  WRITE_WARNINGF("Unknown tls ID '%' in option tls.join-exclude", tlsID);
2424  }
2425  }
2426  std::set<std::string> exclude(excludeList.begin(), excludeList.end());
2427  NodeClusters cands;
2428  generateNodeClusters(maxdist, cands);
2429  for (NodeSet& c : cands) {
2430  for (NodeSet::iterator j = c.begin(); j != c.end();) {
2431  if (!(*j)->isTLControlled() || exclude.count((*(*j)->getControllingTLS().begin())->getID()) != 0) {
2432  c.erase(j++);
2433  } else {
2434  ++j;
2435  }
2436  }
2437  if (c.size() < 2 || onlyCrossings(c) || customTLID(c)) {
2438  continue;
2439  }
2440  // figure out type of the joined TLS
2441  Position dummyPos;
2442  bool dummySetTL = false;
2443  std::string id = "joinedS_"; // prefix (see #3871)
2444  TrafficLightType type;
2446  analyzeCluster(c, id, dummyPos, dummySetTL, type, nodeType);
2447  for (NBNode* j : c) {
2448  std::set<NBTrafficLightDefinition*> tls = j->getControllingTLS();
2449  j->removeTrafficLights();
2450  for (NBTrafficLightDefinition* k : tls) {
2451  tlc.removeFully(k->getID());
2452  }
2453  }
2454  std::vector<NBNode*> nodes;
2455  for (NBNode* j : c) {
2456  nodes.push_back(j);
2457  }
2458  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2459  if (!tlc.insert(tlDef)) {
2460  // actually, nothing should fail here
2461  WRITE_WARNING(TL("Could not build a joined tls."));
2462  delete tlDef;
2463  return;
2464  }
2465  }
2466 }
2467 
2468 
2469 void
2471  TrafficLightType type, std::string id) {
2472  if (id == "") {
2473  id = node->getID();
2474  }
2475  NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, node, 0, type);
2476  if (!tlc.insert(tlDef)) {
2477  // actually, nothing should fail here
2478  WRITE_WARNINGF(TL("Building a tl-logic for junction '%' twice is not possible."), id);
2479  delete tlDef;
2480  return;
2481  }
2482 }
2483 
2484 
2485 // -----------
2486 void
2488  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2489  (*i).second->computeLanes2Lanes();
2490  }
2491 }
2492 
2493 
2494 // computes the "wheel" of incoming and outgoing edges for every node
2495 void
2497  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2498  (*i).second->computeLogic(ec);
2499  }
2500 }
2501 
2502 
2503 void
2505  std::set<NBNode*> roundaboutNodes;
2506  const bool checkLaneFoesAll = oc.getBool("check-lane-foes.all");
2507  const bool checkLaneFoesRoundabout = !checkLaneFoesAll && oc.getBool("check-lane-foes.roundabout");
2508  if (checkLaneFoesRoundabout) {
2509  const std::set<EdgeSet>& roundabouts = ec.getRoundabouts();
2510  for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
2511  for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
2512  roundaboutNodes.insert((*j)->getToNode());
2513  }
2514  }
2515  }
2516  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2517  const bool checkLaneFoes = checkLaneFoesAll || (checkLaneFoesRoundabout && roundaboutNodes.count((*i).second) > 0);
2518  (*i).second->computeLogic2(checkLaneFoes);
2519  }
2520 }
2521 
2522 
2523 void
2525  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2526  delete ((*i).second);
2527  }
2528  myNodes.clear();
2529  for (auto& item : myExtractedNodes) {
2530  delete item.second;
2531  }
2532  myExtractedNodes.clear();
2533 }
2534 
2535 
2536 std::string
2538  int counter = 0;
2539  std::string freeID = "SUMOGenerated" + toString<int>(counter);
2540  // While there is a node with id equal to freeID
2541  while (retrieve(freeID) != nullptr) {
2542  // update counter and generate a new freeID
2543  counter++;
2544  freeID = "SUMOGenerated" + toString<int>(counter);
2545  }
2546  return freeID;
2547 }
2548 
2549 
2550 void
2551 NBNodeCont::computeNodeShapes(double mismatchThreshold) {
2552  for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2553  (*i).second->computeNodeShape(mismatchThreshold);
2554  }
2555 }
2556 
2557 
2558 void
2560  WRITE_MESSAGE(TL("-----------------------------------------------------"));
2561  WRITE_MESSAGE(TL("Summary:"));
2562 
2563  int numUnregulatedJunctions = 0;
2564  int numDeadEndJunctions = 0;
2565  int numTrafficLightJunctions = 0;
2566  int numPriorityJunctions = 0;
2567  int numRightBeforeLeftJunctions = 0;
2568  int numLeftBeforeRightJunctions = 0;
2569  int numAllWayStopJunctions = 0;
2570  int numZipperJunctions = 0;
2571  int numDistrictJunctions = 0;
2572  int numRailCrossing = 0;
2573  int numRailSignals = 0;
2574  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2575  switch ((*i).second->getType()) {
2577  ++numUnregulatedJunctions;
2578  break;
2580  ++numDeadEndJunctions;
2581  break;
2585  ++numTrafficLightJunctions;
2586  break;
2589  ++numPriorityJunctions;
2590  break;
2592  ++numRightBeforeLeftJunctions;
2593  break;
2595  ++numLeftBeforeRightJunctions;
2596  break;
2598  ++numAllWayStopJunctions;
2599  break;
2601  ++numZipperJunctions;
2602  break;
2604  ++numDistrictJunctions;
2605  break;
2607  ++numRailCrossing;
2608  break;
2610  ++numRailSignals;
2611  break;
2613  // should not happen
2614  break;
2615  default:
2616  break;
2617  }
2618  }
2619  WRITE_MESSAGE(TL(" Node type statistics:"));
2620  WRITE_MESSAGE(" Unregulated junctions : " + toString(numUnregulatedJunctions));
2621  if (numDeadEndJunctions > 0) {
2622  WRITE_MESSAGE(" Dead-end junctions : " + toString(numDeadEndJunctions));
2623  }
2624  WRITE_MESSAGE(" Priority junctions : " + toString(numPriorityJunctions));
2625  WRITE_MESSAGE(" Right-before-left junctions : " + toString(numRightBeforeLeftJunctions));
2626  if (numLeftBeforeRightJunctions > 0) {
2627  WRITE_MESSAGE(" Left-before-right junctions : " + toString(numLeftBeforeRightJunctions));
2628  }
2629  if (numTrafficLightJunctions > 0) {
2630  WRITE_MESSAGE(" Traffic light junctions : " + toString(numTrafficLightJunctions));
2631  }
2632  if (numAllWayStopJunctions > 0) {
2633  WRITE_MESSAGE(" All-way stop junctions : " + toString(numAllWayStopJunctions));
2634  }
2635  if (numZipperJunctions > 0) {
2636  WRITE_MESSAGE(" Zipper-merge junctions : " + toString(numZipperJunctions));
2637  }
2638  if (numRailCrossing > 0) {
2639  WRITE_MESSAGE(" Rail crossing junctions : " + toString(numRailCrossing));
2640  }
2641  if (numRailSignals > 0) {
2642  WRITE_MESSAGE(" Rail signal junctions : " + toString(numRailSignals));
2643  }
2644  if (numDistrictJunctions > 0) {
2645  WRITE_MESSAGE(" District junctions : " + toString(numDistrictJunctions));
2646  }
2647  const GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
2648  WRITE_MESSAGE(TL(" Network boundaries:"));
2649  WRITE_MESSAGE(" Original boundary : " + toString(geoConvHelper.getOrigBoundary()));
2650  WRITE_MESSAGE(" Applied offset : " + toString(geoConvHelper.getOffsetBase()));
2651  WRITE_MESSAGE(" Converted boundary : " + toString(geoConvHelper.getConvBoundary()));
2652  WRITE_MESSAGE(TL("-----------------------------------------------------"));
2653 }
2654 
2655 
2656 std::vector<std::string>
2658  std::vector<std::string> ret;
2659  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2660  ret.push_back((*i).first);
2661  }
2662  return ret;
2663 }
2664 
2665 
2666 void
2667 NBNodeCont::addPrefix(const std::string& prefix) {
2668  // make a copy of node containers
2669  const auto nodeContainerCopy = myNodes;
2670  myNodes.clear();
2671  for (const auto& node : nodeContainerCopy) {
2672  node.second->setID(prefix + node.second->getID());
2673  myNodes[node.second->getID()] = node.second;
2674  }
2675 }
2676 
2677 
2678 void
2679 NBNodeCont::rename(NBNode* node, const std::string& newID) {
2680  if (myNodes.count(newID) != 0) {
2681  throw ProcessError(TLF("Attempt to rename node using existing id '%'", newID));
2682  }
2683  myNodes.erase(node->getID());
2684  node->setID(newID);
2685  myNodes[newID] = node;
2686 }
2687 
2688 
2689 void
2690 NBNodeCont::discardTrafficLights(NBTrafficLightLogicCont& tlc, bool geometryLike, bool guessSignals) {
2691  for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2692  NBNode* node = i->second;
2693  if (node->isTLControlled() && (!geometryLike || node->geometryLike())) {
2694  // make a copy of tldefs
2695  const std::set<NBTrafficLightDefinition*> tldefs = node->getControllingTLS();
2696  if (geometryLike && (*tldefs.begin())->getNodes().size() > 1) {
2697  // do not remove joined tls when only removing geometry-like tls
2698  continue;
2699  }
2700  if (guessSignals && node->isTLControlled() && node->geometryLike()) {
2701  // record signal location
2702  for (NBEdge* edge : node->getOutgoingEdges()) {
2703  edge->setSignalPosition(node->getPosition(), nullptr);
2704 #ifdef DEBUG_GUESSSIGNALS
2705  std::cout << " discard-simple " << node->getID() << " edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2706 #endif
2707  }
2708  }
2709  for (std::set<NBTrafficLightDefinition*>::const_iterator it = tldefs.begin(); it != tldefs.end(); ++it) {
2710  NBTrafficLightDefinition* tlDef = *it;
2711  node->removeTrafficLight(tlDef);
2712  tlc.extract(tlDef);
2713  }
2715  node->reinit(node->getPosition(), newType);
2716  }
2717  }
2718 }
2719 
2720 
2721 void
2723  for (auto& item : myNodes) {
2724  NBNode* node = item.second;
2725  if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
2727  }
2728  }
2729 }
2730 
2731 
2732 int
2733 NBNodeCont::remapIDs(bool numericaIDs, bool reservedIDs, const std::string& prefix, NBTrafficLightLogicCont& tlc) {
2734  bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.node-start");
2735  if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
2736  return 0;
2737  }
2738  std::vector<std::string> avoid;
2739  if (startGiven) {
2740  avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.node-start") - 1));
2741  } else {
2742  avoid = getAllNames();
2743  }
2744  std::set<std::string> reserve;
2745  if (reservedIDs) {
2746  NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "node:", reserve); // backward compatibility
2747  NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "junction:", reserve); // selection format
2748  avoid.insert(avoid.end(), reserve.begin(), reserve.end());
2749  }
2750  IDSupplier idSupplier("", avoid);
2751  NodeSet toChange;
2752  for (NodeCont::iterator it = myNodes.begin(); it != myNodes.end(); it++) {
2753  if (startGiven) {
2754  toChange.insert(it->second);
2755  continue;
2756  }
2757  if (numericaIDs) {
2758  try {
2759  StringUtils::toLong(it->first);
2760  } catch (NumberFormatException&) {
2761  toChange.insert(it->second);
2762  }
2763  }
2764  if (reservedIDs && reserve.count(it->first) > 0) {
2765  toChange.insert(it->second);
2766  }
2767  }
2768  const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
2769  for (NBNode* node : toChange) {
2770  myNodes.erase(node->getID());
2771  }
2772  for (NBNode* node : toChange) {
2773  if (origNames && node->getParameter(SUMO_PARAM_ORIGID) == "") {
2774  node->setParameter(SUMO_PARAM_ORIGID, node->getID());
2775  }
2776  node->setID(idSupplier.getNext());
2777  for (NBTrafficLightDefinition* tlDef : node->getControllingTLS()) {
2778  tlc.rename(tlDef, node->getID());
2779  }
2780  myNodes[node->getID()] = node;
2781  }
2782  if (prefix.empty()) {
2783  return (int)toChange.size();
2784  } else {
2785  int renamed = 0;
2786  // make a copy because we will modify the map
2787  auto oldNodes = myNodes;
2788  for (auto item : oldNodes) {
2789  if (!StringUtils::startsWith(item.first, prefix)) {
2790  rename(item.second, prefix + item.first);
2791  for (NBTrafficLightDefinition* tlDef : item.second->getControllingTLS()) {
2792  if (!StringUtils::startsWith(tlDef->getID(), prefix)) {
2793  tlc.rename(tlDef, prefix + tlDef->getID());
2794  }
2795  }
2796  renamed++;
2797  }
2798  }
2799  return renamed;
2800  }
2801 }
2802 
2803 
2804 int
2806  // guess outer fringe by topology and being on the pareto-boundary
2807  NodeSet topRightFront;
2808  NodeSet topLeftFront;
2809  NodeSet bottomRightFront;
2810  NodeSet bottomLeftFront;
2811  for (const auto& item : myNodes) {
2812  paretoCheck(item.second, topRightFront, 1, 1);
2813  paretoCheck(item.second, topLeftFront, -1, 1);
2814  paretoCheck(item.second, bottomRightFront, 1, -1);
2815  paretoCheck(item.second, bottomLeftFront, -1, -1);
2816  }
2817  NodeSet front;
2818  front.insert(topRightFront.begin(), topRightFront.end());
2819  front.insert(topLeftFront.begin(), topLeftFront.end());
2820  front.insert(bottomRightFront.begin(), bottomRightFront.end());
2821  front.insert(bottomLeftFront.begin(), bottomLeftFront.end());
2822  int numFringe = 0;
2823  for (NBNode* n : front) {
2824  const int in = (int)n->getIncomingEdges().size();
2825  const int out = (int)n->getOutgoingEdges().size();
2826  if ((in <= 1 && out <= 1) &&
2827  (in == 0 || out == 0
2828  || n->getIncomingEdges().front()->isTurningDirectionAt(n->getOutgoingEdges().front()))) {
2829  n->setFringeType(FringeType::OUTER);
2830  numFringe++;
2831  }
2832  }
2833  // guess outer fringe by topology and speed
2834  const double speedThreshold = OptionsCont::getOptions().getFloat("fringe.guess.speed-threshold");
2835  for (const auto& item : myNodes) {
2836  NBNode* n = item.second;
2837  if (front.count(n) != 0) {
2838  continue;
2839  }
2840  if (n->getEdges().size() == 1 && n->getEdges().front()->getSpeed() > speedThreshold) {
2842  numFringe++;
2843  }
2844  }
2845  return numFringe;
2846 }
2847 
2848 
2849 void
2850 NBNodeCont::paretoCheck(NBNode* node, NodeSet& frontier, int xSign, int ySign) {
2851  const double x = node->getPosition().x() * xSign;
2852  const double y = node->getPosition().y() * ySign;
2853  std::vector<NBNode*> dominated;
2854  for (NBNode* fn : frontier) {
2855  const double x2 = fn->getPosition().x() * xSign;
2856  const double y2 = fn->getPosition().y() * ySign;
2857  if (x2 >= x && y2 >= y) {
2858  return;
2859  } else if (x2 <= x && y2 <= y) {
2860  dominated.push_back(fn);
2861  }
2862  }
2863  frontier.insert(node);
2864  for (NBNode* r : dominated) {
2865  frontier.erase(r);
2866  }
2867 }
2868 
2869 
2870 void
2872  for (const auto& item : myNodes) {
2873  NBNode* n = item.second;
2874  if (n->isTLControlled() && n->getRightOfWay() == RightOfWay::DEFAULT) {
2875  bool hasNEMA = false;
2876  for (NBTrafficLightDefinition* tl : n->getControllingTLS()) {
2877  if (tl->getType() == TrafficLightType::NEMA) {
2878  hasNEMA = true;
2879  break;
2880  }
2881  }
2882  if (hasNEMA) {
2883  // NEMA controller defaults to allway_stop behavior when switched off
2885  }
2886  }
2887  }
2888 }
2889 
2890 
2891 bool
2893  bool hadShapes = false;
2894  for (const auto& item : myNodes) {
2895  if (item.second->getShape().size() > 0 && !item.second->hasCustomShape()) {
2896  hadShapes = true;
2897  item.second->resetShape();
2898  }
2899  }
2900  return hadShapes;
2901 }
2902 /****************************************************************************/
#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 isRailway(SVCPermissions permissions)
Returns whether an edge with the given permission is a railway edge.
const SVCPermissions SVC_UNSPECIFIED
permissions not specified
bool isWaterway(SVCPermissions permissions)
Returns whether an edge with the given permission 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_WEAK
@ 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_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:4308
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:4776
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:4787
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:1522
bool isNearEnough2BeJoined2(NBEdge *e, double threshold) const
Check if edge is near enought to be joined to another edge.
Definition: NBEdge.cpp:3952
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:2110
double getSignalOffset() const
Returns the offset of a traffic signal from the end of this edge.
Definition: NBEdge.cpp:4355
const std::vector< Connection > & getConnections() const
Returns the connections.
Definition: NBEdge.h:1035
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:1358
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:1847
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:3989
std::vector< std::pair< NBEdge *, NBEdge * > > getEdgesToJoin() const
get edges to join
Definition: NBNode.cpp:2569
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:1741
EdgeVector getPassengerEdges(bool incoming) const
return edges that permit passengers (either incoming or outgoing)
Definition: NBNode.cpp:2338
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:2486
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:3753
bool isNearDistrict() const
@chech if node is near district
Definition: NBNode.cpp:2634
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:317
double distanceTo2D(const Position &p2) const
returns the euclidean distance in the x-y-plane
Definition: Position.h:271
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