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