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