Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
NBNodeCont.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License 2.0 which is available at
6// https://www.eclipse.org/legal/epl-2.0/
7// This Source Code may also be made available under the following Secondary
8// Licenses when the conditions for such availability set forth in the Eclipse
9// Public License 2.0 are satisfied: GNU General Public License, version 2
10// or later which is available at
11// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13/****************************************************************************/
24// Container for nodes during the netbuilding process
25/****************************************************************************/
26#include <config.h>
27
28#include <string>
29#include <map>
30#include <algorithm>
31#include <cmath>
33#include <utils/geom/Boundary.h>
46#include "NBHelpers.h"
47#include "NBAlgorithms.h"
48#include "NBDistrict.h"
49#include "NBEdgeCont.h"
51#include "NBOwnTLDef.h"
52#include "NBPTStop.h"
53#include "NBNodeCont.h"
54#include "NBPTStopCont.h"
55#include "NBPTLineCont.h"
56#include "NBParking.h"
57
58// ===========================================================================
59// Algorithm constants
60// ===========================================================================
61#define MAX_SLIPLANE_LENGTH 1000
62
63// ===========================================================================
64// Debug Flags
65// ===========================================================================
66
67//#define DEBUG_JOINJUNCTIONS
68//#define DEBUG_REDUCE
69//#define DEBUG_JOINJUNCTIONS_CONNECTIONS
70//#define DEBUG_GUESSSIGNALS
71#define DEBUGNODEID ""
72#define DEBUGNODEID2 ""
73//#define DEBUGNODEID "5548037023"
74#define DEBUGCOND(obj) ((obj) != 0 && ((obj)->getID() == DEBUGNODEID || (obj)->getID() == DEBUGNODEID2))
75//#define DEBUGCOND(obj) (true)
76
77
78// ===========================================================================
79// method definitions
80// ===========================================================================
84
85
86// ----------- Insertion/removal/retrieval of nodes
87bool
88NBNodeCont::insert(const std::string& id, const Position& position,
89 NBDistrict* district) {
90 NodeCont::iterator i = myNodes.find(id);
91 if (i != myNodes.end()) {
92 return false;
93 }
94 NBNode* node = new NBNode(id, position, district);
95 myNodes[id] = node;
96 const float pos[2] = {(float)position.x(), (float)position.y()};
97 myRTree.Insert(pos, pos, node);
98 return true;
99}
100
101
102bool
104 std::string id = node->getID();
105 NodeCont::iterator i = myNodes.find(id);
106 if (i != myNodes.end()) {
107 return false;
108 }
109 myNodes[id] = node;
110 const float pos[2] = {(float)node->getPosition().x(), (float)node->getPosition().y()};
111 myRTree.Insert(pos, pos, node);
112 return true;
113}
114
115
116NBNode*
117NBNodeCont::retrieve(const std::string& id) const {
118 NodeCont::const_iterator i = myNodes.find(id);
119 if (i == myNodes.end()) {
120 return nullptr;
121 }
122 return (*i).second;
123}
124
125
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::createClusterId(const std::set<std::string>& cluster, const std::string& prefix) {
711 int maxIds = OptionsCont::getOptions().getInt("max-join-ids");
712 if (maxIds <= 0) {
713 maxIds = (int)cluster.size();
714 }
715 if ((int)cluster.size() > maxIds) {
716 auto clusterIt = cluster.begin();
717 std::string result = prefix + *clusterIt;
718 for (int i = 1; i < maxIds; i++) {
719 ++clusterIt;
720 result += "_" + *clusterIt;
721 }
722 return result + "_#" + toString((int)cluster.size() - maxIds) + "more";
723 }
724 return prefix + joinToString(cluster, "_");
725}
726
727
728void
729NBNodeCont::addCluster2Join(const std::set<std::string>& cluster, NBNode* node) {
730 // error handling has to take place here since joins could be loaded from multiple files
731 std::set<std::string> validCluster;
732 for (std::string nodeID : cluster) {
733 if (myJoinExclusions.count(nodeID) > 0) {
734 WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' was already excluded from joining."), nodeID);
735 return;
736 } else if (myJoined.count(nodeID) > 0) {
737 WRITE_WARNINGF(TL("Ignoring join-cluster because junction '%' already occurred in another join-cluster."), nodeID);
738 return;
739 } else {
740 if (retrieve(nodeID) != nullptr) {
741 validCluster.insert(nodeID);
742 } else {
743 WRITE_ERRORF(TL("Unknown junction '%' in join-cluster."), nodeID);
744 }
745 }
746 }
747 if (validCluster.size() > 1) {
748 myJoined.insert(validCluster.begin(), validCluster.end());
749 myClusters2Join.push_back(std::make_pair(validCluster, node));
750 } else {
751 WRITE_WARNINGF(TL("Ignoring join-cluster '%' because it has size '%'."), node->getID(), validCluster.size());
752 }
753}
754
755
756int
758 int numJoined = 0;
759 for (auto& item : myClusters2Join) {
760 // verify loaded cluster
761 NodeSet cluster;
762 for (std::string nodeID : item.first) {
763 NBNode* node = retrieve(nodeID);
764 if (node == nullptr) {
765 WRITE_ERRORF(TL("unknown junction '%' while joining."), nodeID);
766 } else {
767 cluster.insert(node);
768 }
769 }
770 if (cluster.size() > 1) {
771 joinNodeCluster(cluster, dc, ec, tlc, item.second);
772 numJoined++;
773 myJoinExclusions.insert(item.second->getID());
774 }
775 }
776 myClusters2Join.clear(); // make save for recompute
777 return numJoined;
778}
779
780
781int
783#ifdef DEBUG_JOINJUNCTIONS
784 std::cout << "joinJunctions...\n";
785#endif
786 NodeClusters cands;
787 NodeClusters clusters;
788 std::map<const NBNode*, std::vector<NBNode*> > ptStopEnds;
789 // check for stop edges within the cluster
790 for (const auto& stopIt : sc.getStops()) {
791 NBEdge* edge = ec.retrieve(stopIt.second->getEdgeId());
792 if (edge != nullptr) {
793 ptStopEnds[edge->getFromNode()].push_back(edge->getToNode());
794 }
795 }
796 generateNodeClusters(maxDist, cands);
797 for (NodeSet& cluster : cands) {
798#ifdef DEBUG_JOINJUNCTIONS
799 gDebugFlag1 = false;
800 for (NBNode* n : cluster) {
801 if (DEBUGCOND(n)) {
802 gDebugFlag1 = true;
803 }
804 }
805#endif
806 // remove join exclusions
807 for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
808 NodeSet::iterator check = j;
809 ++j;
810 if (myJoinExclusions.count((*check)->getID()) > 0) {
811 cluster.erase(check);
812 }
813 }
814 std::string origCluster = joinNamedToString(cluster, ',');
815 // remove nodes that can be eliminated by geometry.remove
816 pruneClusterFringe(cluster, maxDist);
817 if (cluster.size() < 2) {
818 continue;
819 }
820 // remove nodes that are part of a bypass lane (typically for turning right without waiting at a traffic light)
821 pruneSlipLaneNodes(cluster, maxDist);
822 if (cluster.size() < 2) {
823 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
824 continue;
825 }
826 origCluster = joinNamedToString(cluster, ',');
827 NBNode* tryRemove = nullptr;
828 std::string reason;
829 std::string origReason;
830 // pruneLongEdges might remove too much, so we check first to have a fallback with the circles
831 bool feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason, tryRemove);
832 if (feasible && ((int)cluster.size() - pruneLongEdges(cluster, maxDist, true) < 2)) {
833 origReason = "long edge";
834 feasible = false;
835 }
836 if (!feasible) {
837#ifdef DEBUG_JOINJUNCTIONS
838 if (gDebugFlag1) {
839 std::cout << " try to reduce to 4-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
840 }
841#endif
842 if (reduceToCircle(cluster, 4, cluster, maxDist)) {
843 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason, tryRemove);
844 if (feasible) {
845 WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
846 }
847 }
848 }
849 if (!feasible) {
850#ifdef DEBUG_JOINJUNCTIONS
851 if (gDebugFlag1) {
852 std::cout << " try to reduce to 2-circle nodes=" << joinNamedToString(cluster, ',') << "\n";
853 }
854#endif
855 if (reduceToCircle(cluster, 2, cluster, maxDist)) {
856 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason, tryRemove);
857 if (feasible) {
858 WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
859 }
860 }
861 }
862 while (!feasible && tryRemove != nullptr) {
863 cluster.erase(tryRemove);
864 pruneClusterFringe(cluster, maxDist);
865 tryRemove = nullptr;
866 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, reason, tryRemove);
867 if (feasible) {
868 WRITE_WARNINGF(TL("Reducing junction cluster % (%)."), origCluster, origReason);
869 }
870 }
871 if (cluster.size() < 2) {
872 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "after reduction");
873 continue;
874 }
875 // avoid removal of long edges (must have been added via an alternative path).
876 const int numPruned = pruneLongEdges(cluster, maxDist);
877 if (cluster.size() < 2) {
878 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
879 continue;
880 }
881 // after pruning long edges we have to recheck
882 if (numPruned > 0) {
883 pruneClusterFringe(cluster, maxDist);
884 if (cluster.size() < 2) {
885 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "long edge");
886 continue;
887 }
888 pruneSlipLaneNodes(cluster, maxDist);
889 if (cluster.size() < 2) {
890 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, "slip lane");
891 continue;
892 }
893 }
894 feasible = feasibleCluster(cluster, ptStopEnds, maxDist, origReason, tryRemove);
895 if (!feasible) {
896 WRITE_WARNINGF(TL("Not joining junctions % (%)."), origCluster, origReason);
897 continue;
898 }
899 // compute all connected components of this cluster
900 // (may be more than 1 if intermediate nodes were removed)
901 NodeClusters components;
902 for (NBNode* current : cluster) {
903 // merge all connected components into newComp
904 NodeSet newComp;
905 //std::cout << "checking connectivity for " << current->getID() << "\n";
906 newComp.insert(current);
907 for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end();) {
908 NodeClusters::iterator check = it_comp;
909 //std::cout << " connected with " << toString(*check) << "?\n";
910 bool connected = false;
911 for (NBNode* k : *check) {
912 if (current->getConnectionTo(k) != nullptr || k->getConnectionTo(current) != nullptr) {
913 //std::cout << "joining with connected component " << toString(*check) << "\n";
914 newComp.insert((*check).begin(), (*check).end());
915 it_comp = components.erase(check);
916 connected = true;
917 break;
918 }
919 }
920 if (!connected) {
921 it_comp++;
922 }
923 }
924 //std::cout << "adding new component " << toString(newComp) << "\n";
925 components.push_back(newComp);
926 }
927 for (NodeClusters::iterator it_comp = components.begin(); it_comp != components.end(); ++it_comp) {
928 if ((*it_comp).size() > 1) {
929 //std::cout << "adding cluster " << toString(*it_comp) << "\n";
930 clusters.push_back(*it_comp);
931 }
932 }
933#ifdef DEBUG_JOINJUNCTIONS
934 gDebugFlag1 = false;
935#endif
936 }
937 joinNodeClusters(clusters, dc, ec, tlc);
938 return (int)clusters.size();
939}
940
941
942int
944#ifdef DEBUG_JOINJUNCTIONS
945 std::cout << "joinSameJunctions...\n";
946#endif
947 std::set<NBNode*> checked;
948 NodeClusters clusters;
949 for (auto& item : myNodes) {
950 NBNode* n = item.second;
951 if (myJoinExclusions.count(item.first) > 0) {
952 continue;
953 }
954 std::vector<NBNode*> nearby = retrieveByPos(n->getPosition(), maxDist);
955 NodeSet cluster;
956 for (NBNode* n2 : nearby) {
957 if (myJoinExclusions.count(n2->getID()) == 0 && checked.count(n2) == 0) {
958 cluster.insert(n2);
959 }
960 }
961 if (cluster.size() > 1) {
962 checked.insert(cluster.begin(), cluster.end());
963 clusters.push_back(cluster);
964 }
965 }
966 joinNodeClusters(clusters, dc, ec, tlc, true);
967 return (int)clusters.size();
968}
969
970void
971NBNodeCont::pruneClusterFringe(NodeSet& cluster, double maxDist, bool remove2TLS) const {
972#ifdef DEBUG_JOINJUNCTIONS
973 if (gDebugFlag1) {
974 std::cout << "pruning cluster=" << joinNamedToString(cluster, ' ') << "\n";
975 }
976#endif
977 // iteratively remove the fringe
978 NodeSet geometryLikeTLS;
979 bool pruneFringe = true;
980 bool pruneNoisyFringe = false;
981 // collect nodes that shall be joined due to distance but are not connected
982 // to the cluster for passenger traffic
983 while (pruneFringe) {
984 pruneFringe = false;
985 for (NodeSet::iterator j = cluster.begin(); j != cluster.end();) {
986 NodeSet::iterator check = j;
987 NBNode* n = *check;
988 ++j;
989
990 // compute clusterDist for node (length of shortest edge which connects this node to the cluster)
991 double clusterDist = std::numeric_limits<double>::max();
992 bool touchingCluster = false;
993 for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
994 NBNode* neighbor = (*it_edge)->getToNode();
995 if (cluster.count(neighbor) != 0) {
996 clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
997 touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
998 }
999 }
1000 for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
1001 NBNode* neighbor = (*it_edge)->getFromNode();
1002 if (cluster.count(neighbor) != 0) {
1003 clusterDist = MIN2(clusterDist, (*it_edge)->getLoadedLength());
1004 touchingCluster |= n->getPosition().distanceTo2D(neighbor->getPosition()) <= SUMO_const_laneWidth;
1005 }
1006 }
1007 // remove geometry-like nodes at fringe of the cluster
1008 // (they have 1 neighbor in the cluster and at most 1 neighbor outside the cluster)
1009 std::set<NBNode*> outsideNeighbors;
1010 std::set<NBNode*> clusterNeighbors;
1011 const double pedestrianFringeThreshold = 0.3;
1012 for (NBEdge* e : n->getEdges()) {
1013 NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1014 if (cluster.count(neighbor) == 0) {
1015 if ((e->getPermissions() & SVC_PASSENGER) != 0
1016 || isRailway(e->getPermissions()) // join railway crossings
1017 || (clusterDist <= pedestrianFringeThreshold
1018 && (!pruneNoisyFringe
1019 || isForVulnerableModes(e->getPermissions())
1020 // permit joining small opposite merges
1021 || getDiameter(cluster) < maxDist
1022 || cluster.size() == 2))
1023 || touchingCluster) {
1024 outsideNeighbors.insert(neighbor);
1025 }
1026 } else {
1027 clusterNeighbors.insert(neighbor);
1028 }
1029 }
1030#ifdef DEBUG_JOINJUNCTIONS
1031 if (gDebugFlag1) std::cout << " check n=" << n->getID()
1032 << " clusterDist=" << clusterDist
1033 << " cd<th=" << (clusterDist <= pedestrianFringeThreshold)
1034 << " touching=" << touchingCluster
1035 << " out=" << joinNamedToString(outsideNeighbors, ',')
1036 << " in=" << joinNamedToString(clusterNeighbors, ',')
1037 << " dia=" << getDiameter(cluster)
1038 << "\n";
1039#endif
1040 if (clusterNeighbors.size() == 0
1041 || (outsideNeighbors.size() <= 1
1042 && clusterNeighbors.size() == 1
1043 && !(n->isTLControlled() /*|| n->hadSignal()*/))) {
1044 cluster.erase(check);
1045 pruneFringe = true; // other nodes could belong to the fringe now
1046#ifdef DEBUG_JOINJUNCTIONS
1047 if (gDebugFlag1) {
1048 std::cout << " pruned n=" << n->getID() << "\n";
1049 }
1050#endif
1051 } else if (outsideNeighbors.size() <= 1 && clusterNeighbors.size() == 1) {
1052 geometryLikeTLS.insert(n);
1053 }
1054 }
1055 if (!pruneFringe && !pruneNoisyFringe) {
1056 // run once more and prune more things (with a look at cluster size)
1057 pruneFringe = true;
1058 pruneNoisyFringe = true;
1059
1060 }
1061 }
1062 if (remove2TLS && geometryLikeTLS.size() == cluster.size()) {
1063 cluster.clear();
1064 }
1065}
1066
1067double
1069 double result = 0;
1070 for (const NBNode* n1 : cluster) {
1071 for (const NBNode* n2 : cluster) {
1072 result = MAX2(result, n1->getPosition().distanceTo2D(n2->getPosition()));
1073 }
1074 }
1075 return result;
1076}
1077
1078int
1079NBNodeCont::pruneLongEdges(NodeSet& cluster, double maxDist, const bool dryRun) {
1080 std::set<NBNode*> toRemove;
1081 int maxPassengerLanes = 0;
1082 for (NBNode* n : cluster) {
1083 for (NBEdge* edge : n->getEdges()) {
1084 maxPassengerLanes = MAX2(maxPassengerLanes, edge->getNumLanesThatAllow(SVC_PASSENGER));
1085 }
1086 }
1087 for (NBNode* n : cluster) {
1088 for (NBEdge* edge : n->getOutgoingEdges()) {
1089 // we must track the edge length across geometry like nodes
1090 // Also, intersections that are geometry-like
1091 // from the perspective of passenger traffic should be tracked across
1092 std::vector<NBNode*> passed;
1093 double length = 0;
1094 NBEdge* cur = edge;
1095 NBNode* to = edge->getToNode();
1096 while (cluster.count(to) != 0) {
1097 length += cur->getLoadedLength();
1098 bool goStraight = (std::find(passed.begin(), passed.end(), to) == passed.end()
1099 && (edge->getPermissions() & SVC_PASSENGER) != 0
1100 && to->geometryLike(
1103 passed.push_back(to);
1104 if (goStraight) {
1106 if (cur != nullptr) {
1107 to = cur->getToNode();
1108 } else {
1109 break;
1110 }
1111 } else {
1112 break;
1113 }
1114 }
1115 // allow higher threshold at larger junctions
1116 double longThreshold = maxDist + SUMO_const_laneWidth * MAX2(0, maxPassengerLanes - 1);
1117#ifdef DEBUG_JOINJUNCTIONS
1118 if (gDebugFlag1) {
1119 std::cout << "check edge length " << edge->getID() << " (" << length << ", passed=" << passed.size() << ", max=" << longThreshold << ")\n";
1120 }
1121#endif
1122 if (length > longThreshold) {
1123 // we found an edge that should not be removed. Maybe we can
1124 // still keep the start or end in the cluster
1125 // (keep the start if the end can be removed and vice versa)
1126 const bool keepStart = getClusterNeighbors(passed.back(), longThreshold, cluster).size() == 1;
1127 const bool keepEnd = !keepStart && getClusterNeighbors(n, longThreshold, cluster).size() == 1;
1128#ifdef DEBUG_JOINJUNCTIONS
1129 if (gDebugFlag1) {
1130 std::cout << "node=" << n->getID() << " long edge " << edge->getID() << " (" << length << ", passed=" << toString(passed) << ", max=" << longThreshold << ") keepStart=" << keepStart << " keepEnd=" << keepEnd << "\n";
1131 }
1132#endif
1133 if (!keepStart) {
1134 toRemove.insert(n);
1135 }
1136 toRemove.insert(passed.begin(), passed.end() - 1);
1137 if (!keepEnd) {
1138 toRemove.insert(passed.back());
1139 }
1140
1141 }
1142 }
1143 }
1144 if (!dryRun) {
1145 for (std::set<NBNode*>::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
1146 cluster.erase(*j);
1147 }
1148 }
1149 return (int)toRemove.size();
1150}
1151
1152
1153NodeSet
1154NBNodeCont::getClusterNeighbors(const NBNode* n, double longThreshold, NodeSet& cluster) {
1155 NodeSet result;
1156 for (NBEdge* e : n->getEdges()) {
1157 if (e->getLength() > longThreshold) {
1158 continue;
1159 }
1160 NBNode* neighbor = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1161 if (cluster.count(neighbor) != 0) {
1162 result.insert(neighbor);
1163 }
1164 }
1165 return result;
1166}
1167
1168
1169void
1170NBNodeCont::pruneSlipLaneNodes(NodeSet& cluster, double maxDist) const {
1171#ifdef DEBUG_JOINJUNCTIONS
1172 if (gDebugFlag1) {
1173 std::cout << "pruning slip-lanes at cluster=" << joinNamedToString(cluster, ' ') << "\n";
1174 }
1175#endif
1176 // fringe has already been removed
1177 if (cluster.size() <= 2) {
1178 return;
1179 }
1180 NodeSet toRemove;
1181 for (NBNode* n : cluster) {
1182 EdgeVector outgoing;
1183 double inAngle;
1184 // find slip lanes where the start is part of the cluster
1185 if (maybeSlipLaneStart(n, outgoing, inAngle)) {
1186 // potential slip lane start but we don't know which of the outgoing edges it is
1187#ifdef DEBUG_JOINJUNCTIONS
1188 if (gDebugFlag1) {
1189 std::cout << " candidate slip-lane start=" << n->getID() << " outgoing=" << toString(outgoing) << "\n";
1190 }
1191#endif
1192 for (NBEdge* contEdge : outgoing) {
1193 if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1194 continue;
1195 }
1196 double slipLength = contEdge->getLength();
1197 NBNode* cont = contEdge->getToNode();
1198 NodeSet cands;
1199 cands.insert(n);
1200 while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1201 if (cands.count(cont) != 0) {
1202 break; // circle, should not happen
1203 }
1204 cands.insert(cont);
1205#ifdef DEBUG_JOINJUNCTIONS
1206 if (gDebugFlag1) {
1207 std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1208 }
1209#endif
1210 NBEdge* next = cont->getOutgoingEdges().front();
1211 slipLength += next->getLength();
1212 cont = next->getToNode();
1213 }
1214#ifdef DEBUG_JOINJUNCTIONS
1215 if (gDebugFlag1) {
1216 std::cout << " candidate slip-lane end=" << cont->getID() << " slipLength=" << slipLength << "\n";
1217 }
1218#endif
1219 if (cont->getIncomingEdges().size() >= 2 && cont->getOutgoingEdges().size() == 1 &&
1220 // slip lanes are for turning so there needs to be a sufficient angle
1221 abs(NBHelpers::relAngle(inAngle, cont->getOutgoingEdges().front()->getAngleAtNode(cont))) > 45) {
1222 // check whether the other continuation at n is also connected to the sliplane end
1223 const NBEdge* const otherEdge = (contEdge == outgoing.front() ? outgoing.back() : outgoing.front());
1224 NodeSet visited;
1225 visited.insert(n);
1226 std::vector<NodeAndDist> toProc;
1227 toProc.push_back(std::make_pair(otherEdge->getToNode(), otherEdge->getLength()));
1228 bool found = false;
1229 while (!toProc.empty()) {
1230 NodeAndDist nodeAndDist = toProc.back();
1231 NBNode* cont2 = nodeAndDist.first;
1232 double dist = nodeAndDist.second;
1233#ifdef DEBUG_JOINJUNCTIONS
1234 if (gDebugFlag1) {
1235 std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1236 }
1237#endif
1238 toProc.pop_back();
1239 if (visited.find(cont2) != visited.end()) {
1240 continue;
1241 }
1242 visited.insert(cont2);
1243 if (cont2 == cont) {
1244 found = true;
1245 break;
1246 }
1247 for (NBEdge* e : cont2->getOutgoingEdges()) {
1248 const double dist2 = dist + e->getLength();
1249 if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1250 toProc.push_back(std::make_pair(e->getToNode(), dist2));
1251 }
1252 }
1253 }
1254 if (found) {
1255 // found slip lane
1256 cands.insert(cont);
1257 toRemove.insert(cands.begin(), cands.end());
1258#ifdef DEBUG_JOINJUNCTIONS
1259 if (gDebugFlag1) {
1260 std::cout << " found slip-lane with nodes=" << joinNamedToString(cands, ' ') << "\n";
1261 }
1262#endif
1263 }
1264 }
1265 }
1266 }
1267
1268 EdgeVector incoming;
1269 double outAngle;
1270 // find slip lanes where the end is part of the cluster
1271 if (maybeSlipLaneEnd(n, incoming, outAngle)) {
1272 // potential slip lane end but we don't know which of the incoming edges it is
1273#ifdef DEBUG_JOINJUNCTIONS
1274 if (gDebugFlag1) {
1275 std::cout << " candidate slip-lane end=" << n->getID() << " incoming=" << toString(incoming) << "\n";
1276 }
1277#endif
1278 for (NBEdge* contEdge : incoming) {
1279 if ((contEdge->getPermissions() & SVC_PASSENGER) == 0) {
1280 continue;
1281 }
1282 double slipLength = contEdge->getLength();
1283 NBNode* cont = contEdge->getFromNode();
1284 NodeSet cands;
1285 cands.insert(n);
1286 while (isSlipLaneContinuation(cont) && slipLength < MAX_SLIPLANE_LENGTH) {
1287 if (cands.count(cont) != 0) {
1288 break; // circle, should not happen
1289 }
1290 cands.insert(cont);
1291#ifdef DEBUG_JOINJUNCTIONS
1292 if (gDebugFlag1) {
1293 std::cout << " candidate slip-lane cont=" << cont->getID() << "\n";
1294 }
1295#endif
1296 NBEdge* next = cont->getIncomingEdges().front();
1297 slipLength += next->getLength();
1298 cont = next->getFromNode();
1299 }
1300#ifdef DEBUG_JOINJUNCTIONS
1301 if (gDebugFlag1) {
1302 std::cout << " candidate slip-lane start=" << cont->getID() << " slipLength=" << slipLength << "\n";
1303 }
1304#endif
1305 if (cont->getOutgoingEdges().size() >= 2 && cont->getIncomingEdges().size() == 1 &&
1306 // slip lanes are for turning so there needs to be a sufficient angle
1307 abs(NBHelpers::relAngle(outAngle, cont->getIncomingEdges().front()->getAngleAtNode(cont))) > 45) {
1308 // check whether the other continuation at n is also connected to the sliplane end
1309 const NBEdge* const otherEdge = (contEdge == incoming.front() ? incoming.back() : incoming.front());
1310 NodeSet visited;
1311 visited.insert(n);
1312 std::vector<NodeAndDist> toProc;
1313 toProc.push_back(std::make_pair(otherEdge->getFromNode(), otherEdge->getLength()));
1314 bool found = false;
1315 while (!toProc.empty()) {
1316 NodeAndDist nodeAndDist = toProc.back();
1317 NBNode* cont2 = nodeAndDist.first;
1318 double dist = nodeAndDist.second;
1319#ifdef DEBUG_JOINJUNCTIONS
1320 if (gDebugFlag1) {
1321 std::cout << " search alternative cont2=" << cont2->getID() << " dist=" << dist << "\n";
1322 }
1323#endif
1324 toProc.pop_back();
1325 if (visited.find(cont2) != visited.end()) {
1326 continue;
1327 }
1328 visited.insert(cont2);
1329 if (cont2 == cont) {
1330 found = true;
1331 break;
1332 }
1333 for (NBEdge* e : cont2->getIncomingEdges()) {
1334 const double dist2 = dist + e->getLength();
1335 if (dist2 < slipLength * 2 && (e->getPermissions() & SVC_PASSENGER) != 0) {
1336 toProc.push_back(std::make_pair(e->getFromNode(), dist2));
1337 }
1338 }
1339 }
1340 if (found) {
1341 // found slip lane
1342 cands.insert(cont);
1343 toRemove.insert(cands.begin(), cands.end());
1344#ifdef DEBUG_JOINJUNCTIONS
1345 if (gDebugFlag1) {
1346 std::cout << " found slip-lane start with nodes=" << joinNamedToString(cands, ' ') << "\n";
1347 }
1348#endif
1349 }
1350 }
1351 }
1352 }
1353
1354
1355
1356 }
1357 int numRemoved = 0;
1358 for (NBNode* n : toRemove) {
1359 numRemoved += (int)cluster.erase(n);
1360 }
1361 if (numRemoved > 0) {
1362#ifdef DEBUG_JOINJUNCTIONS
1363 if (gDebugFlag1) {
1364 std::cout << " removed " << numRemoved << " nodes from cluster: " << joinNamedToString(toRemove, ' ') << "\n";
1365 }
1366#endif
1367 pruneClusterFringe(cluster, maxDist);
1368 }
1369}
1370
1371
1372bool
1374 return cont->getPassengerEdges(true).size() == 1 && cont->getPassengerEdges(false).size() == 1;
1375}
1376
1377
1378bool
1379NBNodeCont::maybeSlipLaneStart(const NBNode* n, EdgeVector& outgoing, double& inAngle) const {
1380 EdgeVector inPE = n->getPassengerEdges(true);
1381 EdgeVector outPE = n->getPassengerEdges(false);
1382 if (inPE.size() == 1 && outPE.size() == 2) {
1383 outgoing.insert(outgoing.begin(), outPE.begin(), outPE.end());
1384 inAngle = inPE.front()->getAngleAtNode(n);
1385 return true;
1386 } else if (inPE.size() >= 2 && outPE.size() == 3) {
1387 // check if the incoming edges are going in opposite directions and then
1388 // use the incoming edge that has 2 almost-straight outgoing edges
1389 const double inRelAngle = fabs(NBHelpers::relAngle(inPE.front()->getAngleAtNode(n), inPE.back()->getAngleAtNode(n)));
1390 //std::cout << "n=" << n->getID() << " inRelAngle=" << inRelAngle << "\n";
1391 if (inRelAngle < 135) {
1392 return false; // not opposite incoming
1393 }
1394 for (NBEdge* in : inPE) {
1395 EdgeVector straight;
1396 int numReverse = 0;
1397 for (NBEdge* out : outPE) {
1398 const double outRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1399 if (outRelAngle <= 45) {
1400 straight.push_back(out);
1401 } else if (outRelAngle >= 135) {
1402 numReverse++;
1403 }
1404 }
1405 if (straight.size() == 2 && numReverse == 1) {
1406 outgoing.insert(outgoing.begin(), straight.begin(), straight.end());
1407 inAngle = in->getAngleAtNode(n);
1408 return true;
1409 }
1410 }
1411 }
1412 return false;
1413}
1414
1415
1416bool
1417NBNodeCont::maybeSlipLaneEnd(const NBNode* n, EdgeVector& incoming, double& outAngle) const {
1418 EdgeVector inPE = n->getPassengerEdges(true);
1419 EdgeVector outPE = n->getPassengerEdges(false);
1420 if (inPE.size() == 2 && outPE.size() == 1) {
1421 incoming.insert(incoming.begin(), inPE.begin(), inPE.end());
1422 outAngle = outPE.front()->getAngleAtNode(n);
1423 return true;
1424 } else if (inPE.size() == 3 && outPE.size() >= 2) {
1425 // check if the outgoing edges are going in opposite directions and then
1426 // use the outgoing edge that has 2 almost-straight incoming edges
1427 const double outRelAngle = fabs(NBHelpers::relAngle(outPE.front()->getAngleAtNode(n), outPE.back()->getAngleAtNode(n)));
1428 //std::cout << "n=" << n->getID() << " outRelAngle=" << outRelAngle << "\n";
1429 if (outRelAngle < 135) {
1430 return false; // not opposite outgoing
1431 }
1432 for (NBEdge* out : outPE) {
1433 EdgeVector straight;
1434 int numReverse = 0;
1435 for (NBEdge* in : inPE) {
1436 const double inRelAngle = fabs(NBHelpers::relAngle(in->getAngleAtNode(n), out->getAngleAtNode(n)));
1437 if (inRelAngle <= 45) {
1438 straight.push_back(in);
1439 } else if (inRelAngle >= 135) {
1440 numReverse++;
1441 }
1442 }
1443 if (straight.size() == 2 && numReverse == 1) {
1444 incoming.insert(incoming.begin(), straight.begin(), straight.end());
1445 outAngle = out->getAngleAtNode(n);
1446 return true;
1447 }
1448 }
1449 }
1450 return false;
1451}
1452
1453bool
1454NBNodeCont::feasibleCluster(const NodeSet& cluster, const std::map<const NBNode*, std::vector<NBNode*> >& ptStopEnds,
1455 double maxDist, std::string& reason, NBNode*& tryRemove) const {
1456 // check for clusters which are to complex and probably won't work very well
1457 // we count the incoming edges of the final junction
1458 std::map<NBEdge*, double, ComparatorIdLess> finalIncomingAngles;
1459 std::map<NBEdge*, double, ComparatorIdLess> finalOutgoingAngles;
1460 for (NBNode* n : cluster) {
1461 for (EdgeVector::const_iterator it_edge = n->getIncomingEdges().begin(); it_edge != n->getIncomingEdges().end(); ++it_edge) {
1462 NBEdge* edge = *it_edge;
1463 if (cluster.count(edge->getFromNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1464 // incoming edge, does not originate in the cluster
1465 finalIncomingAngles[edge] = edge->getAngleAtNode(edge->getToNode());
1466 }
1467 }
1468 for (EdgeVector::const_iterator it_edge = n->getOutgoingEdges().begin(); it_edge != n->getOutgoingEdges().end(); ++it_edge) {
1469 NBEdge* edge = *it_edge;
1470 if (cluster.count(edge->getToNode()) == 0 && (edge->getPermissions() & SVC_PASSENGER) != 0) {
1471 // outgoing edge, does not end in the cluster
1472 finalOutgoingAngles[edge] = edge->getAngleAtNode(edge->getFromNode());
1473 }
1474 }
1475
1476 }
1477#ifdef DEBUG_JOINJUNCTIONS
1478 for (NBNode* n : cluster) {
1479 if (DEBUGCOND(n)) {
1480 std::cout << "feasibleCluster c=" << joinNamedToString(cluster, ',')
1481 << "\n inAngles=" << joinNamedToString(finalIncomingAngles, ' ', ':')
1482 << "\n outAngles=" << joinNamedToString(finalOutgoingAngles, ' ', ':')
1483 << "\n";
1484 }
1485 }
1486#endif
1487 if (finalIncomingAngles.size() > 5) {
1488 reason = toString(finalIncomingAngles.size()) + " incoming edges";
1489 return false;
1490 }
1491 // check for incoming parallel edges
1492 const double PARALLEL_THRESHOLD_DIFF_NODE = OptionsCont::getOptions().getFloat("junctions.join.parallel-threshold");
1493 const double PARALLEL_THRESHOLD_SAME_NODE = PARALLEL_THRESHOLD_DIFF_NODE / 3;
1494 bool foundParallel = false;
1495 for (auto j = finalIncomingAngles.begin(); j != finalIncomingAngles.end() && !foundParallel; ++j) {
1496 auto k = j;
1497 for (++k; k != finalIncomingAngles.end() && !foundParallel; ++k) {
1498 const double angleDiff = fabs(j->second - k->second);
1499 if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1500 NBEdge* e1 = j->first;
1501 NBEdge* e2 = k->first;
1502 // for edge targeting the same node, permit a narrower angle
1503 const double edgeDist = e1->getLaneShape(0).back().distanceTo2D(e2->getLaneShape(0).back());
1504#ifdef DEBUG_JOINJUNCTIONS
1505 if (DEBUGCOND(e1->getToNode())) {
1506 std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1507 }
1508#endif
1509 if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1510 (e1->getToNode() == e2->getToNode()
1511 || (edgeDist < maxDist)))) {
1512 continue;
1513 }
1514 reason = "parallel incoming " + e1->getID() + "," + e2->getID();
1515 if (e1->getToNode() != e2->getToNode() && (int)cluster.size() > 2) {
1516 // removing one of the nodes and try again
1517 if (e1->getPriority() > e2->getPriority()
1519 tryRemove = e2->getToNode();
1520 } else {
1521 tryRemove = e1->getToNode();
1522 }
1523 }
1524 return false;
1525 }
1526 }
1527 }
1528 // check for outgoing parallel edges
1529 for (auto j = finalOutgoingAngles.begin(); j != finalOutgoingAngles.end() && !foundParallel; ++j) {
1530 auto k = j;
1531 for (++k; k != finalOutgoingAngles.end() && !foundParallel; ++k) {
1532 const double angleDiff = fabs(j->second - k->second);
1533 if (angleDiff < PARALLEL_THRESHOLD_DIFF_NODE) {
1534 NBEdge* e1 = j->first;
1535 NBEdge* e2 = k->first;
1536 // for edge leaving the same node, permit a narrower angle
1537 const double edgeDist = e1->getLaneShape(0).front().distanceTo2D(e2->getLaneShape(0).front());
1538#ifdef DEBUG_JOINJUNCTIONS
1539 if (DEBUGCOND(e1->getFromNode())) {
1540 std::cout << " angleDiff=" << angleDiff << " shapeDist=" << edgeDist << "\n";
1541 }
1542#endif
1543 if (angleDiff >= PARALLEL_THRESHOLD_SAME_NODE && (
1544 (e1->getFromNode() == e2->getFromNode()
1545 || (edgeDist < maxDist)))) {
1546 continue;
1547 }
1548 reason = "parallel outgoing " + e1->getID() + "," + e2->getID();
1549 if (e1->getFromNode() != e2->getFromNode() && (int)cluster.size() > 2) {
1550 // removing one of the nodes and try again
1551 if (e1->getPriority() > e2->getPriority()
1553 tryRemove = e2->getFromNode();
1554 } else {
1555 tryRemove = e1->getFromNode();
1556 }
1557 }
1558 return false;
1559 }
1560 }
1561 }
1562 // check for stop edges and tls within the cluster
1563 bool hasTLS = false;
1564 for (NBNode* n : cluster) {
1565 if (n->isTLControlled() || n->hadSignal()) {
1566 hasTLS = true;
1567 }
1568 const auto& stopEnds = ptStopEnds.find(n);
1569 if (stopEnds != ptStopEnds.end()) {
1570 for (NBNode* const to : stopEnds->second) {
1571 if (cluster.count(to) != 0) {
1572 reason = "it contains a pt stop edge";
1573 return false;
1574 }
1575 }
1576 }
1577 }
1578 // prevent removal of long edges unless there is weak circle or a traffic light
1579 if (cluster.size() > 2) {
1580 // find the nodes with the biggests physical distance between them
1581 double maxLength = -1;
1582 NBEdge* maxEdge = nullptr;
1583 for (NBNode* n1 : cluster) {
1584 for (NBNode* n2 : cluster) {
1585 NBEdge* e1 = n1->getConnectionTo(n2);
1586 NBEdge* e2 = n2->getConnectionTo(n1);
1587 if (e1 != nullptr && e1->getLoadedLength() > maxLength) {
1588 maxLength = e1->getLoadedLength();
1589 maxEdge = e1;
1590 }
1591 if (e2 != nullptr && e2->getLoadedLength() > maxLength) {
1592 maxLength = e2->getLoadedLength();
1593 maxEdge = e2;
1594 }
1595 }
1596 }
1597#ifdef DEBUG_JOINJUNCTIONS
1598 for (NBNode* n : cluster) {
1599 if (DEBUGCOND(n)) {
1600 std::cout << "feasible hasTLS=" << hasTLS << " maxLength=" << maxLength << " maxEdge=" << maxEdge->getID() << "\n";
1601 }
1602 }
1603#endif
1604 if (!hasTLS && maxLength > 5) {
1605 // find a weak circle within cluster that does not use maxEdge
1606 std::vector<NBNode*> toCheck;
1607 std::set<NBNode*> visited;
1608 toCheck.push_back(maxEdge->getToNode());
1609 bool foundCircle = false;
1610 while (!toCheck.empty()) {
1611 NBNode* n = toCheck.back();
1612 if (n == maxEdge->getFromNode()) {
1613 foundCircle = true;
1614 break;
1615 }
1616 toCheck.pop_back();
1617 visited.insert(n);
1618 for (NBEdge* e : n->getEdges()) {
1619 if (e != maxEdge) {
1620 NBNode* cand = e->getFromNode() == n ? e->getToNode() : e->getFromNode();
1621 if (visited.count(cand) == 0 && cluster.count(cand) != 0) {
1622 toCheck.push_back(cand);
1623 }
1624 }
1625 }
1626 }
1627 if (!foundCircle) {
1628 reason = "not compact (maxEdge=" + maxEdge->getID() + " length=" + toString(maxLength) + ")";
1629 return false;
1630 }
1631 }
1632 }
1633 // prevent joining of simple merging/spreading structures
1634 if (cluster.size() >= 2) {
1635 int entryNodes = 0;
1636 int exitNodes = 0;
1637 EdgeVector outsideIncoming;
1638 EdgeVector outsideOutgoing;
1639 int edgesWithin = 0;
1640 for (NBNode* n : cluster) {
1641 bool foundOutsideIncoming = false;
1642 for (NBEdge* e : n->getIncomingEdges()) {
1643 if (cluster.count(e->getFromNode()) == 0) {
1644 // edge entering from outside the cluster
1645 outsideIncoming.push_back(e);
1646 foundOutsideIncoming = true;
1647 } else {
1648 edgesWithin++;
1649 }
1650 }
1651 if (foundOutsideIncoming) {
1652 entryNodes++;
1653 }
1654 bool foundOutsideOutgoing = false;
1655 for (NBEdge* e : n->getOutgoingEdges()) {
1656 if (cluster.count(e->getToNode()) == 0) {
1657 // edge leaving cluster
1658 outsideOutgoing.push_back(e);
1659 foundOutsideOutgoing = true;
1660 }
1661 }
1662 if (foundOutsideOutgoing) {
1663 exitNodes++;
1664 }
1665 }
1666 if (!hasTLS) {
1667 if (entryNodes < 2) {
1668 reason = "only 1 entry node";
1669 return false;
1670 }
1671 if (exitNodes < 2) {
1672 reason = "only 1 exit node";
1673 return false;
1674 }
1675 if (cluster.size() == 2) {
1676 if (edgesWithin == 1 && outsideIncoming.size() < 3 && outsideOutgoing.size() < 3) {
1677 reason = "only 1 edge within and no cross-traffic";
1678 return false;
1679 }
1680 }
1681 }
1682 /*
1683 if (NBNode::geometryLike(outsideIncoming, outsideOutgoing) && hasTLS && OptionsCont::getOptions().getBool("tls.discard-simple")) {
1684 reason = "geometry-like simple tls";
1685 return false;
1686 }
1687 */
1688 }
1689 return true;
1690}
1691
1692
1693bool
1694NBNodeCont::reduceToCircle(NodeSet& cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector<NBNode*> cands) const {
1695#ifdef DEBUG_REDUCE
1696 std::cout << "reduceToCircle cs=" << circleSize << " cands=" << toString(cands, ',') << " startNodes=" << joinNamedToString(startNodes, ',') << "\n";
1697#endif
1698 assert(circleSize >= 2);
1699 if ((int)cands.size() == circleSize) {
1700 if (cands.back()->getConnectionTo(cands.front()) != nullptr) {
1701 // cluster found
1702 NodeSet candCluster;
1703 candCluster.insert(cands.begin(), cands.end());
1704 pruneClusterFringe(candCluster, maxDist, true);
1705 bool feasible = (int)candCluster.size() == circleSize;
1706 if (feasible) {
1707 cluster.clear();
1708 cluster.insert(cands.begin(), cands.end());
1709 }
1710 return feasible;
1711 } else {
1712 return false;
1713 }
1714 }
1715 if ((int)cluster.size() <= circleSize || startNodes.size() == 0) {
1716 // no reduction possible
1717#ifdef DEBUG_REDUCE
1718 std::cout << " abort\n";
1719#endif
1720 return false;
1721 }
1722 if (cands.size() == 0) {
1723 // try to find a circle starting from another start node
1724 NBEdge* e = shortestEdge(cluster, startNodes, cands);
1725 if (e != nullptr) {
1726 cands.push_back(e->getFromNode());
1727 startNodes.erase(e->getFromNode());
1728 if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands)) {
1729 return true;
1730 } else {
1731 // try another start node
1732 return reduceToCircle(cluster, circleSize, startNodes, maxDist);
1733 }
1734 }
1735 } else {
1736 NodeSet singleStart;
1737 singleStart.insert(cands.back());
1738 NBEdge* e = shortestEdge(cluster, singleStart, cands);
1739 if (e != nullptr) {
1740 std::vector<NBNode*> cands2(cands);
1741 cands2.push_back(e->getToNode());
1742 if (reduceToCircle(cluster, circleSize, startNodes, maxDist, cands2)) {
1743 return true;
1744 }
1745 }
1746 }
1747#ifdef DEBUG_REDUCE
1748 std::cout << " abort2\n";
1749#endif
1750 return false;
1751}
1752
1753
1754NBEdge*
1755NBNodeCont::shortestEdge(const NodeSet& cluster, const NodeSet& startNodes, const std::vector<NBNode*>& exclude) const {
1756 double minDist = std::numeric_limits<double>::max();
1757 NBEdge* result = nullptr;
1758 for (NBNode* n : startNodes) {
1759 for (NBEdge* e : n->getOutgoingEdges()) {
1760 NBNode* neigh = e->getToNode();
1761 if (cluster.count(neigh) != 0 && std::find(exclude.begin(), exclude.end(), neigh) == exclude.end()) {
1762 const double dist = n->getPosition().distanceTo2D(neigh->getPosition());
1763 //std::cout << " e=" << e->getID() << " dist=" << dist << " minD=" << minDist << "\n";
1764 if (dist < minDist) {
1765 minDist = dist;
1766 result = e;
1767 }
1768 }
1769 }
1770 }
1771 //std::cout << "closestNeighbor startNodes=" << toString(startNodes) << " result=" << Named::getIDSecure(result) << "\n";
1772 return result;
1773}
1774
1775void
1777 NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, bool resetConnections) {
1778 for (NodeSet cluster : clusters) {
1779 joinNodeCluster(cluster, dc, ec, tlc, nullptr, resetConnections);
1780 }
1781}
1782
1783
1784void
1785NBNodeCont::joinNodeCluster(NodeSet cluster, NBDistrictCont& dc, NBEdgeCont& ec, NBTrafficLightLogicCont& tlc, NBNode* predefined, bool resetConnections) {
1786 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1787 assert(cluster.size() > 1);
1788 std::string id = "cluster_";
1789 Position pos;
1790 bool setTL = false;
1793 // collect edges
1794 std::set<NBEdge*, ComparatorIdLess> allEdges;
1795 for (NBNode* n : cluster) {
1796 const EdgeVector& edges = n->getEdges();
1797 allEdges.insert(edges.begin(), edges.end());
1798 }
1799 // determine edges with are incoming or fully inside
1800 std::set<NBEdge*, ComparatorIdLess> clusterIncoming;
1801 std::set<NBEdge*, ComparatorIdLess> inside;
1802 for (NBEdge* e : allEdges) {
1803 if (cluster.count(e->getToNode()) > 0) {
1804 if (cluster.count(e->getFromNode()) > 0) {
1805 inside.insert(e);
1806 if (e->getSignalPosition() != Position::INVALID) {
1807 setTL = true;
1809 }
1810 } else {
1811 clusterIncoming.insert(e);
1812 }
1813 }
1814 }
1815#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1816 std::cout << "joining cluster " << joinNamedToString(cluster, ' ')
1817 << " resetConnections=" << resetConnections << "\n"
1818 << " incoming=" << joinNamedToString(clusterIncoming, ' ') << "\n"
1819 << " inside=" << joinNamedToString(inside, ' ') << "\n";
1820#endif
1821 analyzeCluster(cluster, id, pos, setTL, type, nodeType);
1822 NBNode* newNode = nullptr;
1823 if (predefined != nullptr) {
1824 newNode = predefined;
1825 } else {
1826 if (!insert(id, pos)) {
1827 // should not fail
1828 WRITE_WARNINGF(TL("Could not join junctions %."), id);
1829 return;
1830 }
1831 newNode = retrieve(id);
1832 }
1833 std::string tlID = id;
1834 if (predefined != nullptr) {
1835 if (predefined->getType() != SumoXMLNodeType::UNKNOWN) {
1836 nodeType = predefined->getType();
1837 }
1838 Position ppos = predefined->getPosition();
1839 if (ppos.x() != Position::INVALID.x()) {
1840 pos.setx(ppos.x());
1841 }
1842 if (ppos.y() != Position::INVALID.y()) {
1843 pos.sety(ppos.y());
1844 }
1845 if (ppos.z() != Position::INVALID.z()) {
1846 pos.setz(ppos.z());
1847 }
1848 }
1849 newNode->reinit(pos, nodeType);
1850 if (origNames) {
1851 newNode->setParameter(SUMO_PARAM_ORIGID, joinNamedToString(cluster, ' '));
1852 }
1853 if (setTL && !newNode->isTLControlled()) {
1854 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(tlID, newNode, 0, type);
1855 if (!tlc.insert(tlDef)) {
1856 // actually, nothing should fail here
1857 delete tlDef;
1858 throw ProcessError(TLF("Could not allocate tls '%'.", id));
1859 }
1860 }
1861
1862 // determine possible connectivity from outside edges
1863 std::map<NBEdge*, EdgeSet> reachable;
1864 std::map<std::pair<NBEdge*, NBEdge*>, SVCPermissions> conPermissions;
1865 EdgeSet specialPermissions;
1866 for (NBEdge* const e : clusterIncoming) {
1867 EdgeVector open;
1868 EdgeSet seen;
1869 open.push_back(e);
1870 while (open.size() > 0) {
1871 NBEdge* const cur = open.back();
1872 const SVCPermissions pCur = conPermissions.count({e, cur}) == 0 ? cur->getPermissions() : conPermissions[ {e, cur}];
1873#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1874 std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1875 std::cout << "e=" << e->getID() << " cur=" << cur->getID() << " open=" << toString(open) << "\n";
1876#endif
1877 seen.insert(cur);
1878 open.pop_back();
1879 if (cluster.count(cur->getToNode()) == 0) {
1880 //std::cout << " continue\n";
1881 continue;
1882 }
1883 const auto& cons = cur->getConnections();
1884 if (cons.size() == 0 || ec.hasPostProcessConnection(cur->getID()) || cur->getStep() == NBEdge::EdgeBuildingStep::INIT) {
1885 // check permissions to determine reachability
1886 for (NBEdge* out : cur->getToNode()->getOutgoingEdges()) {
1887 if (allEdges.count(out) != 0) {
1888 const SVCPermissions p = pCur & out->getPermissions();
1889 if (seen.count(out) == 0 || (~conPermissions[ {e, out}] & p) != 0) {
1890 if ((p & ~SVC_PEDESTRIAN) != 0) {
1891 open.push_back(out);
1892 conPermissions[ {e, out}] |= p;
1893#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1894 std::cout << " e=" << e->getID() << " out=" << out->getID() << " pOut=" << getVehicleClassNames(out->getPermissions()) << "\n p=" << getVehicleClassNames(p) << "\n q=" << getVehicleClassNames(conPermissions[ {e, out}]) << "\n";
1895#endif
1896 }
1897 }
1898 }
1899 }
1900 } else {
1901 // check existing connections
1902 for (const auto& con : cons) {
1903 if (con.toEdge != nullptr && allEdges.count(con.toEdge) != 0) {
1904 SVCPermissions p = pCur & con.toEdge->getPermissions();
1905 if (con.permissions != SVC_UNSPECIFIED) {
1906 p &= con.permissions;
1907 }
1908 if (seen.count(con.toEdge) == 0 || (~conPermissions[ {e, con.toEdge}] & p) != 0) {
1909 open.push_back(con.toEdge);
1910 conPermissions[ {e, con.toEdge}] |= p;
1911 //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";
1912 }
1913 }
1914 }
1915 }
1916 }
1917 seen.erase(e);
1918 for (NBEdge* reached : seen) {
1919 // filter out inside edges from reached
1920 if (inside.count(reached) == 0) {
1921 if (e->getStep() > NBEdge::EdgeBuildingStep::INIT && reached->getFromNode() == e->getToNode() && !e->isConnectedTo(reached)) {
1922 // also filter out edges that are outgoing of the to-node of edge but aren't currently connected
1923 continue;
1924 }
1925 reachable[e].insert(reached);
1926 const SVCPermissions pDefault = e->getPermissions() & reached->getPermissions();
1927 if (conPermissions[ {e, reached}] != pDefault) {
1928 specialPermissions.insert(e);
1929#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1930 std::cout << "e=" << e->getID() << " out=" << reached->getID() << " special=" << getVehicleClassNames(conPermissions[ {e, reached}]) << "\n";
1931#endif
1932 }
1933 }
1934 }
1935#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1936 std::cout << " reachable e=" << e->getID() << " seen=" << toString(seen) << " reachable=" << toString(reachable[e]) << "\n";
1937#endif
1938 }
1939
1940 // remap and remove edges which are completely within the new intersection
1941 if (origNames) {
1942 newNode->setParameter("origEdgeIds", joinNamedToString(inside, ' '));
1943 }
1944 for (NBEdge* e : inside) {
1945 for (NBEdge* e2 : allEdges) {
1946 if (e != e2) {
1947 e2->replaceInConnections(e, e->getConnections());
1948 }
1949 }
1950 ec.extract(dc, e, true);
1951 allEdges.erase(e);
1952 }
1953
1954 // remap edges which are incoming / outgoing
1955 for (NBEdge* e : allEdges) {
1956 const bool outgoing = cluster.count(e->getFromNode()) > 0;
1957 NBNode* from = outgoing ? newNode : e->getFromNode();
1958 NBNode* to = outgoing ? e->getToNode() : newNode;
1959 if (origNames) {
1960 if (outgoing) {
1961 e->setParameter("origFrom", e->getFromNode()->getID());
1962 } else {
1963 e->setParameter("origTo", e->getToNode()->getID());
1964 }
1965 }
1966 if (e->getTurnSignTarget() != "") {
1967 for (NBNode* n : cluster) {
1968 if (e->getTurnSignTarget() == n->getID()) {
1969 e->setTurnSignTarget(to->getID());
1970 break;
1971 }
1972 }
1973 }
1974 e->reinitNodes(from, to);
1975 // re-add connections which previously existed and may still valid.
1976 // connections to removed edges will be ignored
1977 std::vector<NBEdge::Connection> conns = e->getConnections();
1978 for (std::vector<NBEdge::Connection>::iterator k = conns.begin(); k != conns.end(); ++k) {
1979 if ((*k).toEdge == nullptr) {
1980 // edge explicitly set to have no connections
1981 continue;
1982 }
1983 e->addLane2LaneConnection((*k).fromLane, (*k).toEdge, (*k).toLane, NBEdge::Lane2LaneInfoType::USER, false, (*k).mayDefinitelyPass);
1984 if ((*k).fromLane >= 0 && (*k).fromLane < e->getNumLanes() && e->getLaneStruct((*k).fromLane).connectionsDone) {
1985 // @note (see NIImporter_DlrNavteq::ConnectedLanesHandler)
1986 e->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::INIT);
1987#ifdef DEBUG_JOINJUNCTIONS_CONNECTIONS
1988 std::cout << " e=" << e->getID() << " declareConnectionsAsLoaded\n";
1989#endif
1990 }
1991 }
1992 }
1993 if (!resetConnections) {
1994 // disable connections that were impossible with the old topology
1995 // if connectivity has special permissions, set edge to edge connections explicitly
1996 for (NBEdge* in : newNode->getIncomingEdges()) {
1997 for (NBEdge* out : newNode->getOutgoingEdges()) {
1998 if (reachable[in].count(out) == 0) {
1999 if (!ec.hasPostProcessConnection(in->getID(), out->getID())) {
2000 //std::cout << " removeUnreachable in=" << in->getID() << " out=" << out->getID() << "\n";
2001 in->removeFromConnections(out, -1, -1, true, false, true);
2002 } else {
2003 //std::cout << " hasPostProcessConnection in=" << in->getID() << " out=" << out->getID() << "\n";
2004 }
2005 } else if (specialPermissions.count(in) != 0) {
2006 SVCPermissions pDefault = in->getPermissions() & out->getPermissions();
2007 SVCPermissions p = conPermissions[ {in, out}] == 0 ? pDefault : conPermissions[ {in, out}];
2008 in->addEdge2EdgeConnection(out, true, p == pDefault ? SVC_UNSPECIFIED : p);
2009 //std::cout << " addEdge2Edge in=" << in->getID() << " out=" << out->getID() << "\n";
2010 }
2011 }
2012 }
2013 } else {
2014 for (NBEdge* in : newNode->getIncomingEdges()) {
2015 in->invalidateConnections(true);
2016 }
2017 }
2018
2019 // remove original nodes
2020 registerJoinedCluster(cluster);
2021 for (NBNode* n : cluster) {
2022 erase(n);
2023 }
2024}
2025
2026
2027void
2029 std::set<std::string> ids;
2030 for (NBNode* n : cluster) {
2031 ids.insert(n->getID());
2032 }
2033 myJoinedClusters.push_back(ids);
2034}
2035
2036void
2037NBNodeCont::registerJoinedCluster(const std::set<std::string>& cluster) {
2038 myJoinedClusters.push_back(cluster);
2039}
2040
2041void
2042NBNodeCont::unregisterJoinedCluster(const std::set<std::string>& cluster) {
2043 auto it = std::find(myJoinedClusters.begin(), myJoinedClusters.end(), cluster);
2044 if (it != myJoinedClusters.end()) {
2045 myJoinedClusters.erase(it);
2046 }
2047}
2048
2049
2050void
2051NBNodeCont::analyzeCluster(NodeSet cluster, std::string& id, Position& pos,
2052 bool& hasTLS, TrafficLightType& type, SumoXMLNodeType& nodeType) {
2053 id = createClusterId(cluster, id);
2054 bool ambiguousType = false;
2055 for (NBNode* j : cluster) {
2056 pos.add(j->getPosition());
2057 // add a traffic light if any of the cluster members was controlled
2058 if (j->isTLControlled()) {
2059 if (!hasTLS) {
2060 // init type
2061 type = (*j->getControllingTLS().begin())->getType();
2062 } else if (type != (*j->getControllingTLS().begin())->getType()) {
2063 ambiguousType = true;
2064 }
2065 hasTLS = true;
2066 }
2067 SumoXMLNodeType otherType = j->getType();
2068 if (nodeType == SumoXMLNodeType::UNKNOWN) {
2069 nodeType = otherType;
2070 } else if (nodeType != otherType) {
2071 if (hasTLS) {
2073 } else if (otherType != SumoXMLNodeType::UNKNOWN) {
2074 if ((nodeType != SumoXMLNodeType::PRIORITY && (nodeType != SumoXMLNodeType::NOJUNCTION || otherType != SumoXMLNodeType::PRIORITY))
2075 || (otherType != SumoXMLNodeType::NOJUNCTION && otherType != SumoXMLNodeType::UNKNOWN && otherType != SumoXMLNodeType::PRIORITY)) {
2076 WRITE_WARNINGF("Ambiguous node type for node cluster '%' (%,%), setting to '" + toString(SumoXMLNodeType::PRIORITY) + "'.", id, toString(nodeType), toString(otherType));
2077 }
2078 nodeType = SumoXMLNodeType::PRIORITY;
2079 }
2080 }
2081 }
2082 pos.mul(1. / (double)cluster.size());
2083 if (ambiguousType) {
2084 type = SUMOXMLDefinitions::TrafficLightTypes.get(OptionsCont::getOptions().getString("tls.default-type"));
2085 WRITE_WARNINGF(TL("Ambiguous traffic light type for node cluster '%', setting to '%'."), id, toString(type));
2086 }
2087}
2088
2089
2090// ----------- (Helper) methods for guessing/computing traffic lights
2091bool
2092NBNodeCont::shouldBeTLSControlled(const NodeSet& c, double laneSpeedThreshold, bool recheck) const {
2093 bool tooFast = false;
2094 double laneSpeedSum = 0;
2095 std::set<NBEdge*> seen;
2096 for (NBNode* j : c) {
2097 for (const NBEdge* e : j->getEdges()) {
2098 if (c.find(e->getFromNode()) != c.end() && c.find(e->getToNode()) != c.end()) {
2099 // edges fully within the cluster do not count
2100 continue;
2101 }
2102 if (j->hasIncoming(e)) {
2103 if (recheck && !j->hasConflict(e)) {
2104 // edges without conflict do not count
2105 // we can only check this after connections have been computed
2106 continue;
2107 }
2108 laneSpeedSum += (double)e->getNumLanes() * e->getLaneSpeed(0);
2109 }
2110 if (e->getLaneSpeed(0) * 3.6 > 79) {
2111 tooFast = true;
2112 }
2113 }
2114 }
2115 //std::cout << " c=" << joinNamedToString(c, ' ') << " size=" << c.size() << " laneSpeedSum=" << laneSpeedSum << " thresh=" << laneSpeedThreshold << " tooFast=" << tooFast << "\n";
2116 return !tooFast && laneSpeedSum >= laneSpeedThreshold && c.size() != 0;
2117}
2118
2119bool
2121 // check whether all component nodes are solely pedestrian crossings
2122 // (these work fine without joining)
2123 for (NBNode* node : c) {
2124 EdgeVector nonPedIncoming;
2125 EdgeVector nonPedOutgoing;
2126 for (NBEdge* e : node->getIncomingEdges()) {
2127 if (e->getPermissions() != SVC_PEDESTRIAN) {
2128 nonPedIncoming.push_back(e);
2129 }
2130 }
2131 for (NBEdge* e : node->getOutgoingEdges()) {
2132 if (e->getPermissions() != SVC_PEDESTRIAN) {
2133 nonPedOutgoing.push_back(e);
2134 }
2135 }
2136 if (!node->geometryLike(nonPedIncoming, nonPedOutgoing)) {
2137 //for (NBNode* node : c) {
2138 // if (node->getID() == "2480337678") {
2139 // std::cout << " node=" << node->getID() << " nonPedIncoming=" << toString(nonPedIncoming) << " nonPedOutgoing=" << toString(nonPedOutgoing) << "\n";
2140 // }
2141 //}
2142 return false;
2143 }
2144 }
2145 return true;
2146}
2147
2148
2149bool
2151 for (NBNode* node : c) {
2152 if (node->isTLControlled()) {
2153 NBTrafficLightDefinition* tl = (*node->getControllingTLS().begin());
2154 if (tl->getNodes().size() > 1) {
2155 // joined tls also imply a customID
2156 return true;
2157 }
2158 const std::string tlID = tl->getID();
2159 if (tlID != node->getID()
2160 && !StringUtils::startsWith(tlID, "joinedS_")
2161 && !StringUtils::startsWith(tlID, "joinedG_")
2162 && !StringUtils::startsWith(tlID, "GS")) {
2163 return true;
2164 }
2165 }
2166 }
2167 return false;
2168}
2169
2170
2171void
2173 myGuessedTLS.clear();
2174 // build list of definitely not tls-controlled junctions
2175 const double laneSpeedThreshold = oc.getFloat("tls.guess.threshold");
2176 if (oc.isSet("tls.unset")) {
2177 std::vector<std::string> notTLControlledNodes = oc.getStringVector("tls.unset");
2178 for (std::vector<std::string>::const_iterator i = notTLControlledNodes.begin(); i != notTLControlledNodes.end(); ++i) {
2180 if (n == nullptr) {
2181 throw ProcessError(TLF(" The junction '%' to set as not-controlled is not known.", *i));
2182 }
2183 std::set<NBTrafficLightDefinition*> tls = n->getControllingTLS();
2184 for (std::set<NBTrafficLightDefinition*>::const_iterator j = tls.begin(); j != tls.end(); ++j) {
2185 (*j)->removeNode(n);
2186 }
2188 myUnsetTLS.insert(n);
2189 }
2190 }
2191
2193 // loop#1 checking whether the node shall be tls controlled,
2194 // because it is assigned to a district
2195 if (oc.exists("tls.taz-nodes") && oc.getBool("tls.taz-nodes")) {
2196 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2197 NBNode* cur = (*i).second;
2198 if (cur->isNearDistrict() && myUnsetTLS.count(cur) == 0) {
2199 setAsTLControlled(cur, tlc, type);
2200 }
2201 }
2202 }
2203
2204 // figure out which nodes mark the locations of TLS signals
2205 // This assumes nodes are already joined
2206 if (oc.exists("tls.guess-signals") && oc.getBool("tls.guess-signals")) {
2207 // prepare candidate edges
2208 const double signalDist = oc.getFloat("tls.guess-signals.dist");
2209 for (const auto& item : myNodes) {
2210 const NBNode* node = item.second;
2211 if (node->isTLControlled() && (node->getIncomingEdges().size() == 1 || node->geometryLike())) {
2212#ifdef DEBUG_GUESSSIGNALS
2213 if (DEBUGCOND(node) || true) {
2214 std::cout << " propagate TLS from " << node->getID() << " downstream\n";
2215 }
2216#endif
2217 for (NBEdge* edge : node->getOutgoingEdges()) {
2218 // do not overwrite closer signals
2219 if (edge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET) {
2220 edge->setSignalPosition(node->getPosition(), node);
2221 }
2222 }
2223 }
2224 }
2225 std::set<NBEdge*> seen;
2226 std::set<NBEdge*> check;
2227 for (const auto& item : myNodes) {
2228 for (NBEdge* edge : item.second->getOutgoingEdges()) {
2229 if (edge->getSignalPosition() != Position::INVALID) {
2230 check.insert(edge);
2231 seen.insert(edge);
2232#ifdef DEBUG_GUESSSIGNALS
2233 if (DEBUGCOND(edge->getSignalNode()) || true) {
2234 std::cout << " primary signalPosition edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2235 }
2236#endif
2237 }
2238 }
2239 }
2240 // propagate signal position until the next real intersection
2241 while (check.size() > 0) {
2242 NBEdge* const edge = *check.begin();
2243 check.erase(check.begin());
2244 seen.insert(edge);
2245 NBNode* const nextNode = edge->getToNode();
2246 if (nextNode->geometryLike() && !nextNode->isTLControlled()) {
2247 for (NBEdge* const outEdge : nextNode->getOutgoingEdges()) {
2248 if (seen.count(outEdge) == 0) {
2249 outEdge->setSignalPosition(edge->getSignalPosition(), edge->getSignalNode());
2250#ifdef DEBUG_GUESSSIGNALS
2251 if (DEBUGCOND(edge->getSignalNode()) || true) {
2252 std::cout << " setSignalPosition edge=" << outEdge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2253 }
2254#endif
2255 check.insert(outEdge);
2256 }
2257 }
2258 }
2259 }
2260
2261 // check which nodes should be controlled
2262 const int slack = oc.getInt("tls.guess-signals.slack");
2263 for (std::map<std::string, NBNode*>::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2264 NBNode* node = i->second;
2265 if (myUnsetTLS.count(node) != 0) {
2266 continue;
2267 }
2268 const EdgeVector& incoming = node->getIncomingEdges();
2269 const EdgeVector& outgoing = node->getOutgoingEdges();
2270 if (!node->isTLControlled() && incoming.size() > 1 && !node->geometryLike()
2273 std::vector<const NBNode*> signals;
2274 int foundSignals = 0;
2275 int missingSignals = 0;
2276 // check if there is a signal at every incoming edge
2277 for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2278 const NBEdge* inEdge = *it_i;
2280 if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2281#ifdef DEBUG_GUESSSIGNALS
2282 if (DEBUGCOND(node)) {
2283 std::cout << " noTLS, edge=" << inEdge->getID() << "\n";
2284 }
2285#endif
2286 missingSignals++;
2287 if (missingSignals > slack) {
2288 break;
2289 }
2290 }
2291 } else {
2292 foundSignals++;
2293 }
2294 }
2295 missingSignals = 0;
2296 int foundSignalsAtDist = 0;
2297 if (foundSignals > 1 && missingSignals <= slack && missingSignals < foundSignals) {
2299 // check if all signals are within the required distance
2300 // (requires detailed geometry computation)
2301 for (EdgeVector::const_iterator it_i = incoming.begin(); it_i != incoming.end(); ++it_i) {
2302 const NBEdge* inEdge = *it_i;
2303 if (inEdge->getSignalOffset() == NBEdge::UNSPECIFIED_SIGNAL_OFFSET || inEdge->getSignalOffset() > signalDist) {
2304 if ((inEdge->getPermissions() & SVC_PASSENGER) != 0) {
2305#ifdef DEBUG_GUESSSIGNALS
2306 if (DEBUGCOND(node)) {
2307 std::cout << " noTLS, edge=" << inEdge->getID() << " offset=" << inEdge->getSignalOffset() << " tlsPos=" << inEdge->getSignalPosition() << "\n";
2308 }
2309#endif
2310 missingSignals++;
2311 if (missingSignals > slack) {
2312 break;
2313 }
2314 }
2315 } else {
2316 foundSignalsAtDist++;
2317 }
2318 const NBNode* signal = inEdge->getSignalNode();
2319 if (signal != nullptr) {
2320 signals.push_back(signal);
2321 }
2322 }
2323 // outgoing edges may be tagged with pedestrian crossings. These
2324 // should also be merged into the main TLS
2325 for (const NBEdge* outEdge : outgoing) {
2326 NBNode* cand = outEdge->getToNode();
2327 if (cand->isTLControlled() && cand->geometryLike() && outEdge->getLength() <= signalDist) {
2328#ifdef DEBUG_GUESSSIGNALS
2329 if (DEBUGCOND(node)) {
2330 std::cout << " node=" << node->getID() << " outEdge=" << outEdge->getID() << " signalNode=" << cand->getID() << " len=" << outEdge->getLength() << "\n";
2331 }
2332#endif
2333 signals.push_back(cand);
2334 }
2335 }
2336 }
2337 if (foundSignalsAtDist > 1 && missingSignals <= slack && missingSignals < foundSignalsAtDist) {
2338 for (const NBNode* s : signals) {
2339 std::set<NBTrafficLightDefinition*> tls = s->getControllingTLS();
2340 const_cast<NBNode*>(s)->reinit(s->getPosition(), SumoXMLNodeType::PRIORITY);
2341 for (std::set<NBTrafficLightDefinition*>::iterator k = tls.begin(); k != tls.end(); ++k) {
2342 tlc.removeFully(s->getID());
2343 }
2344 }
2345 //if (true) std::cout << " node=" << node->getID() << " signals=" << toString(signals) << "\n";
2346 NBTrafficLightDefinition* tlDef = new NBOwnTLDef("GS_" + node->getID(), node, 0, type);
2347 // @todo patch endOffset for all incoming lanes according to the signal positions
2348 if (!tlc.insert(tlDef)) {
2349 // actually, nothing should fail here
2350 WRITE_WARNINGF(TL("Could not build joined tls '%'."), node->getID());
2351 delete tlDef;
2352 return;
2353 }
2354 }
2355 }
2356 }
2357 }
2358
2359 // guess joined tls first, if wished
2360 if (oc.getBool("tls.guess.joining")) {
2361 // get node clusters
2362 NodeClusters cands;
2363 generateNodeClusters(oc.getFloat("tls.join-dist"), cands);
2364 // check these candidates (clusters) whether they should be controlled by a tls
2365 for (NodeClusters::iterator i = cands.begin(); i != cands.end();) {
2366 NodeSet& c = (*i);
2367 // regard only junctions which are not yet controlled and are not
2368 // forbidden to be controlled
2369 for (NodeSet::iterator j = c.begin(); j != c.end();) {
2370 if ((*j)->isTLControlled() || myUnsetTLS.count(*j) != 0) {
2371 c.erase(j++);
2372 } else {
2373 ++j;
2374 }
2375 }
2376 // check whether the cluster should be controlled
2377 // to avoid gigantic clusters, assume that at most 4 nodes should be needed for a guessed-joined-tls
2378 if (c.size() == 0 || !shouldBeTLSControlled(c, laneSpeedThreshold * (double)c.size() / MIN2((double)c.size(), 4.))) {
2379 i = cands.erase(i);
2380 } else {
2381 ++i;
2382 }
2383 }
2384 // cands now only contain sets of junctions that shall be joined into being tls-controlled
2385 for (auto nodeSet : cands) {
2386 std::vector<NBNode*> nodes;
2387 for (NBNode* node : nodeSet) {
2388 nodes.push_back(node);
2389 myGuessedTLS.insert(node);
2390 }
2391 const std::string& id = createClusterId(nodeSet, "joinedG_");
2392 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2393 if (!tlc.insert(tlDef)) {
2394 // actually, nothing should fail here
2395 WRITE_WARNING(TL("Could not build guessed, joined tls."));
2396 delete tlDef;
2397 return;
2398 }
2399 }
2400 }
2401
2402 // guess single tls
2403 if (oc.getBool("tls.guess")) {
2404 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2405 NBNode* cur = (*i).second;
2406 // do nothing if already is tl-controlled
2407 if (cur->isTLControlled()) {
2408 continue;
2409 }
2410 // do nothing if in the list of explicit non-controlled junctions
2411 if (myUnsetTLS.count(cur) != 0) {
2412 continue;
2413 }
2414 NodeSet c;
2415 c.insert(cur);
2416 if (!shouldBeTLSControlled(c, laneSpeedThreshold) || cur->geometryLike()) {
2417 continue;
2418 }
2419 setAsTLControlled(cur, tlc, type);
2420 myGuessedTLS.insert(cur);
2421 }
2422 }
2423}
2424
2425
2426void
2428 std::set<NBTrafficLightDefinition*> recompute;
2429 for (NBNode* node : myGuessedTLS) {
2430 if (!node->hasConflict() || !recheckTLSThreshold(node)) {
2431 const std::set<NBTrafficLightDefinition*>& tlDefs = node->getControllingTLS();
2432 recompute.insert(tlDefs.begin(), tlDefs.end());
2433 node->removeTrafficLights(true);
2434 for (NBEdge* edge : node->getIncomingEdges()) {
2435 edge->clearControllingTLInformation();
2436 }
2437 }
2438 }
2439 for (NBTrafficLightDefinition* def : recompute) {
2440 if (def->getNodes().size() == 0) {
2441 tlc.removeFully(def->getID());
2442 } else {
2443 def->setParticipantsInformation();
2444 def->setTLControllingInformation();
2446 }
2447 }
2448}
2449
2450
2451bool
2453 if (!node->isTLControlled()) {
2454 return false;
2455 }
2456 if ((*node->getControllingTLS().begin())->getNodes().size() != 1) {
2457 // unable to perform check for a joined tls
2458 return true;
2459 }
2460 NodeSet c;
2461 c.insert(node);
2462 const double laneSpeedThreshold = OptionsCont::getOptions().getFloat("tls.guess.threshold");
2463 return shouldBeTLSControlled(c, laneSpeedThreshold, true);
2464}
2465
2466
2467void
2469 for (const auto& item : myNodes) {
2470 item.second->computeKeepClear();
2471 }
2472}
2473
2474
2475void
2477 const std::vector<std::string> excludeList = OptionsCont::getOptions().getStringVector("tls.join-exclude");
2478 for (const std::string& tlsID : excludeList) {
2479 if (!tlc.exist(tlsID, false)) {
2480 WRITE_WARNINGF("Unknown tls ID '%' in option tls.join-exclude", tlsID);
2481 }
2482 }
2483 std::set<std::string> exclude(excludeList.begin(), excludeList.end());
2484 NodeClusters cands;
2485 generateNodeClusters(maxdist, cands);
2486 for (NodeSet& c : cands) {
2487 for (NodeSet::iterator j = c.begin(); j != c.end();) {
2488 if (!(*j)->isTLControlled() || exclude.count((*(*j)->getControllingTLS().begin())->getID()) != 0) {
2489 c.erase(j++);
2490 } else {
2491 ++j;
2492 }
2493 }
2494 if (c.size() < 2 || onlyCrossings(c) || customTLID(c)) {
2495 continue;
2496 }
2497 // figure out type of the joined TLS
2498 Position dummyPos;
2499 bool dummySetTL = false;
2500 std::string id = "joinedS_"; // prefix (see #3871)
2501 TrafficLightType type;
2503 analyzeCluster(c, id, dummyPos, dummySetTL, type, nodeType);
2504 for (NBNode* j : c) {
2505 std::set<NBTrafficLightDefinition*> tls = j->getControllingTLS();
2506 j->removeTrafficLights();
2507 for (NBTrafficLightDefinition* k : tls) {
2508 tlc.removeFully(k->getID());
2509 }
2510 }
2511 std::vector<NBNode*> nodes;
2512 for (NBNode* j : c) {
2513 nodes.push_back(j);
2514 }
2515 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, nodes, 0, type);
2516 if (!tlc.insert(tlDef)) {
2517 // actually, nothing should fail here
2518 WRITE_WARNING(TL("Could not build a joined tls."));
2519 delete tlDef;
2520 return;
2521 }
2522 }
2523}
2524
2525
2526void
2528 TrafficLightType type, std::string id) {
2529 if (id == "") {
2530 id = node->getID();
2531 }
2532 NBTrafficLightDefinition* tlDef = new NBOwnTLDef(id, node, 0, type);
2533 if (!tlc.insert(tlDef)) {
2534 // actually, nothing should fail here
2535 WRITE_WARNINGF(TL("Building a tl-logic for junction '%' twice is not possible."), id);
2536 delete tlDef;
2537 return;
2538 }
2539}
2540
2541
2542// -----------
2543void
2545 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2546 (*i).second->computeLanes2Lanes();
2547 }
2548}
2549
2550
2551// computes the "wheel" of incoming and outgoing edges for every node
2552void
2554 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2555 (*i).second->computeLogic(ec);
2556 }
2557}
2558
2559
2560void
2562 std::set<NBNode*> roundaboutNodes;
2563 const bool checkLaneFoesAll = oc.getBool("check-lane-foes.all");
2564 const bool checkLaneFoesRoundabout = !checkLaneFoesAll && oc.getBool("check-lane-foes.roundabout");
2565 if (checkLaneFoesRoundabout) {
2566 const std::set<EdgeSet>& roundabouts = ec.getRoundabouts();
2567 for (std::set<EdgeSet>::const_iterator i = roundabouts.begin(); i != roundabouts.end(); ++i) {
2568 for (EdgeSet::const_iterator j = (*i).begin(); j != (*i).end(); ++j) {
2569 roundaboutNodes.insert((*j)->getToNode());
2570 }
2571 }
2572 }
2573 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2574 const bool checkLaneFoes = checkLaneFoesAll || (checkLaneFoesRoundabout && roundaboutNodes.count((*i).second) > 0);
2575 (*i).second->computeLogic2(checkLaneFoes);
2576 }
2577}
2578
2579
2580void
2582 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2583 delete ((*i).second);
2584 }
2585 myNodes.clear();
2586 for (auto& item : myExtractedNodes) {
2587 delete item.second;
2588 }
2589 myExtractedNodes.clear();
2590}
2591
2592
2593std::string
2595 int counter = 0;
2596 std::string freeID = "SUMOGenerated" + toString<int>(counter);
2597 // While there is a node with id equal to freeID
2598 while (retrieve(freeID) != nullptr) {
2599 // update counter and generate a new freeID
2600 counter++;
2601 freeID = "SUMOGenerated" + toString<int>(counter);
2602 }
2603 return freeID;
2604}
2605
2606
2607void
2608NBNodeCont::computeNodeShapes(double mismatchThreshold) {
2609 for (NodeCont::iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2610 (*i).second->computeNodeShape(mismatchThreshold);
2611 }
2612}
2613
2614
2615void
2617 WRITE_MESSAGE(TL("-----------------------------------------------------"));
2618 WRITE_MESSAGE(TL("Summary:"));
2619
2620 int numUnregulatedJunctions = 0;
2621 int numDeadEndJunctions = 0;
2622 int numTrafficLightJunctions = 0;
2623 int numPriorityJunctions = 0;
2624 int numRightBeforeLeftJunctions = 0;
2625 int numLeftBeforeRightJunctions = 0;
2626 int numAllWayStopJunctions = 0;
2627 int numZipperJunctions = 0;
2628 int numDistrictJunctions = 0;
2629 int numRailCrossing = 0;
2630 int numRailSignals = 0;
2631 for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); i++) {
2632 switch ((*i).second->getType()) {
2634 ++numUnregulatedJunctions;
2635 break;
2637 ++numDeadEndJunctions;
2638 break;
2642 ++numTrafficLightJunctions;
2643 break;
2646 ++numPriorityJunctions;
2647 break;
2649 ++numRightBeforeLeftJunctions;
2650 break;
2652 ++numLeftBeforeRightJunctions;
2653 break;
2655 ++numAllWayStopJunctions;
2656 break;
2658 ++numZipperJunctions;
2659 break;
2661 ++numDistrictJunctions;
2662 break;
2664 ++numRailCrossing;
2665 break;
2667 ++numRailSignals;
2668 break;
2670 // should not happen
2671 break;
2672 default:
2673 break;
2674 }
2675 }
2676 WRITE_MESSAGE(TL(" Node type statistics:"));
2677 WRITE_MESSAGE(" Unregulated junctions : " + toString(numUnregulatedJunctions));
2678 if (numDeadEndJunctions > 0) {
2679 WRITE_MESSAGE(" Dead-end junctions : " + toString(numDeadEndJunctions));
2680 }
2681 WRITE_MESSAGE(" Priority junctions : " + toString(numPriorityJunctions));
2682 WRITE_MESSAGE(" Right-before-left junctions : " + toString(numRightBeforeLeftJunctions));
2683 if (numLeftBeforeRightJunctions > 0) {
2684 WRITE_MESSAGE(" Left-before-right junctions : " + toString(numLeftBeforeRightJunctions));
2685 }
2686 if (numTrafficLightJunctions > 0) {
2687 WRITE_MESSAGE(" Traffic light junctions : " + toString(numTrafficLightJunctions));
2688 }
2689 if (numAllWayStopJunctions > 0) {
2690 WRITE_MESSAGE(" All-way stop junctions : " + toString(numAllWayStopJunctions));
2691 }
2692 if (numZipperJunctions > 0) {
2693 WRITE_MESSAGE(" Zipper-merge junctions : " + toString(numZipperJunctions));
2694 }
2695 if (numRailCrossing > 0) {
2696 WRITE_MESSAGE(" Rail crossing junctions : " + toString(numRailCrossing));
2697 }
2698 if (numRailSignals > 0) {
2699 WRITE_MESSAGE(" Rail signal junctions : " + toString(numRailSignals));
2700 }
2701 if (numDistrictJunctions > 0) {
2702 WRITE_MESSAGE(" District junctions : " + toString(numDistrictJunctions));
2703 }
2704 const GeoConvHelper& geoConvHelper = GeoConvHelper::getProcessing();
2705 WRITE_MESSAGE(TL(" Network boundaries:"));
2706 WRITE_MESSAGE(" Original boundary : " + toString(geoConvHelper.getOrigBoundary()));
2707 WRITE_MESSAGE(" Applied offset : " + toString(geoConvHelper.getOffsetBase()));
2708 WRITE_MESSAGE(" Converted boundary : " + toString(geoConvHelper.getConvBoundary()));
2709 WRITE_MESSAGE(TL("-----------------------------------------------------"));
2710}
2711
2712
2713std::vector<std::string>
2715 std::vector<std::string> ret;
2716 for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2717 ret.push_back((*i).first);
2718 }
2719 return ret;
2720}
2721
2722
2723void
2724NBNodeCont::addPrefix(const std::string& prefix) {
2725 // make a copy of node containers
2726 const auto nodeContainerCopy = myNodes;
2727 myNodes.clear();
2728 for (const auto& node : nodeContainerCopy) {
2729 node.second->setID(prefix + node.second->getID());
2730 myNodes[node.second->getID()] = node.second;
2731 }
2732}
2733
2734
2735void
2736NBNodeCont::rename(NBNode* node, const std::string& newID) {
2737 if (myNodes.count(newID) != 0) {
2738 throw ProcessError(TLF("Attempt to rename node using existing id '%'", newID));
2739 }
2740 myNodes.erase(node->getID());
2741 node->setID(newID);
2742 myNodes[newID] = node;
2743}
2744
2745
2746void
2748 for (NodeCont::const_iterator i = myNodes.begin(); i != myNodes.end(); ++i) {
2749 NBNode* node = i->second;
2750 if (node->isTLControlled() && (!geometryLike || node->geometryLike())) {
2751 // make a copy of tldefs
2752 const std::set<NBTrafficLightDefinition*> tldefs = node->getControllingTLS();
2753 if (geometryLike && (*tldefs.begin())->getNodes().size() > 1) {
2754 // do not remove joined tls when only removing geometry-like tls
2755 continue;
2756 }
2757 if (node->getCrossings().size() > 0) {
2758 // keep controlled pedestrian crossings
2759 continue;
2760 }
2761 // record signal location
2762 for (NBEdge* edge : node->getOutgoingEdges()) {
2763 edge->setSignalPosition(node->getPosition(), nullptr);
2764#ifdef DEBUG_GUESSSIGNALS
2765 std::cout << " discard-simple " << node->getID() << " edge=" << edge->getID() << " pos=" << edge->getSignalPosition() << "\n";
2766#endif
2767 }
2768 for (std::set<NBTrafficLightDefinition*>::const_iterator it = tldefs.begin(); it != tldefs.end(); ++it) {
2769 NBTrafficLightDefinition* tlDef = *it;
2770 node->removeTrafficLight(tlDef);
2771 tlc.extract(tlDef);
2772 }
2774 node->reinit(node->getPosition(), newType);
2775 }
2776 }
2777}
2778
2779
2780void
2782 for (auto& item : myNodes) {
2783 NBNode* node = item.second;
2784 if (node->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
2786 }
2787 }
2788}
2789
2790
2791int
2792NBNodeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBTrafficLightLogicCont& tlc) {
2793 bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.node-start");
2794 if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
2795 return 0;
2796 }
2797 std::vector<std::string> avoid;
2798 if (startGiven) {
2799 avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.node-start") - 1));
2800 } else {
2801 avoid = getAllNames();
2802 }
2803 std::set<std::string> reserve;
2804 if (reservedIDs) {
2805 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "node:", reserve); // backward compatibility
2806 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "junction:", reserve); // selection format
2807 avoid.insert(avoid.end(), reserve.begin(), reserve.end());
2808 }
2809 IDSupplier idSupplier("", avoid);
2810 NodeSet toChange;
2811 for (NodeCont::iterator it = myNodes.begin(); it != myNodes.end(); it++) {
2812 if (startGiven) {
2813 toChange.insert(it->second);
2814 continue;
2815 }
2816 if (numericaIDs) {
2817 try {
2818 StringUtils::toLong(it->first);
2819 } catch (NumberFormatException&) {
2820 toChange.insert(it->second);
2821 }
2822 }
2823 if (reservedIDs && reserve.count(it->first) > 0) {
2824 toChange.insert(it->second);
2825 }
2826 }
2827 std::set<std::string> keep;
2828 if (keptIDs) {
2829 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "node:", keep); // backward compatibility
2830 NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "junction:", keep); // selection format
2831 for (auto it = toChange.begin(); it != toChange.end();) {
2832 if (keep.count((*it)->getID()) != 0) {
2833 it = toChange.erase(it++);
2834 } else {
2835 it++;
2836 }
2837 }
2838 }
2839 const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
2840 for (NBNode* node : toChange) {
2841 myNodes.erase(node->getID());
2842 }
2843 for (NBNode* node : toChange) {
2844 if (origNames && node->getParameter(SUMO_PARAM_ORIGID) == "") {
2845 node->setParameter(SUMO_PARAM_ORIGID, node->getID());
2846 }
2847 node->setID(idSupplier.getNext());
2848 for (NBTrafficLightDefinition* tlDef : node->getControllingTLS()) {
2849 tlc.rename(tlDef, node->getID());
2850 }
2851 myNodes[node->getID()] = node;
2852 }
2853 if (prefix.empty()) {
2854 return (int)toChange.size();
2855 } else {
2856 int renamed = 0;
2857 // make a copy because we will modify the map
2858 auto oldNodes = myNodes;
2859 for (auto item : oldNodes) {
2860 if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
2861 rename(item.second, prefix + item.first);
2862 for (NBTrafficLightDefinition* tlDef : item.second->getControllingTLS()) {
2863 if (!StringUtils::startsWith(tlDef->getID(), prefix)) {
2864 tlc.rename(tlDef, prefix + tlDef->getID());
2865 }
2866 }
2867 renamed++;
2868 }
2869 }
2870 return renamed;
2871 }
2872}
2873
2874
2875int
2877 // guess outer fringe by topology and being on the pareto-boundary
2878 NodeSet topRightFront;
2879 NodeSet topLeftFront;
2880 NodeSet bottomRightFront;
2881 NodeSet bottomLeftFront;
2882 for (const auto& item : myNodes) {
2883 paretoCheck(item.second, topRightFront, 1, 1);
2884 paretoCheck(item.second, topLeftFront, -1, 1);
2885 paretoCheck(item.second, bottomRightFront, 1, -1);
2886 paretoCheck(item.second, bottomLeftFront, -1, -1);
2887 }
2888 NodeSet front;
2889 front.insert(topRightFront.begin(), topRightFront.end());
2890 front.insert(topLeftFront.begin(), topLeftFront.end());
2891 front.insert(bottomRightFront.begin(), bottomRightFront.end());
2892 front.insert(bottomLeftFront.begin(), bottomLeftFront.end());
2893 int numFringe = 0;
2894 for (NBNode* n : front) {
2895 const int in = (int)n->getIncomingEdges().size();
2896 const int out = (int)n->getOutgoingEdges().size();
2897 if ((in <= 1 && out <= 1) &&
2898 (in == 0 || out == 0
2899 || n->getIncomingEdges().front()->isTurningDirectionAt(n->getOutgoingEdges().front()))) {
2900 n->setFringeType(FringeType::OUTER);
2901 numFringe++;
2902 }
2903 }
2904 // guess outer fringe by topology and speed
2905 const double speedThreshold = OptionsCont::getOptions().getFloat("fringe.guess.speed-threshold");
2906 for (const auto& item : myNodes) {
2907 NBNode* n = item.second;
2908 if (front.count(n) != 0) {
2909 continue;
2910 }
2911 if (n->getEdges().size() == 1 && n->getEdges().front()->getSpeed() > speedThreshold) {
2913 numFringe++;
2914 }
2915 }
2916 return numFringe;
2917}
2918
2919
2920void
2921NBNodeCont::paretoCheck(NBNode* node, NodeSet& frontier, int xSign, int ySign) {
2922 const double x = node->getPosition().x() * xSign;
2923 const double y = node->getPosition().y() * ySign;
2924 std::vector<NBNode*> dominated;
2925 for (NBNode* fn : frontier) {
2926 const double x2 = fn->getPosition().x() * xSign;
2927 const double y2 = fn->getPosition().y() * ySign;
2928 if (x2 >= x && y2 >= y) {
2929 return;
2930 } else if (x2 <= x && y2 <= y) {
2931 dominated.push_back(fn);
2932 }
2933 }
2934 frontier.insert(node);
2935 for (NBNode* r : dominated) {
2936 frontier.erase(r);
2937 }
2938}
2939
2940
2941void
2943 for (const auto& item : myNodes) {
2944 NBNode* n = item.second;
2945 if (n->isTLControlled() && n->getRightOfWay() == RightOfWay::DEFAULT) {
2946 bool hasNEMA = false;
2948 if (tl->getType() == TrafficLightType::NEMA) {
2949 hasNEMA = true;
2950 break;
2951 }
2952 }
2953 if (hasNEMA) {
2954 // NEMA controller defaults to allway_stop behavior when switched off
2956 }
2957 }
2958 }
2959}
2960
2961
2962bool
2964 bool hadShapes = false;
2965 for (const auto& item : myNodes) {
2966 if (item.second->getShape().size() > 0 && !item.second->hasCustomShape()) {
2967 hadShapes = true;
2968 item.second->resetShape();
2969 }
2970 }
2971 return hadShapes;
2972}
2973/****************************************************************************/
#define DEBUGCOND(PED)
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:288
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:290
#define WRITE_ERRORF(...)
Definition MsgHandler.h:297
#define WRITE_MESSAGE(msg)
Definition MsgHandler.h:289
#define WRITE_WARNING(msg)
Definition MsgHandler.h:287
#define TL(string)
Definition MsgHandler.h:305
#define TLF(string,...)
Definition MsgHandler.h:307
std::set< NBNode *, ComparatorIdLess > NodeSet
Definition NBCont.h:52
std::set< NBEdge * > EdgeSet
container for unique edges
Definition NBCont.h:50
std::vector< NBEdge * > EdgeVector
container for (sorted) edges
Definition NBCont.h:42
#define MAX_SLIPLANE_LENGTH
bool isForVulnerableModes(SVCPermissions permissions)
Returns whether an edge with the given permissions allows only vulnerable road users.
bool isRailway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a (exclusive) railway edge.
const SVCPermissions SVC_UNSPECIFIED
permissions not specified
bool isWaterway(SVCPermissions permissions)
Returns whether an edge with the given permissions is a waterway edge.
const std::string & getVehicleClassNames(SVCPermissions permissions, bool expand)
Returns the ids of the given classes, divided using a ' '.
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
@ SVC_RAIL_CLASSES
classes which drive on tracks
@ SVC_PASSENGER
vehicle is a passenger car (a "normal" car)
@ SVC_DELIVERY
vehicle is a small delivery vehicle
@ SVC_VULNERABLE
@ SVC_TRAM
vehicle is a light rail
@ SVC_PEDESTRIAN
pedestrian
const std::string SUMO_PARAM_ORIGID
SumoXMLNodeType
Numbers representing special SUMO-XML-attribute values for representing node- (junction-) types used ...
bool gDebugFlag1
global utility flags for debugging
Definition StdDefs.cpp:40
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:326
std::string joinToString(const std::vector< T > &v, const T_BETWEEN &between, std::streamsize accuracy=gPrecision)
Definition ToString.h:283
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition ToString.h:46
static methods for processing the coordinates conversion for the current net
const Boundary & getOrigBoundary() const
Returns the original boundary.
static GeoConvHelper & getProcessing()
the coordinate transformation to use for input conversion and processing
const Position getOffsetBase() const
Returns the network base.
const Boundary & getConvBoundary() const
Returns the converted boundary.
std::string getNext()
Returns the next id.
A container for districts.
A class representing a single district.
Definition NBDistrict.h:62
Storage for edges, including some functionality operating on multiple edges.
Definition NBEdgeCont.h:59
NBEdge * getByID(const std::string &edgeID) const
Returns the edge with id if it exists.
const std::set< EdgeSet > getRoundabouts() const
Returns the determined roundabouts.
std::map< std::string, NBEdge * >::const_iterator begin() const
Returns the pointer to the begin of the stored edges.
Definition NBEdgeCont.h:171
void extract(NBDistrictCont &dc, NBEdge *edge, bool remember=false)
Removes the given edge from the container like erase but does not delete it.
void erase(NBDistrictCont &dc, NBEdge *edge)
Removes the given edge from the container (deleting it)
NBEdge * retrieve(const std::string &id, bool retrieveExtracted=false) const
Returns the edge that has the given id.
std::map< std::string, NBEdge * >::const_iterator end() const
Returns the pointer to the end of the stored edges.
Definition NBEdgeCont.h:178
bool hasPostProcessConnection(const std::string &from, const std::string &to="")
add post process connections
void removeRoundaboutEdges(const EdgeSet &toRemove)
remove edges from all stored roundabouts
void joinSameNodeConnectingEdges(NBDistrictCont &dc, NBTrafficLightLogicCont &tlc, EdgeVector edges)
Joins the given edges because they connect the same nodes.
std::vector< std::string > getAllNames() const
Returns all ids of known edges.
The representation of a single edge during network building.
Definition NBEdge.h:92
double getLength() const
Returns the computed length of the edge.
Definition NBEdge.h:599
SVCPermissions getPermissions(int lane=-1) const
get the union of allowed classes over all lanes or for a specific lane
Definition NBEdge.cpp:4523
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:4999
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:5010
bool isNearEnough2BeJoined2(NBEdge *e, double threshold) const
Check if edge is near enought to be joined to another edge.
Definition NBEdge.cpp:4167
@ 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:1540
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:4652
@ 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:2181
double getSignalOffset() const
Returns the offset of a traffic signal from the end of this edge.
Definition NBEdge.cpp:4570
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:1007
void append(NBEdge *continuation)
append another edge
Definition NBEdge.cpp:4076
static void loadPrefixedIDsFomFile(const std::string &file, const std::string prefix, std::set< std::string > &into)
Add prefixed ids defined in file.
static double relAngle(double angle1, double angle2)
computes the relative angle between the two angles
Definition NBHelpers.cpp:45
static void loadEdgesFromFile(const std::string &file, std::set< std::string > &into)
Add edge ids defined in file (either ID or edge:ID per line) into the given set.
Definition NBHelpers.cpp:86
void clear()
deletes all nodes
std::set< std::string > myJoinExclusions
set of node ids which should not be joined
Definition NBNodeCont.h:452
std::vector< std::vector< std::string > > myRailComponents
network components that must be removed if not connected to the road network via stop access
Definition NBNodeCont.h:476
static double getDiameter(const NodeSet &cluster)
compute the maximum distance between any two cluster nodes
NamedRTree myRTree
node positions for faster lookup
Definition NBNodeCont.h:473
void avoidOverlap()
fix overlap
int removeRailComponents(NBDistrictCont &dc, NBEdgeCont &ec, NBPTStopCont &sc)
bool onlyCrossings(const NodeSet &c) const
check wheter the set of nodes only contains pedestrian crossings
std::vector< std::pair< std::set< std::string >, NBNode * > > myClusters2Join
loaded sets of node ids to join (cleared after use)
Definition NBNodeCont.h:455
std::string createClusterId(const NodeSet &cluster, const std::string &prefix="cluster_")
generate id from cluster node ids
Definition NBNodeCont.h:136
std::map< std::string, NBNode * >::const_iterator begin() const
Returns the pointer to the begin of the stored nodes.
Definition NBNodeCont.h:113
void recheckGuessedTLS(NBTrafficLightLogicCont &tlc)
recheck myGuessedTLS after node logics are computed
std::vector< NodeSet > NodeClusters
Definition of a node cluster container.
Definition NBNodeCont.h:61
void computeKeepClear()
compute keepClear status for all connections
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:446
bool reduceToCircle(NodeSet &cluster, int circleSize, NodeSet startNodes, double maxDist, std::vector< NBNode * > cands=std::vector< NBNode * >()) const
try to find a joinable subset (recursively)
void registerJoinedCluster(const NodeSet &cluster)
gets all joined clusters (see doc for myClusters2Join)
std::string getFreeID()
generates a new node ID
int removeSelfLoops(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tc)
Removes self-loop edges (edges where the source and the destination node are the same)
bool recheckTLSThreshold(NBNode *node)
check whether a specific guessed tls should keep its type
void paretoCheck(NBNode *node, NodeSet &frontier, int xSign, int ySign)
update pareto frontier with the given node
bool maybeSlipLaneStart(const NBNode *n, EdgeVector &outgoing, double &inAngle) const
check whether the given node maybe the start of a slip lane
void addJoinExclusion(const std::vector< std::string > &ids)
bool erase(NBNode *node)
Removes the given node, deleting it.
int joinLoadedClusters(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc)
Joins loaded junction clusters (see NIXMLNodesHandler)
static bool geometryLikeForClass(const NBNode *n, SVCPermissions permissions)
check whether the node is geometryLike when only considering edges that support the given permissions
void applyConditionalDefaults()
apply default values after loading
void pruneSlipLaneNodes(NodeSet &cluster, double maxDist) const
remove nodes that form a slip lane from cluster
int remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string &prefix, NBTrafficLightLogicCont &tlc)
remap node IDs according to options –numerical-ids and –reserved-ids
bool insert(const std::string &id, const Position &position, NBDistrict *district=0)
Inserts a node into the map.
std::set< const NBNode * > myUnsetTLS
nodes that are excluded from tls-guessing
Definition NBNodeCont.h:470
NBNode * retrieve(const std::string &id) const
Returns the node with the given name.
NodeCont myExtractedNodes
The extracted nodes which are kept for reference.
Definition NBNodeCont.h:449
void joinTLS(NBTrafficLightLogicCont &tlc, double maxdist)
Builds clusters of tls-controlled junctions and joins the control if possible.
int removeUnwishedNodes(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, NBPTStopCont &sc, NBPTLineCont &lc, NBParkingCont &pc, bool removeGeometryNodes)
Removes "unwished" nodes.
~NBNodeCont()
Destructor.
bool extract(NBNode *node, bool remember=false)
Removes the given node but does not delete it.
void pruneClusterFringe(NodeSet &cluster, double maxDist, bool remove2TLS=false) const
remove geometry-like fringe nodes from cluster
int removeComponents(NBDistrictCont &dc, NBEdgeCont &ec, const int numKeep, bool hasPTStops)
Checks the network for weak connectivity and removes all but the largest components....
std::vector< std::string > getAllNames() const
get all node names
void computeLogics2(const NBEdgeCont &ec, OptionsCont &oc)
compute right-of-way logic for all lane-to-lane connections
bool shouldBeTLSControlled(const NodeSet &c, double laneSpeedThreshold, bool recheck=false) const
Returns whethe the given node cluster should be controlled by a tls.
void rename(NBNode *node, const std::string &newID)
Renames the node. Throws exception if newID already exists.
void joinSimilarEdges(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, bool removeDuplicates)
Joins edges connecting the same nodes.
int removeIsolatedRoads(NBDistrictCont &dc, NBEdgeCont &ec)
Removes sequences of edges that are not connected with a junction. Simple roads without junctions som...
void joinNodeClusters(NodeClusters clusters, NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, bool resetConnections=false)
joins the given node clusters
void discardRailSignals()
discards rail signals
void addPrefix(const std::string &prefix)
add prefix to all nodes
void printBuiltNodesStatistics() const
Prints statistics about built nodes.
void setAsTLControlled(NBNode *node, NBTrafficLightLogicCont &tlc, TrafficLightType type, std::string id="")
Sets the given node as being controlled by a tls.
std::set< const NBNode * > mySplit
nodes that were created when splitting an edge
Definition NBNodeCont.h:464
static NodeSet getClusterNeighbors(const NBNode *n, double longThreshold, NodeSet &cluster)
return all cluster neighbors for the given node
void computeLogics(const NBEdgeCont &ec)
build the list of outgoing edges and lanes
void joinNodeCluster(NodeSet clusters, NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tlc, NBNode *predefined=nullptr, bool resetConnections=false)
void unregisterJoinedCluster(const std::set< std::string > &cluster)
remove cluster from list (on netedit-undo)
void generateNodeClusters(double maxDist, NodeClusters &into) const
Builds node clusters.
static bool isSlipLaneContinuation(const NBNode *cont)
whether the given node may continue a slip lane
void computeNodeShapes(double mismatchThreshold=-1)
Compute the junction shape for this node.
std::vector< std::set< std::string > > myJoinedClusters
sets of node ids which were joined
Definition NBNodeCont.h:458
NBEdge * shortestEdge(const NodeSet &cluster, const NodeSet &startNodes, const std::vector< NBNode * > &exclude) const
find closest neighbor for building circle
std::pair< NBNode *, double > NodeAndDist
Definition NBNodeCont.h:62
void guessTLs(OptionsCont &oc, NBTrafficLightLogicCont &tlc)
Guesses which junctions or junction clusters shall be controlled by tls.
bool feasibleCluster(const NodeSet &cluster, const std::map< const NBNode *, std::vector< NBNode * > > &ptStopEnds, double maxDist, std::string &reason, NBNode *&tryRemove) const
determine wether the cluster is not too complex for joining
int 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 discardTrafficLights(NBTrafficLightLogicCont &tlc, bool geometryLike)
std::set< NBNode *, ComparatorIdLess > myGuessedTLS
nodes that received a traffic light due to guessing (–tls.guess)
Definition NBNodeCont.h:467
std::set< std::string > myJoined
ids found in loaded join clusters used for error checking
Definition NBNodeCont.h:461
void analyzeCluster(NodeSet cluster, std::string &id, Position &pos, bool &hasTLS, TrafficLightType &type, SumoXMLNodeType &nodeType)
void addCluster2Join(const std::set< std::string > &cluster, NBNode *node)
add ids of nodes which shall be joined into a single node
bool customTLID(const NodeSet &c) const
check wheter the set of nodes contains traffic lights with custom id
bool resetNodeShapes()
reset all node shapes
static int pruneLongEdges(NodeSet &cluster, double maxDist, const bool dryRun=false)
avoid removal of long edges when joining junction clusters
bool maybeSlipLaneEnd(const NBNode *n, EdgeVector &incoming, double &outAngle) const
check whether the given node maybe the end of a slip lane
Represents a single node (junction) during network building.
Definition NBNode.h:66
bool hasIncoming(const NBEdge *const e) const
Returns whether the given edge ends at this node.
Definition NBNode.cpp:1983
RightOfWay getRightOfWay() const
Returns hint on how to compute right of way.
Definition NBNode.h:300
const std::set< NBTrafficLightDefinition * > & getControllingTLS() const
Returns the traffic lights that were assigned to this node (The set of tls that control this node)
Definition NBNode.h:340
void reinit(const Position &position, SumoXMLNodeType type, bool updateEdgeGeometries=false)
Resets initial values.
Definition NBNode.cpp:345
SumoXMLNodeType getType() const
Returns the type of this node.
Definition NBNode.h:285
void setRightOfWay(RightOfWay rightOfWay)
set method for computing right-of-way
Definition NBNode.h:576
static bool isTrafficLight(SumoXMLNodeType type)
return whether the given type is a traffic light
Definition NBNode.cpp:4269
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:2736
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:435
int removeSelfLoops(NBDistrictCont &dc, NBEdgeCont &ec, NBTrafficLightLogicCont &tc)
Removes edges which are both incoming and outgoing into this node.
Definition NBNode.cpp:489
std::vector< Crossing * > getCrossings() const
return this junctions pedestrian crossings
Definition NBNode.cpp:3087
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:1877
EdgeVector getPassengerEdges(bool incoming) const
return edges that permit passengers (either incoming or outgoing)
Definition NBNode.cpp:2476
const Position & getPosition() const
Definition NBNode.h:260
void removeTrafficLight(NBTrafficLightDefinition *tlDef)
Removes the given traffic light from this node.
Definition NBNode.cpp:428
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:1174
bool checkIsRemovable() const
check if node is removable
Definition NBNode.cpp:2653
void setFringeType(FringeType fringeType)
set method for computing right-of-way
Definition NBNode.h:581
bool geometryLike() const
whether this is structurally similar to a geometry node
Definition NBNode.cpp:4026
bool isNearDistrict() const
@chech if node is near district
Definition NBNode.cpp:2801
bool isTLControlled() const
Returns whether this node is controlled by any tls.
Definition NBNode.h:331
static bool isRailwayNode(const NBNode *n)
whether the given node only has rail edges
A traffic light logics which must be computed (only nodes/edges are given)
Definition NBOwnTLDef.h:44
void replaceEdge(const std::string &edgeID, const EdgeVector &replacement)
replace the edge with the given edge list in all lines
Container for public transport stops during the net building process.
void replaceEdge(const std::string &edgeID, const std::vector< NBEdge * > &replacement)
replace the edge with the closes edge on the given edge list in all stops
const std::map< std::string, std::shared_ptr< NBPTStop > > & getStops() const
Returns an unmodifiable reference to the stored pt stops.
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
void addEdges2Keep(const OptionsCont &oc, std::set< std::string > &into)
add edges that must be kept
Definition NBParking.cpp:78
The base class for traffic light logic definitions.
const std::vector< NBNode * > & getNodes() const
Returns the list of controlled nodes.
A container for traffic light definitions and built programs.
bool exist(const std::string &newID, bool requireComputed=true) const
check if exists a definition with the given ID
void rename(NBTrafficLightDefinition *tlDef, const std::string &newID)
rename traffic light
bool computeSingleLogic(OptionsCont &oc, NBTrafficLightDefinition *def)
Computes a specific traffic light logic (using by netedit)
bool removeFully(const std::string id)
Removes a logic definition (and all programs) from the dictionary.
bool insert(NBTrafficLightDefinition *logic, bool forceInsert=false)
Adds a logic definition to the dictionary.
void extract(NBTrafficLightDefinition *definition)
Extracts a traffic light definition from myDefinitions but keeps it in myExtracted for eventual * del...
Allows to store the object; used as context while traveling the rtree in TraCI.
Definition Named.h:90
Base class for objects which have an id.
Definition Named.h:54
virtual void setID(const std::string &newID)
resets the id
Definition Named.h:82
const std::string & getID() const
Returns the id.
Definition Named.h:74
void Remove(const float a_min[2], const float a_max[2], Named *const &a_data)
Remove entry.
Definition NamedRTree.h:90
void Insert(const float a_min[2], const float a_max[2], Named *const &a_data)
Insert entry.
Definition NamedRTree.h:79
int Search(const float a_min[2], const float a_max[2], const Named::StoringVisitor &c) const
Find all within search rectangle.
Definition NamedRTree.h:112
A storage for options typed value containers)
Definition OptionsCont.h:89
bool isSet(const std::string &name, bool failOnNonExistant=true) const
Returns the information whether the named option is set.
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
int getInt(const std::string &name) const
Returns the int-value of the named option (only for Option_Integer)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool isDefault(const std::string &name) const
Returns the information whether the named option has still the default value.
bool exists(const std::string &name) const
Returns the information whether the named option is known.
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
const StringVector & getStringVector(const std::string &name) const
Returns the list of string-value of the named option (only for Option_StringVector)
static OptionsCont & getOptions()
Retrieves the options.
virtual void setParameter(const std::string &key, const std::string &value)
Sets a parameter.
A point in 2D or 3D with translation and scaling methods.
Definition Position.h:37
void setx(double x)
set position x
Definition Position.h:67
static const Position INVALID
used to indicate that a position is valid
Definition Position.h: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