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