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