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