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