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