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