Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBEdgeCont.cpp
193828 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 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 NBEdgeCont.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @date Tue, 20 Nov 2001
20
///
21
// Storage for edges, including some functionality operating on multiple edges
22
/****************************************************************************/
23
#include <config.h>
24
25
#include <vector>
26
#include <string>
27
#include <cassert>
28
#include <algorithm>
29
#include <cmath>
30
#include <utils/geom/Boundary.h>
31
#include <utils/geom/GeomHelper.h>
32
#include <utils/geom/GeoConvHelper.h>
33
#include <utils/geom/GeomConvHelper.h>
34
#include <utils/common/MsgHandler.h>
35
#include <utils/common/ToString.h>
36
#include <utils/common/StringUtils.h>
37
#include <utils/common/IDSupplier.h>
38
#include <utils/common/StringUtils.h>
39
#include <utils/common/StringTokenizer.h>
40
#include <utils/common/UtilExceptions.h>
41
#include <utils/iodevices/OutputDevice.h>
42
#include <utils/options/OptionsCont.h>
43
#include "NBNetBuilder.h"
44
#include "NBEdgeCont.h"
45
#include "NBNodeCont.h"
46
#include "NBPTLineCont.h"
47
#include "NBPTStop.h"
48
#include "NBHelpers.h"
49
#include "NBCont.h"
50
#include "NBTrafficLightLogicCont.h"
51
#include "NBDistrictCont.h"
52
#include "NBTypeCont.h"
53
54
#define JOIN_TRAM_MAX_ANGLE 10
55
#define JOIN_TRAM_MIN_LENGTH 3
56
57
//#define DEBUG_GUESS_ROUNDABOUT
58
//#define DEBUG_JOIN_TRAM
59
#define DEBUG_EDGE_ID ""
60
61
// ===========================================================================
62
// method definitions
63
// ===========================================================================
64
NBEdgeCont::NBEdgeCont(NBTypeCont& tc) :
65
myTypeCont(tc),
66
myVehicleClasses2Keep(0),
67
myVehicleClasses2Remove(0),
68
myNeedGeoTransformedPruningBoundary(false) {
69
}
70
71
72
NBEdgeCont::~NBEdgeCont() {
73
clear();
74
}
75
76
77
void
78
NBEdgeCont::applyOptions(OptionsCont& oc) {
79
// set edges dismiss/accept options
80
myEdgesMinSpeed = oc.getFloat("keep-edges.min-speed");
81
myRemoveEdgesAfterLoading = oc.exists("keep-edges.postload") && oc.getBool("keep-edges.postload");
82
// we possibly have to load the edges to keep/remove
83
if (oc.isSet("keep-edges.input-file")) {
84
NBHelpers::loadEdgesFromFile(oc.getString("keep-edges.input-file"), myEdges2Keep);
85
}
86
if (oc.isSet("remove-edges.input-file")) {
87
NBHelpers::loadEdgesFromFile(oc.getString("remove-edges.input-file"), myEdges2Remove);
88
}
89
if (oc.isSet("keep-edges.explicit")) {
90
const std::vector<std::string> edges = oc.getStringVector("keep-edges.explicit");
91
myEdges2Keep.insert(edges.begin(), edges.end());
92
}
93
if (oc.isSet("remove-edges.explicit")) {
94
const std::vector<std::string> edges = oc.getStringVector("remove-edges.explicit");
95
myEdges2Remove.insert(edges.begin(), edges.end());
96
}
97
if (oc.exists("keep-edges.by-vclass") && oc.isSet("keep-edges.by-vclass")) {
98
myVehicleClasses2Keep = parseVehicleClasses(oc.getStringVector("keep-edges.by-vclass"));
99
}
100
if (oc.exists("remove-edges.by-vclass") && oc.isSet("remove-edges.by-vclass")) {
101
myVehicleClasses2Remove = parseVehicleClasses(oc.getStringVector("remove-edges.by-vclass"));
102
}
103
if (oc.exists("keep-edges.by-type") && oc.isSet("keep-edges.by-type")) {
104
const std::vector<std::string> types = oc.getStringVector("keep-edges.by-type");
105
myTypes2Keep.insert(types.begin(), types.end());
106
}
107
if (oc.exists("remove-edges.by-type") && oc.isSet("remove-edges.by-type")) {
108
const std::vector<std::string> types = oc.getStringVector("remove-edges.by-type");
109
myTypes2Remove.insert(types.begin(), types.end());
110
}
111
112
if (oc.isSet("keep-edges.in-boundary") || oc.isSet("keep-edges.in-geo-boundary")) {
113
114
std::string polyPlainString = oc.getValueString(oc.isSet("keep-edges.in-boundary") ?
115
"keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
116
// try interpreting the boundary like shape attribute with spaces
117
bool ok = true;
118
PositionVector boundaryShape = GeomConvHelper::parseShapeReporting(polyPlainString, "pruning-boundary", 0, ok, false, false);
119
if (ok) {
120
if (boundaryShape.size() < 2) {
121
throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
122
} else if (boundaryShape.size() == 2) {
123
// prunning boundary (box)
124
myPruningBoundary.push_back(boundaryShape[0]);
125
myPruningBoundary.push_back(Position(boundaryShape[1].x(), boundaryShape[0].y()));
126
myPruningBoundary.push_back(boundaryShape[1]);
127
myPruningBoundary.push_back(Position(boundaryShape[0].x(), boundaryShape[1].y()));
128
} else {
129
myPruningBoundary = boundaryShape;
130
}
131
} else {
132
// maybe positions are separated by ',' instead of ' '
133
std::vector<std::string> polyS = oc.getStringVector(oc.isSet("keep-edges.in-boundary") ?
134
"keep-edges.in-boundary" : "keep-edges.in-geo-boundary");
135
std::vector<double> poly;
136
for (std::vector<std::string>::iterator i = polyS.begin(); i != polyS.end(); ++i) {
137
poly.push_back(StringUtils::toDouble((*i))); // !!! may throw something anyhow...
138
}
139
if (poly.size() < 4) {
140
throw ProcessError(TL("Invalid boundary: need at least 2 coordinates"));
141
} else if (poly.size() % 2 != 0) {
142
throw ProcessError(TL("Invalid boundary: malformed coordinate"));
143
} else if (poly.size() == 4) {
144
// prunning boundary (box)
145
myPruningBoundary.push_back(Position(poly[0], poly[1]));
146
myPruningBoundary.push_back(Position(poly[2], poly[1]));
147
myPruningBoundary.push_back(Position(poly[2], poly[3]));
148
myPruningBoundary.push_back(Position(poly[0], poly[3]));
149
} else {
150
for (std::vector<double>::iterator j = poly.begin(); j != poly.end();) {
151
double x = *j++;
152
double y = *j++;
153
myPruningBoundary.push_back(Position(x, y));
154
}
155
}
156
}
157
myNeedGeoTransformedPruningBoundary = oc.isSet("keep-edges.in-geo-boundary");
158
}
159
}
160
161
162
void
163
NBEdgeCont::clear() {
164
for (const auto& i : myEdges) {
165
delete i.second;
166
}
167
myEdges.clear();
168
for (const auto& i : myExtractedEdges) {
169
delete i.second;
170
}
171
myExtractedEdges.clear();
172
for (NBEdge* const e : myEdgeCemetery) {
173
delete e;
174
}
175
myEdgeCemetery.clear();
176
}
177
178
179
180
// ----- edge access methods
181
bool
182
NBEdgeCont::insert(NBEdge* edge, bool ignorePrunning) {
183
if (myEdges.count(edge->getID()) != 0) {
184
return false;
185
}
186
if (!ignorePrunning && ignoreFilterMatch(edge)) {
187
edge->getFromNode()->removeEdge(edge);
188
edge->getToNode()->removeEdge(edge);
189
myIgnoredEdges.insert(edge->getID());
190
delete edge;
191
} else {
192
OptionsCont& oc = OptionsCont::getOptions();
193
if (oc.exists("dismiss-vclasses") && oc.getBool("dismiss-vclasses")) {
194
edge->dismissVehicleClassInformation();
195
}
196
myEdges[edge->getID()] = edge;
197
}
198
return true;
199
}
200
201
202
bool
203
NBEdgeCont::ignoreFilterMatch(NBEdge* edge) {
204
if (!myRemoveEdgesAfterLoading) {
205
// check whether the edge is a named edge to keep
206
if (myEdges2Keep.size() != 0) {
207
if (myEdges2Keep.count(edge->getID()) == 0) {
208
// explicit whitelisting may be combined additively with other filters
209
if (myVehicleClasses2Keep == 0 && myVehicleClasses2Remove == 0
210
&& myTypes2Keep.size() == 0 && myTypes2Remove.size() == 0
211
&& myPruningBoundary.size() == 0) {
212
return true;
213
}
214
} else {
215
// explicit whitelisting overrides other filters
216
return false;
217
}
218
}
219
// remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
220
if (edge->getSpeed() < myEdgesMinSpeed) {
221
return true;
222
}
223
// check whether the edge shall be removed because it does not allow any of the wished classes
224
if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
225
return true;
226
}
227
// check whether the edge shall be removed due to allowing unwished classes only
228
if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
229
return true;
230
}
231
}
232
// check whether the edge is a named edge to remove
233
if (myEdges2Remove.size() != 0) {
234
if (myEdges2Remove.count(edge->getID()) != 0) {
235
return true;
236
}
237
}
238
// check whether the edge shall be removed because it does not have one of the requested types
239
if (myTypes2Keep.size() != 0) {
240
if (myTypes2Keep.count(edge->getTypeID()) == 0) {
241
return true;
242
}
243
}
244
// check whether the edge shall be removed because it has one of the forbidden types
245
if (myTypes2Remove.size() != 0) {
246
if (myTypes2Remove.count(edge->getTypeID()) > 0) {
247
return true;
248
}
249
}
250
// check whether the edge is within the pruning boundary
251
if (myPruningBoundary.size() != 0) {
252
if (myNeedGeoTransformedPruningBoundary) {
253
if (GeoConvHelper::getProcessing().usingGeoProjection()) {
254
NBNetBuilder::transformCoordinates(myPruningBoundary, false);
255
} else if (GeoConvHelper::getLoaded().usingGeoProjection()) {
256
// XXX what if input file with different projections are loaded?
257
for (int i = 0; i < (int) myPruningBoundary.size(); i++) {
258
GeoConvHelper::getLoaded().x2cartesian_const(myPruningBoundary[i]);
259
}
260
} else {
261
WRITE_ERROR(TL("Cannot prune edges using a geo-boundary because no projection has been loaded"));
262
}
263
myNeedGeoTransformedPruningBoundary = false;
264
}
265
if (!(edge->getGeometry().getBoxBoundary().grow(POSITION_EPS).overlapsWith(myPruningBoundary))) {
266
return true;
267
} else if (!(edge->getGeometry().partialWithin(myPruningBoundary, 2 * POSITION_EPS) || edge->getGeometry().intersects(myPruningBoundary))) {
268
// a more detailed check is necessary because the bounding box may be much bigger than the edge
269
// @note: overlapsWith implicitly closes the edge shape but this is not wanted here
270
return true;
271
}
272
}
273
if (myTypeCont.knows(edge->getTypeID()) && myTypeCont.getEdgeTypeShallBeDiscarded(edge->getTypeID())) {
274
return true;
275
}
276
return false;
277
}
278
279
280
NBEdge*
281
NBEdgeCont::retrieve(const std::string& id, bool retrieveExtracted) const {
282
EdgeCont::const_iterator i = myEdges.find(id);
283
if (i == myEdges.end()) {
284
if (retrieveExtracted) {
285
i = myExtractedEdges.find(id);
286
if (i == myExtractedEdges.end()) {
287
return nullptr;
288
}
289
} else {
290
return nullptr;
291
}
292
}
293
return (*i).second;
294
}
295
296
// FIXME: This can't work
297
/*
298
NBEdge*
299
NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
300
NBEdge* edge = retrieve(id);
301
if (edge == 0) {
302
return 0;
303
}
304
const EdgeVector* candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
305
while (candidates->size() == 1) {
306
const std::string& nextID = candidates->front()->getID();
307
if (nextID.find(id) != 0 || nextID.size() <= id.size() + 1 || (nextID[id.size()] != '.' && nextID[id.size()] != '-')) {
308
break;
309
}
310
edge = candidates->front();
311
candidates = downstream ? &edge->getToNode()->getOutgoingEdges() : &edge->getFromNode()->getIncomingEdges();
312
}
313
return edge;
314
}*/
315
316
NBEdge*
317
NBEdgeCont::retrievePossiblySplit(const std::string& id, bool downstream) const {
318
NBEdge* edge = retrieve(id);
319
if (edge != nullptr) {
320
return edge;
321
}
322
// NOTE: (TODO) for multiply split edges (e.g. 15[0][0]) one could try recursion
323
if ((retrieve(id + "[0]") != nullptr) && (retrieve(id + "[1]") != nullptr)) {
324
// Edge was split during the netbuilding process
325
if (downstream) {
326
return retrieve(id + "[1]");
327
} else {
328
return retrieve(id + "[0]");
329
}
330
}
331
return edge;
332
}
333
334
335
NBEdge*
336
NBEdgeCont::retrievePossiblySplit(const std::string& id, const std::string& hint, bool incoming) const {
337
// try to retrieve using the given name (iterative)
338
NBEdge* edge = retrieve(id);
339
if (edge != nullptr) {
340
return edge;
341
}
342
// now, we did not find it; we have to look over all possibilities
343
EdgeVector hints;
344
// check whether at least the hint was not splitted
345
NBEdge* hintedge = retrieve(hint);
346
if (hintedge == nullptr) {
347
hints = getGeneratedFrom(hint);
348
} else {
349
hints.push_back(hintedge);
350
}
351
EdgeVector candidates = getGeneratedFrom(id);
352
for (const NBEdge* const currHint : hints) {
353
for (NBEdge* const poss_searched : candidates) {
354
const NBNode* const node = incoming ? poss_searched->myTo : poss_searched->myFrom;
355
const EdgeVector& cont = incoming ? node->getOutgoingEdges() : node->getIncomingEdges();
356
if (find(cont.begin(), cont.end(), currHint) != cont.end()) {
357
return poss_searched;
358
}
359
}
360
}
361
return nullptr;
362
}
363
364
365
NBEdge*
366
NBEdgeCont::retrievePossiblySplit(const std::string& id, double pos) const {
367
// check whether the edge was not split, yet
368
NBEdge* edge = retrieve(id);
369
if (edge != nullptr) {
370
return edge;
371
}
372
int maxLength = 0;
373
std::string tid = id + "[";
374
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
375
if ((*i).first.find(tid) == 0) {
376
maxLength = MAX2(maxLength, (int)(*i).first.length());
377
}
378
}
379
// find the part of the edge which matches the position
380
double seen = 0;
381
std::vector<std::string> names;
382
names.push_back(id + "[1]");
383
names.push_back(id + "[0]");
384
while (names.size() > 0) {
385
// retrieve the first subelement (to follow)
386
std::string cid = names.back();
387
names.pop_back();
388
edge = retrieve(cid);
389
// The edge was splitted; check its subparts within the
390
// next step
391
if (edge == nullptr) {
392
if ((int)cid.length() + 3 < maxLength) {
393
names.push_back(cid + "[1]");
394
names.push_back(cid + "[0]");
395
}
396
}
397
// an edge with the name was found,
398
// check whether the position lies within it
399
else {
400
seen += edge->getLength();
401
if (seen >= pos) {
402
return edge;
403
}
404
}
405
}
406
return nullptr;
407
}
408
409
410
void
411
NBEdgeCont::erase(NBDistrictCont& dc, NBEdge* edge) {
412
extract(dc, edge);
413
delete edge;
414
}
415
416
417
void
418
NBEdgeCont::extract(NBDistrictCont& dc, NBEdge* edge, bool remember) {
419
if (remember) {
420
const auto& prevExtracted = myExtractedEdges.find(edge->getID());
421
if (prevExtracted != myExtractedEdges.end()) {
422
if (edge != prevExtracted->second) {
423
myEdgeCemetery.insert(prevExtracted->second);
424
prevExtracted->second = edge;
425
}
426
} else {
427
myExtractedEdges[edge->getID()] = edge;
428
}
429
}
430
myEdges.erase(edge->getID());
431
edge->myFrom->removeEdge(edge);
432
edge->myTo->removeEdge(edge);
433
dc.removeFromSinksAndSources(edge);
434
}
435
436
437
void
438
NBEdgeCont::rename(NBEdge* edge, const std::string& newID) {
439
if (myEdges.count(newID) != 0) {
440
throw ProcessError(TLF("Attempt to rename edge using existing id '%'", newID));
441
}
442
myEdges.erase(edge->getID());
443
edge->setID(newID);
444
myEdges[newID] = edge;
445
// update oppositeID
446
if (edge->getLanes().back().oppositeID != "") {
447
NBEdge* oppo = retrieve(SUMOXMLDefinitions::getEdgeIDFromLane(edge->getLanes().back().oppositeID));
448
if (oppo != nullptr) {
449
oppo->getLaneStruct(oppo->getNumLanes() - 1).oppositeID = edge->getLaneID(edge->getNumLanes() - 1);
450
}
451
}
452
}
453
454
455
// ----- explicit edge manipulation methods
456
457
void
458
NBEdgeCont::processSplits(NBEdge* e, std::vector<Split> splits,
459
NBNodeCont& nc, NBDistrictCont& dc, NBTrafficLightLogicCont& tlc) {
460
if (splits.empty()) {
461
return;
462
}
463
const std::string origID = e->getID();
464
sort(splits.begin(), splits.end(), split_sorter());
465
int maxNumLanes = e->getNumLanes();
466
// compute the node positions and sort the lanes
467
for (Split& split : splits) {
468
sort(split.lanes.begin(), split.lanes.end());
469
maxNumLanes = MAX2(maxNumLanes, (int)split.lanes.size());
470
}
471
// split the edge
472
std::vector<int> currLanes;
473
for (int l = 0; l < e->getNumLanes(); ++l) {
474
currLanes.push_back(l);
475
}
476
if (e->getNumLanes() != (int)splits.back().lanes.size()) {
477
// invalidate traffic light definitions loaded from a SUMO network
478
e->getToNode()->invalidateTLS(tlc, true, true);
479
// if the number of lanes changes the connections should be
480
// recomputed
481
e->invalidateConnections(true);
482
}
483
484
std::string firstID = "";
485
double seen = 0;
486
for (const Split& exp : splits) {
487
assert(exp.lanes.size() != 0);
488
if (exp.pos > 0 && e->getLoadedLength() + seen > exp.pos && exp.pos > seen) {
489
nc.insert(exp.node);
490
nc.markAsSplit(exp.node);
491
// split the edge
492
const std::string idBefore = exp.idBefore == "" ? e->getID() : exp.idBefore;
493
const std::string idAfter = exp.idAfter == "" ? exp.nameID : exp.idAfter;
494
if (firstID == "") {
495
firstID = idBefore;
496
}
497
const bool ok = splitAt(dc, e, exp.pos - seen, exp.node,
498
idBefore, idAfter, e->getNumLanes(), (int) exp.lanes.size(), exp.speed);
499
if (!ok) {
500
WRITE_WARNINGF(TL("Error on parsing a split (edge '%')."), origID);
501
return;
502
}
503
seen = exp.pos;
504
std::vector<int> newLanes = exp.lanes;
505
NBEdge* pe = retrieve(idBefore);
506
NBEdge* ne = retrieve(idAfter);
507
// reconnect lanes
508
pe->invalidateConnections(true);
509
// new on right
510
int rightMostP = currLanes[0];
511
int rightMostN = newLanes[0];
512
for (int l = 0; l < (int) rightMostP - (int) rightMostN; ++l) {
513
pe->addLane2LaneConnection(0, ne, l, NBEdge::Lane2LaneInfoType::VALIDATED, true);
514
}
515
// new on left
516
int leftMostP = currLanes.back();
517
int leftMostN = newLanes.back();
518
for (int l = 0; l < (int) leftMostN - (int) leftMostP; ++l) {
519
pe->addLane2LaneConnection(pe->getNumLanes() - 1, ne, leftMostN - l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
520
}
521
// all other connected
522
for (int l = 0; l < maxNumLanes; ++l) {
523
if (find(currLanes.begin(), currLanes.end(), l) == currLanes.end()) {
524
continue;
525
}
526
if (find(newLanes.begin(), newLanes.end(), l) == newLanes.end()) {
527
continue;
528
}
529
pe->addLane2LaneConnection(l - rightMostP, ne, l - rightMostN, NBEdge::Lane2LaneInfoType::VALIDATED, true);
530
}
531
// if there are edges at this node which are not connected
532
// we can assume that this split was attached to an
533
// existing node. Reset all connections to let the default
534
// algorithm recompute them
535
if (exp.node->getIncomingEdges().size() > 1 || exp.node->getOutgoingEdges().size() > 1 || exp.node->getType() == SumoXMLNodeType::ZIPPER) {
536
for (NBEdge* in : exp.node->getIncomingEdges()) {
537
in->invalidateConnections(true);
538
}
539
}
540
// move to next
541
e = ne;
542
currLanes = newLanes;
543
} else if (exp.pos == 0) {
544
const int laneCountDiff = e->getNumLanes() - (int)exp.lanes.size();
545
if (laneCountDiff < 0) {
546
e->incLaneNo(-laneCountDiff);
547
} else {
548
e->decLaneNo(laneCountDiff);
549
}
550
currLanes = exp.lanes;
551
// invalidate traffic light definition loaded from a SUMO network
552
// XXX it would be preferable to reconstruct the phase definitions heuristically
553
e->getFromNode()->invalidateTLS(tlc, true, true);
554
if (exp.speed != -1.) {
555
e->setSpeed(-1, exp.speed);
556
}
557
} else {
558
WRITE_WARNINGF(TL("Split at '%' lies beyond the edge's length (edge '%')."), toString(exp.pos), origID);
559
}
560
}
561
// patch lane offsets
562
e = retrieve(firstID);
563
if (e != nullptr) {
564
if (splits.front().pos != 0) {
565
// add a dummy split at the beginning to ensure correct offset
566
Split start;
567
start.pos = 0;
568
for (int lane = 0; lane < (int)e->getNumLanes(); ++lane) {
569
start.lanes.push_back(lane);
570
}
571
start.offset = splits.front().offset;
572
start.offsetFactor = splits.front().offsetFactor;
573
splits.insert(splits.begin(), start);
574
}
575
for (const Split& split : splits) {
576
int maxLeft = split.lanes.back();
577
double offset = split.offset;
578
if (maxLeft < maxNumLanes) {
579
if (e->getLaneSpreadFunction() == LaneSpreadFunction::RIGHT) {
580
offset += split.offsetFactor * SUMO_const_laneWidth * (maxNumLanes - 1 - maxLeft);
581
} else {
582
offset += split.offsetFactor * SUMO_const_halfLaneWidth * (maxNumLanes - 1 - maxLeft);
583
}
584
}
585
int maxRight = split.lanes.front();
586
if (maxRight > 0 && e->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
587
offset -= split.offsetFactor * SUMO_const_halfLaneWidth * maxRight;
588
}
589
//std::cout << " processSplits " << origID << " splitOffset=" << (*i).offset << " offset=" << offset << "\n";
590
if (offset != 0) {
591
PositionVector g = e->getGeometry();
592
g.move2side(offset);
593
e->setGeometry(g);
594
}
595
if (e->getToNode()->getOutgoingEdges().size() != 0) {
596
e = e->getToNode()->getOutgoingEdges()[0];
597
}
598
}
599
}
600
}
601
602
603
bool
604
NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node) {
605
return splitAt(dc, edge, node, edge->getID() + "[0]", edge->getID() + "[1]",
606
(int) edge->myLanes.size(), (int) edge->myLanes.size());
607
}
608
609
610
bool
611
NBEdgeCont::splitAt(NBDistrictCont& dc, NBEdge* edge, NBNode* node,
612
const std::string& firstEdgeName,
613
const std::string& secondEdgeName,
614
int noLanesFirstEdge, int noLanesSecondEdge,
615
const double speed, const double friction,
616
const int changedLeft) {
617
double pos;
618
pos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
619
if (pos <= 0) {
620
pos = GeomHelper::nearest_offset_on_line_to_point2D(
621
edge->myFrom->getPosition(), edge->myTo->getPosition(),
622
node->getPosition());
623
}
624
if (pos <= 0 || pos + POSITION_EPS > edge->getGeometry().length()) {
625
return false;
626
}
627
return splitAt(dc, edge, pos, node, firstEdgeName, secondEdgeName,
628
noLanesFirstEdge, noLanesSecondEdge, speed, friction, changedLeft);
629
}
630
631
632
bool
633
NBEdgeCont::splitAt(NBDistrictCont& dc,
634
NBEdge* edge, double pos, NBNode* node,
635
const std::string& firstEdgeName,
636
const std::string& secondEdgeName,
637
int noLanesFirstEdge, int noLanesSecondEdge,
638
const double speed, const double friction,
639
const int changedLeft) {
640
if (firstEdgeName != edge->getID() && myEdges.count(firstEdgeName) != 0) {
641
WRITE_ERRORF(TL("Could not insert edge '%' before split of edge '%'."), firstEdgeName, edge->getID());
642
return false;
643
}
644
if (secondEdgeName == firstEdgeName || (secondEdgeName != edge->getID() && myEdges.count(secondEdgeName) != 0)) {
645
WRITE_ERRORF(TL("Could not insert edge '%' after split of edge '%'."), secondEdgeName, edge->getID());
646
return false;
647
}
648
// there must be at least some overlap between first and second edge
649
assert(changedLeft > -((int)noLanesFirstEdge));
650
assert(changedLeft < (int)noLanesSecondEdge);
651
652
// build the new edges' geometries
653
double geomPos = pos;
654
if (edge->hasLoadedLength()) {
655
geomPos = edge->getGeometry().nearest_offset_to_point2D(node->getPosition());
656
if (geomPos <= 0) {
657
geomPos = pos * edge->getGeometry().length() / edge->getLoadedLength();
658
}
659
}
660
std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
661
// reduce inaccuracies and preserve bidi
662
if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
663
geoms.first[-1] = node->getPosition();
664
geoms.second[0] = node->getPosition();
665
}
666
// build and insert the edges
667
NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
668
NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
669
if (OptionsCont::getOptions().getBool("output.original-names")) {
670
const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
671
if (firstEdgeName != origID) {
672
one->setOrigID(origID, false);
673
}
674
if (secondEdgeName != origID) {
675
two->setOrigID(origID, false);
676
}
677
}
678
two->copyConnectionsFrom(edge);
679
if (speed != -1.) {
680
two->setSpeed(-1, speed);
681
}
682
if (friction != -1.) {
683
two->setFriction(-1, friction);
684
}
685
if (edge->getDistance() != 0) {
686
one->setDistance(edge->getDistance());
687
two->setDistance(one->getDistance() + pos);
688
}
689
if (edge->hasLoadedLength()) {
690
one->setLoadedLength(pos);
691
two->setLoadedLength(edge->getLoadedLength() - pos);
692
}
693
// replace information about this edge within the nodes
694
edge->myFrom->replaceOutgoing(edge, one, 0);
695
edge->myTo->replaceIncoming(edge, two, 0);
696
// patch tls
697
for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
698
tld->replaceRemoved(edge, -1, one, -1, false);
699
}
700
for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
701
tld->replaceRemoved(edge, -1, two, -1, true);
702
}
703
// the edge is now occuring twice in both nodes...
704
// clean up
705
edge->myFrom->removeDoubleEdges();
706
edge->myTo->removeDoubleEdges();
707
// add connections from the first to the second edge
708
// there will be as many connections as there are lanes on the second edge
709
// by default lanes will be added / discontinued on the right side
710
// (appropriate for highway on-/off-ramps)
711
const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
712
for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
713
const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
714
if (!one->addLane2LaneConnection(i1, two, i2, NBEdge::Lane2LaneInfoType::COMPUTED)) {
715
throw ProcessError(TL("Could not set connection!"));
716
}
717
}
718
if (myRemoveEdgesAfterLoading) {
719
if (myEdges2Keep.count(edge->getID()) != 0) {
720
myEdges2Keep.insert(one->getID());
721
myEdges2Keep.insert(two->getID());
722
}
723
if (myEdges2Remove.count(edge->getID()) != 0) {
724
myEdges2Remove.insert(one->getID());
725
myEdges2Remove.insert(two->getID());
726
}
727
}
728
// erase the splitted edge
729
patchRoundabouts(edge, one, two, myRoundabouts);
730
patchRoundabouts(edge, one, two, myGuessedRoundabouts);
731
const std::string oldID = edge->getID();
732
extract(dc, edge, true);
733
insert(one, true); // duplicate id check happened earlier
734
insert(two, true); // duplicate id check happened earlier
735
myEdgesSplit[edge] = {one, two};
736
myWasSplit.insert(one);
737
myWasSplit.insert(two);
738
return true;
739
}
740
741
742
void
743
NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
744
std::set<EdgeSet> addLater;
745
for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
746
EdgeSet roundaboutSet = *it;
747
if (roundaboutSet.count(orig) > 0) {
748
roundaboutSet.erase(orig);
749
roundaboutSet.insert(part1);
750
roundaboutSet.insert(part2);
751
}
752
addLater.insert(roundaboutSet);
753
}
754
roundabouts.clear();
755
roundabouts.insert(addLater.begin(), addLater.end());
756
}
757
758
759
// ----- container access methods
760
std::vector<std::string>
761
NBEdgeCont::getAllNames() const {
762
std::vector<std::string> ret;
763
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
764
ret.push_back((*i).first);
765
}
766
return ret;
767
}
768
769
770
NBEdge*
771
NBEdgeCont::getSplitBase(const std::string& edgeID) const {
772
NBEdge* longest = nullptr;
773
for (auto item : myEdgesSplit) {
774
if (item.first->getID() == edgeID) {
775
if (longest == nullptr || longest->getLoadedLength() < item.first->getLoadedLength()) {
776
longest = const_cast<NBEdge*>(item.first);
777
}
778
}
779
}
780
return longest;
781
}
782
783
// ----- Adapting the input
784
int
785
NBEdgeCont::removeUnwishedEdges(NBDistrictCont& dc) {
786
EdgeVector toRemove;
787
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
788
NBEdge* edge = (*i).second;
789
if (!myEdges2Keep.count(edge->getID())) {
790
edge->getFromNode()->removeEdge(edge);
791
edge->getToNode()->removeEdge(edge);
792
toRemove.push_back(edge);
793
}
794
}
795
for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
796
erase(dc, *j);
797
}
798
return (int)toRemove.size();
799
}
800
801
802
void
803
NBEdgeCont::splitGeometry(NBDistrictCont& dc, NBNodeCont& nc) {
804
// make a copy of myEdges because splitting will modify it
805
EdgeCont edges = myEdges;
806
for (auto& item : edges) {
807
NBEdge* edge = item.second;
808
if (edge->getGeometry().size() < 3) {
809
continue;
810
}
811
PositionVector geom = edge->getGeometry();
812
const std::string id = edge->getID();
813
double offset = 0;
814
for (int i = 1; i < (int)geom.size() - 1; i++) {
815
offset += geom[i - 1].distanceTo(geom[i]);
816
std::string nodeID = id + "." + toString((int)offset);
817
if (!nc.insert(nodeID, geom[i])) {
818
WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
819
continue;
820
}
821
NBNode* node = nc.retrieve(nodeID);
822
splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
823
edge = retrieve(nodeID);
824
}
825
}
826
}
827
828
829
void
830
NBEdgeCont::reduceGeometries(const double minDist) {
831
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
832
(*i).second->reduceGeometry(minDist);
833
}
834
}
835
836
837
void
838
NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
839
if (maxAngle > 0 || minRadius > 0) {
840
for (auto& item : myEdges) {
841
if ((item.second->getPermissions() & (SVC_PUBLIC_CLASSES | SVC_PASSENGER)) == 0) {
842
continue;
843
}
844
item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
845
}
846
}
847
}
848
849
850
// ----- processing methods
851
void
852
NBEdgeCont::clearControllingTLInformation() const {
853
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
854
(*i).second->clearControllingTLInformation();
855
}
856
}
857
858
859
void
860
NBEdgeCont::sortOutgoingLanesConnections() {
861
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
862
(*i).second->sortOutgoingConnectionsByAngle();
863
}
864
}
865
866
867
void
868
NBEdgeCont::computeEdge2Edges(bool noLeftMovers) {
869
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
870
(*i).second->computeEdge2Edges(noLeftMovers);
871
}
872
}
873
874
875
void
876
NBEdgeCont::computeLanes2Edges() {
877
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
878
(*i).second->computeLanes2Edges();
879
}
880
}
881
882
883
void
884
NBEdgeCont::recheckLanes() {
885
const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
886
for (const auto& edgeIt : myEdges) {
887
NBEdge* const edge = edgeIt.second;
888
edge->recheckLanes();
889
edge->recheckOpposite(*this, fixOppositeLengths);
890
}
891
}
892
893
894
void
895
NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
896
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
897
(*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
898
}
899
}
900
901
902
void
903
NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
904
for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
905
myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
906
}
907
}
908
909
910
void
911
NBEdgeCont::appendRailwayTurnarounds(const NBPTStopCont& sc) {
912
std::set<std::string> stopEdgeIDs;
913
for (auto& stopItem : sc.getStops()) {
914
stopEdgeIDs.insert(stopItem.second->getEdgeId());
915
}
916
for (auto& item : myEdges) {
917
NBEdge* edge = item.second;
918
if (edge->isBidiRail()
919
&& (stopEdgeIDs.count(item.first) > 0 ||
920
stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
921
NBEdge* to = edge->getTurnDestination(true);
922
assert(to != 0);
923
edge->setConnection(edge->getNumLanes() - 1,
924
to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
925
KEEPCLEAR_UNSPECIFIED,
926
NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
927
SUMO_const_haltingSpeed);
928
}
929
}
930
}
931
932
void
933
NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
934
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
935
(*i).second->computeEdgeShape(smoothElevationThreshold);
936
}
937
// equalize length of opposite edges
938
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
939
NBEdge* edge = i->second;
940
const std::string& oppositeID = edge->getLanes().back().oppositeID;
941
if (oppositeID != "" && oppositeID != "-") {
942
NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
943
if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
944
continue;
945
}
946
if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
947
double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
948
edge->setAverageLengthWithOpposite(avgLength);
949
oppEdge->setAverageLengthWithOpposite(avgLength);
950
}
951
}
952
}
953
}
954
955
956
void
957
NBEdgeCont::computeLaneShapes() {
958
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
959
(*i).second->computeLaneShapes();
960
}
961
}
962
963
964
void
965
NBEdgeCont::joinSameNodeConnectingEdges(NBDistrictCont& dc,
966
NBTrafficLightLogicCont& tlc,
967
EdgeVector edges) {
968
// !!! Attention!
969
// No merging of the geometry to come is being done
970
// The connections are moved from one edge to another within
971
// the replacement where the edge is a node's incoming edge.
972
973
// count the number of lanes, the speed and the id
974
int nolanes = 0;
975
double speed = 0;
976
int priority = -1;
977
bool joinEdges = true;
978
std::string id;
979
sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
980
// retrieve the connected nodes
981
NBEdge* tpledge = *(edges.begin());
982
NBNode* from = tpledge->getFromNode();
983
NBNode* to = tpledge->getToNode();
984
EdgeVector::const_iterator i;
985
int myPriority = (*edges.begin())->getPriority();
986
for (i = edges.begin(); i != edges.end(); i++) {
987
// some assertions
988
assert((*i)->getFromNode() == from);
989
assert((*i)->getToNode() == to);
990
// ad the number of lanes the current edge has
991
nolanes += (*i)->getNumLanes();
992
// build the id
993
if (i != edges.begin()) {
994
id += "+";
995
}
996
id += (*i)->getID();
997
// compute the speed
998
speed += (*i)->getSpeed();
999
// build the priority
1000
// merged edges should have the same inherited priority
1001
if (myPriority == (*i)->getPriority()) {
1002
priority = myPriority;
1003
} else {
1004
priority = -1;
1005
joinEdges = false;
1006
}
1007
}
1008
if (joinEdges) {
1009
speed /= (double)edges.size();
1010
// build the new edge
1011
NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1012
NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
1013
tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1014
// copy lane attributes
1015
int laneIndex = 0;
1016
for (i = edges.begin(); i != edges.end(); ++i) {
1017
const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1018
for (int j = 0; j < (int)lanes.size(); ++j) {
1019
newEdge->setPermissions(lanes[j].permissions, laneIndex);
1020
newEdge->setLaneWidth(laneIndex, lanes[j].width);
1021
newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1022
laneIndex++;
1023
}
1024
}
1025
insert(newEdge, true);
1026
// replace old edge by current within the nodes
1027
// and delete the old
1028
from->replaceOutgoing(edges, newEdge);
1029
to->replaceIncoming(edges, newEdge);
1030
// patch connections
1031
// add edge2edge-information
1032
for (i = edges.begin(); i != edges.end(); i++) {
1033
EdgeVector ev = (*i)->getConnectedEdges();
1034
for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1035
newEdge->addEdge2EdgeConnection(*j);
1036
}
1037
}
1038
// copy outgoing connections to the new edge
1039
int currLane = 0;
1040
for (i = edges.begin(); i != edges.end(); i++) {
1041
newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1042
currLane += (*i)->getNumLanes();
1043
}
1044
// patch tl-information
1045
currLane = 0;
1046
for (i = edges.begin(); i != edges.end(); i++) {
1047
int noLanes = (*i)->getNumLanes();
1048
for (int j = 0; j < noLanes; j++, currLane++) {
1049
// replace in traffic lights
1050
tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1051
tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1052
}
1053
}
1054
// delete joined edges
1055
for (i = edges.begin(); i != edges.end(); i++) {
1056
extract(dc, *i, true);
1057
}
1058
}
1059
}
1060
1061
1062
void
1063
NBEdgeCont::guessOpposites() {
1064
//@todo magic values
1065
const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
1066
// ensure consistency of loaded values before starting to guess
1067
for (const auto& edgeIt : myEdges) {
1068
NBEdge* const edge = edgeIt.second;
1069
edge->recheckOpposite(*this, fixOppositeLengths);
1070
}
1071
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1072
NBEdge* edge = i->second;
1073
edge->guessOpposite();
1074
}
1075
}
1076
1077
1078
void
1079
NBEdgeCont::recheckLaneSpread() {
1080
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1081
NBEdge* opposite = getOppositeByID(i->first);
1082
if (opposite != nullptr) {
1083
i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1084
opposite->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1085
} else {
1086
i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1087
}
1088
}
1089
}
1090
1091
1092
NBEdge*
1093
NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1094
const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1095
EdgeCont::const_iterator it = myEdges.find(oppositeID);
1096
return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1097
}
1098
1099
NBEdge*
1100
NBEdgeCont::getByID(const std::string& edgeID) const {
1101
EdgeCont::const_iterator it = myEdges.find(edgeID);
1102
return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1103
}
1104
1105
// ----- other
1106
void
1107
NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1108
KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1109
const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1110
SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1111
myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1112
speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1113
}
1114
1115
bool
1116
NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1117
if (myConnections.count(from) == 0) {
1118
return false;
1119
} else {
1120
if (to == "") {
1121
// wildcard
1122
return true;
1123
}
1124
for (const auto& ppc : myConnections[from]) {
1125
if (ppc.to == to) {
1126
return true;
1127
}
1128
}
1129
return false;
1130
}
1131
}
1132
1133
void
1134
NBEdgeCont::recheckPostProcessConnections() {
1135
const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1136
for (const auto& item : myConnections) {
1137
for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1138
NBEdge* from = retrievePossiblySplit((*i).from, true);
1139
NBEdge* to = retrievePossiblySplit((*i).to, false);
1140
if (from == nullptr || to == nullptr ||
1141
!from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1142
(*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1143
(*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1144
true)) {
1145
const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1146
if (warnOnly || (*i).warnOnly) {
1147
WRITE_WARNING(msg);
1148
} else {
1149
WRITE_ERROR(msg);
1150
}
1151
}
1152
}
1153
}
1154
// during loading we also kept some ambiguous connections in hope they might be valid after processing
1155
// we need to make sure that all invalid connections are removed now
1156
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1157
NBEdge* edge = it->second;
1158
NBNode* to = edge->getToNode();
1159
// make a copy because we may delete connections
1160
std::vector<NBEdge::Connection> connections = edge->getConnections();
1161
for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1162
NBEdge::Connection& c = *it_con;
1163
if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1164
WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1165
"' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1166
edge->removeFromConnections(c.toEdge);
1167
}
1168
}
1169
}
1170
}
1171
1172
1173
EdgeVector
1174
NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1175
int len = (int)id.length();
1176
EdgeVector ret;
1177
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1178
std::string curr = (*i).first;
1179
// the next check makes it possibly faster - we don not have
1180
// to compare the names
1181
if ((int)curr.length() <= len) {
1182
continue;
1183
}
1184
// the name must be the same as the given id but something
1185
// beginning with a '[' must be appended to it
1186
if (curr.substr(0, len) == id && curr[len] == '[') {
1187
ret.push_back((*i).second);
1188
continue;
1189
}
1190
// ok, maybe the edge is a compound made during joining of edges
1191
std::string::size_type pos = curr.find(id);
1192
// surely not
1193
if (pos == std::string::npos) {
1194
continue;
1195
}
1196
// check leading char
1197
if (pos > 0) {
1198
if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1199
// actually, this is another id
1200
continue;
1201
}
1202
}
1203
if (pos + id.length() < curr.length()) {
1204
if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1205
// actually, this is another id
1206
continue;
1207
}
1208
}
1209
ret.push_back((*i).second);
1210
}
1211
return ret;
1212
}
1213
1214
1215
int
1216
NBEdgeCont::guessRoundabouts() {
1217
myGuessedRoundabouts.clear();
1218
std::set<NBEdge*> loadedRoundaboutEdges;
1219
for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1220
loadedRoundaboutEdges.insert(it->begin(), it->end());
1221
}
1222
// step 1: keep only those edges which have no turnarounds and which are not
1223
// part of a loaded roundabout
1224
std::set<NBEdge*> candidates;
1225
SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1226
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1227
NBEdge* e = (*i).second;
1228
NBNode* const to = e->getToNode();
1229
if (e->getTurnDestination() == nullptr
1230
&& to->getConnectionTo(e->getFromNode()) == nullptr
1231
&& (e->getPermissions() & valid) != 0) {
1232
candidates.insert(e);
1233
}
1234
}
1235
1236
// step 2:
1237
std::set<NBEdge*> visited;
1238
for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1239
EdgeVector loopEdges;
1240
// start with a random edge (this doesn't have to be a roundabout edge)
1241
// loop over connected edges (using always the leftmost one)
1242
// and keep the list in loopEdges
1243
// continue until we loop back onto a loopEdges and extract the loop
1244
NBEdge* e = (*i);
1245
if (visited.count(e) > 0) {
1246
// already seen
1247
continue;
1248
}
1249
loopEdges.push_back(e);
1250
bool doLoop = true;
1251
#ifdef DEBUG_GUESS_ROUNDABOUT
1252
gDebugFlag1 = e->getID() == DEBUG_EDGE_ID;
1253
#endif
1254
do {
1255
#ifdef DEBUG_GUESS_ROUNDABOUT
1256
if (gDebugFlag1) {
1257
std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1258
gDebugFlag1 = true;
1259
}
1260
#endif
1261
visited.insert(e);
1262
const EdgeVector& edges = e->getToNode()->getEdges();
1263
if ((e->getToNode()->getType() == SumoXMLNodeType::RIGHT_BEFORE_LEFT || e->getToNode()->getType() == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
1264
&& !e->getToNode()->typeWasGuessed()) {
1265
doLoop = false;
1266
#ifdef DEBUG_GUESS_ROUNDABOUT
1267
if (gDebugFlag1) {
1268
std::cout << " rbl\n";
1269
}
1270
gDebugFlag1 = false;
1271
#endif
1272
break;
1273
}
1274
if (e->getToNode()->getRoundaboutType() == RoundaboutType::NO) {
1275
doLoop = false;
1276
#ifdef DEBUG_GUESS_ROUNDABOUT
1277
if (gDebugFlag1) {
1278
std::cout << " disabled\n";
1279
}
1280
gDebugFlag1 = false;
1281
#endif
1282
break;
1283
}
1284
if (edges.size() < 2) {
1285
doLoop = false;
1286
#ifdef DEBUG_GUESS_ROUNDABOUT
1287
if (gDebugFlag1) {
1288
std::cout << " deadend\n";
1289
}
1290
gDebugFlag1 = false;
1291
#endif
1292
break;
1293
}
1294
if (e->getTurnDestination(true) != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1295
// do not follow turn-arounds while in a (tentative) loop
1296
doLoop = false;
1297
#ifdef DEBUG_GUESS_ROUNDABOUT
1298
if (gDebugFlag1) {
1299
std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1300
}
1301
gDebugFlag1 = false;
1302
#endif
1303
break;
1304
}
1305
EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1306
NBContHelper::nextCW(edges, me);
1307
NBEdge* left = *me;
1308
while ((left->getPermissions() & valid) == 0 && left != e) {
1309
NBContHelper::nextCW(edges, me);
1310
left = *me;
1311
}
1312
if (left == e) {
1313
// no usable continuation edge found
1314
doLoop = false;
1315
#ifdef DEBUG_GUESS_ROUNDABOUT
1316
if (gDebugFlag1) {
1317
std::cout << " noContinuation\n";
1318
}
1319
gDebugFlag1 = false;
1320
#endif
1321
break;
1322
}
1323
NBContHelper::nextCW(edges, me);
1324
NBEdge* nextLeft = *me;
1325
double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1326
double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1327
#ifdef DEBUG_GUESS_ROUNDABOUT
1328
if (gDebugFlag1) {
1329
std::cout << " e=" << e->getID() << " left=" << left->getID() << " nextLeft=" << nextLeft->getID() << " angle=" << angle << " nextAngle=" << nextAngle << " eLength=" << e->getLength() << " lLength=" << left->getLength() << " dist=" << e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) << "\n";
1330
}
1331
#endif
1332
// there should be no straigher edge further left
1333
if (angle >= 90 && nextAngle < 45) {
1334
doLoop = false;
1335
#ifdef DEBUG_GUESS_ROUNDABOUT
1336
if (gDebugFlag1) {
1337
std::cout << " failed nextAngle=" << nextAngle << "\n";
1338
}
1339
gDebugFlag1 = false;
1340
#endif
1341
break;
1342
}
1343
// roundabouts do not have sharp turns (or they wouldn't be called 'round')
1344
// however, if the roundabout is very small then most of the roundness may be in the junction so the angle may be as high as 180 (for smooth attachments at a joined junction)
1345
if (angle >= 90) {
1346
double edgeAngle = fabs(NBHelpers::relAngle(e->getStartAngle(), e->getEndAngle()));
1347
double edgeAngle2 = fabs(NBHelpers::relAngle(left->getStartAngle(), left->getEndAngle()));
1348
double edgeRadius = e->getGeometry().length2D() / DEG2RAD(edgeAngle);
1349
double edgeRadius2 = left->getGeometry().length2D() / DEG2RAD(edgeAngle2);
1350
const double avgRadius = 0.5 * (edgeRadius + edgeRadius2);
1351
double junctionRadius = e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) / DEG2RAD(angle);
1352
//std::cout << " junction=" << e->getToNode()->getID() << " e=" << e->getID() << " left=" << left->getID() << " angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1353
if (junctionRadius < 0.8 * avgRadius) {
1354
doLoop = false;
1355
#ifdef DEBUG_GUESS_ROUNDABOUT
1356
if (gDebugFlag1) {
1357
std::cout << " failed angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1358
}
1359
gDebugFlag1 = false;
1360
#endif
1361
break;
1362
}
1363
}
1364
1365
EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1366
const int loopSize = (int)(loopEdges.end() - loopClosed);
1367
if (loopSize > 0) {
1368
// loop found
1369
if (loopSize < 3) {
1370
doLoop = false; // need at least 3 edges for a roundabout
1371
} else if (loopSize < (int)loopEdges.size()) {
1372
// remove initial edges not belonging to the loop
1373
EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1374
}
1375
// count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1376
int attachments = 0;
1377
for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1378
if ((*j)->getToNode()->getEdges().size() > 2) {
1379
attachments++;
1380
}
1381
}
1382
if (attachments < 3) {
1383
doLoop = false;
1384
#ifdef DEBUG_GUESS_ROUNDABOUT
1385
if (gDebugFlag1) {
1386
std::cout << " attachments=" << attachments << "\n";
1387
}
1388
gDebugFlag1 = false;
1389
#endif
1390
}
1391
break;
1392
}
1393
if (visited.count(left) > 0) {
1394
doLoop = false;
1395
} else {
1396
// keep going
1397
loopEdges.push_back(left);
1398
e = left;
1399
}
1400
} while (doLoop);
1401
if (doLoop) {
1402
// check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1403
#ifdef DEBUG_GUESS_ROUNDABOUT
1404
if (gDebugFlag1) {
1405
std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1406
}
1407
#endif
1408
double loopLength = 0;
1409
for (const NBEdge* const le : loopEdges) {
1410
loopLength += le->getLoadedLength();
1411
}
1412
if (formFactor(loopEdges) > 0.6
1413
&& loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
1414
// collected edges are marked in markRoundabouts
1415
EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1416
if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1417
if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1418
for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1419
if ((*it).count(loopEdges.front()) != 0) {
1420
WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1421
myRoundabouts.erase(it);
1422
break;
1423
}
1424
}
1425
myGuessedRoundabouts.insert(guessed);
1426
}
1427
} else {
1428
myGuessedRoundabouts.insert(guessed);
1429
#ifdef DEBUG_GUESS_ROUNDABOUT
1430
if (gDebugFlag1) {
1431
std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1432
}
1433
#endif
1434
}
1435
}
1436
}
1437
#ifdef DEBUG_GUESS_ROUNDABOUT
1438
gDebugFlag1 = false;
1439
#endif
1440
}
1441
return (int)myGuessedRoundabouts.size();
1442
}
1443
1444
1445
int
1446
NBEdgeCont::extractRoundabouts() {
1447
std::set<NBEdge*> candidateEdges;
1448
for (const auto& edge : myEdges) {
1449
NBEdge* const e = edge.second;
1450
if (e->getJunctionPriority(e->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT || e->getJunctionPriority(e->getFromNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
1451
candidateEdges.insert(e);
1452
}
1453
}
1454
std::set<NBEdge*> visited;
1455
int extracted = 0;
1456
for (const auto& edgeIt : candidateEdges) {
1457
EdgeVector loopEdges;
1458
NBEdge* e = edgeIt;
1459
if (visited.count(e) > 0) {
1460
// already seen
1461
continue;
1462
}
1463
loopEdges.push_back(e);
1464
bool doLoop = true;
1465
//
1466
do {
1467
if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
1468
if (loopEdges.size() > 1) {
1469
addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
1470
++extracted;
1471
}
1472
doLoop = false;
1473
break;
1474
}
1475
visited.insert(e);
1476
loopEdges.push_back(e);
1477
const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
1478
EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
1479
return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
1480
});
1481
if (me == outgoingEdges.end()) { // no closed loop
1482
doLoop = false;
1483
} else {
1484
e = *me;
1485
}
1486
} while (doLoop);
1487
}
1488
return extracted;
1489
}
1490
1491
1492
void
1493
NBEdgeCont::cleanupRoundabouts() {
1494
// only loaded roundabouts are of concern here since guessing comes later
1495
std::set<EdgeSet> validRoundabouts;
1496
std::set<NBEdge*> validEdges;
1497
for (auto item : myEdges) {
1498
validEdges.insert(item.second);
1499
}
1500
for (EdgeSet roundabout : myRoundabouts) {
1501
EdgeSet validRoundabout;
1502
for (NBEdge* cand : roundabout) {
1503
if (validEdges.count(cand) != 0) {
1504
validRoundabout.insert(cand);
1505
}
1506
}
1507
if (validRoundabout.size() > 0) {
1508
validRoundabouts.insert(validRoundabout);
1509
}
1510
}
1511
myRoundabouts = validRoundabouts;
1512
}
1513
1514
1515
double
1516
NBEdgeCont::formFactor(const EdgeVector& loopEdges) {
1517
// A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
1518
PositionVector points;
1519
for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1520
points.append((*it)->getGeometry());
1521
}
1522
double circumference = points.length2D();
1523
return 4 * M_PI * points.area() / (circumference * circumference);
1524
}
1525
1526
1527
const std::set<EdgeSet>
1528
NBEdgeCont::getRoundabouts() const {
1529
std::set<EdgeSet> result = myRoundabouts;
1530
result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1531
return result;
1532
}
1533
1534
1535
void
1536
NBEdgeCont::addRoundabout(const EdgeSet& roundabout) {
1537
if (roundabout.size() > 0) {
1538
if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1539
WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1540
} else {
1541
myRoundabouts.insert(roundabout);
1542
}
1543
}
1544
}
1545
1546
void
1547
NBEdgeCont::removeRoundabout(const NBNode* node) {
1548
for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1549
for (NBEdge* e : *it) {
1550
if (e->getToNode() == node) {
1551
myRoundabouts.erase(it);
1552
return;
1553
}
1554
}
1555
}
1556
}
1557
1558
void
1559
NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove) {
1560
removeRoundaboutEdges(toRemove, myRoundabouts);
1561
removeRoundaboutEdges(toRemove, myGuessedRoundabouts);
1562
}
1563
1564
void
1565
NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1566
// members of a set are constant so we have to do some tricks
1567
std::vector<EdgeSet> rList;
1568
for (const EdgeSet& r : roundabouts) {
1569
EdgeSet r2;
1570
std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1571
rList.push_back(r2);
1572
}
1573
roundabouts.clear();
1574
roundabouts.insert(rList.begin(), rList.end());
1575
}
1576
1577
1578
void
1579
NBEdgeCont::markRoundabouts() {
1580
for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1581
for (NBEdge* const edge : roundaboutSet) {
1582
// disable turnarounds on incoming edges
1583
NBNode* const node = edge->getToNode();
1584
for (NBEdge* const inEdge : node->getIncomingEdges()) {
1585
if (roundaboutSet.count(inEdge) > 0) {
1586
continue;
1587
}
1588
if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1589
continue;
1590
}
1591
if (inEdge->getTurnDestination() != nullptr) {
1592
inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1593
} else {
1594
// also remove connections that are effecively a turnaround but
1595
// where not correctly detector due to geometrical quirks
1596
const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1597
for (const NBEdge::Connection& con : cons) {
1598
if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1599
const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1600
if (angle > 160) {
1601
inEdge->removeFromConnections(con.toEdge, -1);
1602
}
1603
}
1604
}
1605
}
1606
1607
}
1608
// let the connections to succeeding roundabout edge have a higher priority
1609
edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1610
edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1611
node->setRoundabout();
1612
}
1613
}
1614
}
1615
1616
1617
void
1618
NBEdgeCont::generateStreetSigns() {
1619
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1620
NBEdge* e = i->second;
1621
const double offset = MAX2(0., e->getLength() - 3);
1622
if (e->getToNode()->isSimpleContinuation(false)) {
1623
// not a "real" junction?
1624
continue;
1625
}
1626
const SumoXMLNodeType nodeType = e->getToNode()->getType();
1627
switch (nodeType) {
1628
case SumoXMLNodeType::PRIORITY:
1629
// yield or major?
1630
if (e->getJunctionPriority(e->getToNode()) > 0) {
1631
e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1632
} else {
1633
e->addSign(NBSign(NBSign::SIGN_TYPE_YIELD, offset));
1634
}
1635
break;
1636
case SumoXMLNodeType::PRIORITY_STOP:
1637
// yield or major?
1638
if (e->getJunctionPriority(e->getToNode()) > 0) {
1639
e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1640
} else {
1641
e->addSign(NBSign(NBSign::SIGN_TYPE_STOP, offset));
1642
}
1643
break;
1644
case SumoXMLNodeType::ALLWAY_STOP:
1645
e->addSign(NBSign(NBSign::SIGN_TYPE_ALLWAY_STOP, offset));
1646
break;
1647
case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
1648
e->addSign(NBSign(NBSign::SIGN_TYPE_RIGHT_BEFORE_LEFT, offset));
1649
break;
1650
case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
1651
e->addSign(NBSign(NBSign::SIGN_TYPE_LEFT_BEFORE_RIGHT, offset));
1652
break;
1653
default:
1654
break;
1655
}
1656
}
1657
}
1658
1659
1660
int
1661
NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1662
NBTrafficLightLogicCont& tlc) {
1663
int lanesCreated = 0;
1664
std::vector<std::string> edges;
1665
if (excludeOpt != "") {
1666
edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1667
}
1668
std::set<std::string> exclude(edges.begin(), edges.end());
1669
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1670
NBEdge* edge = it->second;
1671
if (// not excluded
1672
exclude.count(edge->getID()) == 0
1673
// does not yet have a sidewalk/bikelane
1674
&& !edge->hasRestrictedLane(svc)
1675
// needs a sidewalk/bikelane
1676
&& ((edge->getPermissions() & ~SVC_VULNERABLE) != 0 || (edge->getPermissions() & svc) == 0)
1677
&& (
1678
// guess.from-permissions
1679
(fromPermissions && (edge->getPermissions() & svc) != 0)
1680
// guess from speed
1681
|| (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1682
)) {
1683
edge->addRestrictedLane(width, svc);
1684
lanesCreated += 1;
1685
if (svc != SVC_PEDESTRIAN) {
1686
if (edge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1687
// preserve existing connections and only add new ones
1688
edge->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::LANES2LANES_DONE);
1689
edge->getFromNode()->recheckVClassConnections(edge);
1690
for (NBEdge* to : edge->getToNode()->getOutgoingEdges()) {
1691
edge->getToNode()->recheckVClassConnections(to);
1692
}
1693
// patching TLS is not feasible because existing states may
1694
// change from 'G' to 'g' when bike lanes are added (i.e. right-turns)
1695
} else {
1696
edge->invalidateConnections(true);
1697
edge->getFromNode()->invalidateOutgoingConnections(true);
1698
}
1699
edge->getFromNode()->invalidateTLS(tlc, true, false);
1700
edge->getToNode()->invalidateTLS(tlc, true, false);
1701
}
1702
}
1703
}
1704
return lanesCreated;
1705
}
1706
1707
1708
void
1709
NBEdgeCont::updateAllChangeRestrictions(SVCPermissions ignoring) {
1710
for (auto item : myEdges) {
1711
item.second->updateChangeRestrictions(ignoring);
1712
}
1713
}
1714
1715
1716
void
1717
NBEdgeCont::addPrefix(const std::string& prefix) {
1718
// make a copy of node containers
1719
const auto nodeContainerCopy = myEdges;
1720
myEdges.clear();
1721
for (const auto& node : nodeContainerCopy) {
1722
node.second->setID(prefix + node.second->getID());
1723
myEdges[node.second->getID()] = node.second;
1724
}
1725
}
1726
1727
1728
int
1729
NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBPTStopCont& sc) {
1730
bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1731
if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1732
return 0;
1733
}
1734
std::vector<std::string> avoid;
1735
if (startGiven) {
1736
avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1737
} else {
1738
avoid = getAllNames();
1739
}
1740
std::set<std::string> reserve;
1741
if (reservedIDs) {
1742
NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1743
avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1744
}
1745
IDSupplier idSupplier("", avoid);
1746
std::set<NBEdge*, ComparatorIdLess> toChange;
1747
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1748
if (startGiven) {
1749
toChange.insert(it->second);
1750
continue;
1751
}
1752
if (numericaIDs) {
1753
try {
1754
StringUtils::toLong(it->first);
1755
} catch (NumberFormatException&) {
1756
toChange.insert(it->second);
1757
}
1758
}
1759
if (reservedIDs && reserve.count(it->first) > 0) {
1760
toChange.insert(it->second);
1761
}
1762
}
1763
std::set<std::string> keep;
1764
if (keptIDs) {
1765
NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "edge:", keep);
1766
for (auto it = toChange.begin(); it != toChange.end();) {
1767
if (keep.count((*it)->getID()) != 0) {
1768
toChange.erase(it++);
1769
} else {
1770
it++;
1771
}
1772
}
1773
}
1774
1775
std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1776
for (const auto& item : sc.getStops()) {
1777
stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1778
}
1779
1780
const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1781
for (NBEdge* edge : toChange) {
1782
myEdges.erase(edge->getID());
1783
}
1784
for (NBEdge* edge : toChange) {
1785
const std::string origID = edge->getID();
1786
if (origNames) {
1787
edge->setOrigID(origID, false);
1788
}
1789
edge->setID(idSupplier.getNext());
1790
myEdges[edge->getID()] = edge;
1791
for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1792
stop->setEdgeId(prefix + edge->getID(), *this);
1793
}
1794
}
1795
if (prefix.empty()) {
1796
return (int)toChange.size();
1797
} else {
1798
int renamed = 0;
1799
// make a copy because we will modify the map
1800
auto oldEdges = myEdges;
1801
for (auto item : oldEdges) {
1802
if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
1803
rename(item.second, prefix + item.first);
1804
renamed++;
1805
}
1806
}
1807
return renamed;
1808
}
1809
}
1810
1811
1812
void
1813
NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1814
for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1815
const NBEdge* e1 = it->second;
1816
Boundary b1 = e1->getGeometry().getBoxBoundary();
1817
b1.grow(e1->getTotalWidth());
1818
PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1819
outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1820
// check is symmetric. only check once per pair
1821
for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1822
const NBEdge* e2 = it2->second;
1823
if (e1 == e2) {
1824
continue;
1825
}
1826
Boundary b2 = e2->getGeometry().getBoxBoundary();
1827
b2.grow(e2->getTotalWidth());
1828
if (b1.overlapsWith(b2)) {
1829
PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1830
outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1831
const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1832
if (overlap > threshold) {
1833
WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1834
}
1835
}
1836
}
1837
}
1838
}
1839
1840
1841
void
1842
NBEdgeCont::checkGrade(double threshold) const {
1843
for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1844
const NBEdge* edge = it->second;
1845
for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1846
double maxJump = 0;
1847
const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1848
if (maxJump > 0.01) {
1849
WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1850
} else if (grade > threshold) {
1851
WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1852
break;
1853
}
1854
}
1855
const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1856
for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1857
const NBEdge::Connection& c = *it_con;
1858
double maxJump = 0;
1859
const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1860
if (maxJump > 0.01) {
1861
WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1862
} else if (grade > threshold) {
1863
WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1864
break;
1865
}
1866
}
1867
}
1868
}
1869
1870
1871
int
1872
NBEdgeCont::joinLanes(SVCPermissions perms) {
1873
int affectedEdges = 0;
1874
for (auto item : myEdges) {
1875
if (item.second->joinLanes(perms)) {
1876
affectedEdges++;
1877
}
1878
}
1879
return affectedEdges;
1880
}
1881
1882
1883
bool
1884
NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1885
if (a.first->getID() == b.first->getID()) {
1886
return a.second < b.second;
1887
}
1888
return a.first->getID() < b.first->getID();
1889
}
1890
1891
int
1892
NBEdgeCont::joinTramEdges(NBDistrictCont& dc, NBPTStopCont& sc, NBPTLineCont& lc, double maxDist) {
1893
// this is different from joinSimilarEdges because there don't need to be
1894
// shared nodes and tram edges may be split
1895
std::vector<NBEdge*> tramEdges;
1896
std::vector<NBEdge*> targetEdges;
1897
for (auto item : myEdges) {
1898
SVCPermissions permissions = item.second->getPermissions();
1899
if (isTram(permissions)) {
1900
if (item.second->getNumLanes() == 1) {
1901
tramEdges.push_back(item.second);
1902
} else {
1903
WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1904
}
1905
} else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1906
targetEdges.push_back(item.second);
1907
}
1908
}
1909
if (tramEdges.empty() || targetEdges.empty()) {
1910
return 0;
1911
}
1912
int numJoined = 0;
1913
NamedRTree tramTree;
1914
for (NBEdge* const edge : tramEdges) {
1915
const Boundary& bound = edge->getGeometry().getBoxBoundary();
1916
float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1917
float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1918
tramTree.Insert(min, max, edge);
1919
}
1920
// {targetEdge, laneIndex : tramEdge}
1921
std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1922
1923
for (NBEdge* const edge : targetEdges) {
1924
Boundary bound = edge->getGeometry().getBoxBoundary();
1925
bound.grow(maxDist + edge->getTotalWidth());
1926
float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1927
float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1928
std::set<const Named*> near;
1929
Named::StoringVisitor visitor(near);
1930
tramTree.Search(min, max, visitor);
1931
// the nearby set is actually just re-sorting according to the id to make the tests comparable
1932
std::set<NBEdge*, ComparatorIdLess> nearby;
1933
for (const Named* namedEdge : near) {
1934
nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
1935
}
1936
for (NBEdge* const tramEdge : nearby) {
1937
// find a continous stretch of tramEdge that runs along one of the lanes of the road edge
1938
PositionVector tramShape = tramEdge->getGeometry();
1939
if (tramEdge->getToNode() == edge->getToNode()) {
1940
tramShape.extrapolate(tramShape.back().distanceTo2D(edge->getGeometry().back()), false, true);
1941
}
1942
double minEdgeDist = maxDist + 1;
1943
int minLane = -1;
1944
// find the lane where the maximum distance from the tram geometry
1945
// is minimal and within maxDist
1946
for (int i = 0; i < edge->getNumLanes(); i++) {
1947
double maxLaneDist = -1;
1948
if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1949
const PositionVector& laneShape = edge->getLaneShape(i);
1950
for (Position pos : laneShape) {
1951
const double dist = tramShape.distance2D(pos, false);
1952
#ifdef DEBUG_JOIN_TRAM
1953
//if (edge->getID() == "106838214#1") {
1954
// std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1955
//}
1956
#endif
1957
if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1958
maxLaneDist = -1;
1959
break;
1960
}
1961
maxLaneDist = MAX2(maxLaneDist, dist);
1962
}
1963
if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1964
minEdgeDist = maxLaneDist;
1965
minLane = i;
1966
}
1967
}
1968
}
1969
if (minLane >= 0) {
1970
// edge could run in the wrong direction and still fit the threshold we check the angle as well
1971
const PositionVector& laneShape = edge->getLaneShape(minLane);
1972
const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1973
const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1974
Position p1 = tramShape.positionAtOffset2D(offset1);
1975
Position p2 = tramShape.positionAtOffset2D(offset2);
1976
double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1977
bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1978
if (angleOK && offset2 > offset1) {
1979
std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1980
if (matches.count(key) == 0) {
1981
matches[key] = tramEdge;
1982
} else {
1983
WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1984
}
1985
#ifdef DEBUG_JOIN_TRAM
1986
std::cout << edge->getLaneID(minLane) << " is close to tramEdge " << tramEdge->getID() << " maxLaneDist=" << minEdgeDist << " tramLength=" << tramEdge->getLength() << " edgeLength=" << edge->getLength() << " tramAngle=" << tramAngle << " edgeAngle=" << edge->getTotalAngle() << "\n";
1987
#endif
1988
}
1989
}
1990
}
1991
}
1992
if (matches.size() == 0) {
1993
return 0;
1994
}
1995
const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1996
// find continous runs of matched edges for each tramEdge
1997
for (NBEdge* tramEdge : tramEdges) {
1998
std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
1999
for (auto item : matches) {
2000
if (item.second == tramEdge) {
2001
NBEdge* road = item.first.first;
2002
int laneIndex = item.first.second;
2003
const PositionVector& laneShape = road->getLaneShape(laneIndex);
2004
double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
2005
//std::cout << " road=" << road->getID() << " tramEdge=" << tramEdge->getID() << " tramShape=" << tramEdge->getGeometry() << " laneFront=" << laneShape.front() << " tramPos=" << tramPos << "\n";
2006
roads.push_back(std::make_pair(tramPos, item.first));
2007
}
2008
}
2009
if (roads.size() != 0) {
2010
2011
sort(roads.begin(), roads.end());
2012
#ifdef DEBUG_JOIN_TRAM
2013
std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
2014
for (auto item : roads) {
2015
std::cout << item.second.first->getLaneID(item.second.second) << ",";
2016
}
2017
std::cout << " offsets=";
2018
for (auto item : roads) {
2019
std::cout << item.first << ",";
2020
}
2021
std::cout << "\n";
2022
#endif
2023
// merge tramEdge into road lanes
2024
EdgeVector replacement;
2025
double pos = 0;
2026
int tramPart = 0;
2027
std::string tramEdgeID = tramEdge->getID();
2028
NBNode* tramFrom = tramEdge->getFromNode();
2029
PositionVector tramShape = tramEdge->getGeometry();
2030
const double tramLength = tramShape.length();
2031
EdgeVector incoming = tramFrom->getIncomingEdges();
2032
bool erasedLast = false;
2033
for (const auto& item : roads) {
2034
const double gap = item.first - pos;
2035
NBEdge* road = item.second.first;
2036
int laneIndex = item.second.second;
2037
if (gap >= JOIN_TRAM_MIN_LENGTH && road->getFromNode() != tramEdge->getFromNode()) {
2038
#ifdef DEBUG_JOIN_TRAM
2039
std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
2040
#endif
2041
const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
2042
splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
2043
tramEdge = retrieve(tramEdgeID); // second part;
2044
NBEdge* firstPart = retrieve(firstPartID);
2045
firstPart->invalidateConnections(true);
2046
incoming.clear();
2047
incoming.push_back(firstPart);
2048
replacement.push_back(firstPart);
2049
}
2050
pos = item.first + road->getGeometry().length();
2051
numJoined++;
2052
replacement.push_back(road);
2053
// merge section of tramEdge into road lane
2054
if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
2055
tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
2056
tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
2057
erasedLast = false;
2058
#ifdef DEBUG_JOIN_TRAM
2059
std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
2060
#endif
2061
} else {
2062
#ifdef DEBUG_JOIN_TRAM
2063
std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
2064
#endif
2065
extract(dc, tramEdge, true);
2066
erasedLast = true;
2067
}
2068
road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
2069
if (origNames) {
2070
road->setOrigID(tramEdgeID, true, laneIndex);
2071
}
2072
for (NBEdge* in : incoming) {
2073
if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
2074
if (in->getFromNode() != road->getFromNode()) {
2075
in->reinitNodes(in->getFromNode(), road->getFromNode());
2076
} else {
2077
extract(dc, in, true);
2078
#ifdef DEBUG_JOIN_TRAM
2079
std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
2080
#endif
2081
}
2082
}
2083
}
2084
incoming.clear();
2085
}
2086
NBEdge* lastRoad = roads.back().second.first;
2087
if (erasedLast) {
2088
// copy to avoid concurrent modification
2089
auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
2090
for (NBEdge* out : outEdges) {
2091
if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
2092
if (lastRoad->getToNode() != out->getToNode()) {
2093
out->reinitNodes(lastRoad->getToNode(), out->getToNode());
2094
} else {
2095
extract(dc, out, true);
2096
#ifdef DEBUG_JOIN_TRAM
2097
std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
2098
#endif
2099
2100
}
2101
}
2102
}
2103
} else {
2104
replacement.push_back(tramEdge);
2105
}
2106
// update ptstops and ptlines
2107
sc.replaceEdge(tramEdgeID, replacement);
2108
lc.replaceEdge(tramEdgeID, replacement);
2109
}
2110
}
2111
2112
return numJoined;
2113
}
2114
2115
2116
EdgeVector
2117
NBEdgeCont::getAllEdges() const {
2118
EdgeVector result;
2119
for (auto item : myEdges) {
2120
item.second->setNumericalID((int)result.size());
2121
result.push_back(item.second);
2122
}
2123
return result;
2124
}
2125
2126
RouterEdgeVector
2127
NBEdgeCont::getAllRouterEdges() const {
2128
EdgeVector all = getAllEdges();
2129
return RouterEdgeVector(all.begin(), all.end());
2130
}
2131
2132
bool
2133
NBEdgeCont::checkConsistency(const NBNodeCont& nc) {
2134
bool ok = true;
2135
for (const auto& item : myEdges) {
2136
NBEdge* e = item.second;
2137
if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2138
WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2139
ok = false;
2140
}
2141
if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2142
WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2143
ok = false;
2144
}
2145
2146
}
2147
return ok;
2148
}
2149
2150
2151
void
2152
NBEdgeCont::fixSplitCustomLength() {
2153
for (auto item : myEdges) {
2154
NBEdge* e = item.second;
2155
if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2156
// subtract half the length of the longest incoming / outgoing connection
2157
double maxLengthOut = 0;
2158
for (const NBEdge::Connection& c : e->getConnections()) {
2159
maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2160
}
2161
double maxLengthIn = 0;
2162
for (const NBEdge* in : e->getIncomingEdges()) {
2163
for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2164
maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2165
}
2166
}
2167
e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2168
}
2169
}
2170
}
2171
2172
void
2173
NBEdgeCont::computeAngles() {
2174
for (auto item : myEdges) {
2175
item.second->computeAngle();
2176
}
2177
}
2178
2179
2180
std::set<std::string>
2181
NBEdgeCont::getUsedTypes() const {
2182
std::set<std::string> result;
2183
for (auto item : myEdges) {
2184
if (item.second->getTypeID() != "") {
2185
result.insert(item.second->getTypeID());
2186
}
2187
}
2188
return result;
2189
}
2190
2191
2192
int
2193
NBEdgeCont::removeEdgesBySpeed(NBDistrictCont& dc) {
2194
EdgeSet toRemove;
2195
for (auto item : myEdges) {
2196
NBEdge* edge = item.second;
2197
// remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
2198
if (edge->getSpeed() < myEdgesMinSpeed) {
2199
toRemove.insert(edge);
2200
}
2201
}
2202
int numRemoved = 0;
2203
for (NBEdge* edge : toRemove) {
2204
// explicit whitelist overrides removal
2205
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2206
extract(dc, edge);
2207
numRemoved++;
2208
}
2209
}
2210
return numRemoved;
2211
}
2212
2213
2214
int
2215
NBEdgeCont::removeEdgesByPermissions(NBDistrictCont& dc) {
2216
EdgeSet toRemove;
2217
for (auto item : myEdges) {
2218
NBEdge* edge = item.second;
2219
// check whether the edge shall be removed because it does not allow any of the wished classes
2220
if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
2221
toRemove.insert(edge);
2222
}
2223
// check whether the edge shall be removed due to allowing unwished classes only
2224
if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
2225
toRemove.insert(edge);
2226
}
2227
}
2228
int numRemoved = 0;
2229
for (NBEdge* edge : toRemove) {
2230
// explicit whitelist overrides removal
2231
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2232
extract(dc, edge);
2233
numRemoved++;
2234
}
2235
}
2236
return numRemoved;
2237
}
2238
2239
2240
int
2241
NBEdgeCont::removeLanesByWidth(NBDistrictCont& dc, const double minWidth) {
2242
EdgeSet toRemove;
2243
for (auto item : myEdges) {
2244
NBEdge* const edge = item.second;
2245
std::vector<int> indices;
2246
int idx = 0;
2247
for (const auto& lane : edge->getLanes()) {
2248
if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
2249
indices.push_back(idx);
2250
}
2251
idx++;
2252
}
2253
if ((int)indices.size() == edge->getNumLanes()) {
2254
toRemove.insert(edge);
2255
} else {
2256
std::reverse(indices.begin(), indices.end());
2257
for (const int i : indices) {
2258
edge->deleteLane(i, false, true);
2259
}
2260
}
2261
}
2262
int numRemoved = 0;
2263
for (NBEdge* edge : toRemove) {
2264
// explicit whitelist overrides removal
2265
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2266
extract(dc, edge);
2267
numRemoved++;
2268
}
2269
}
2270
return numRemoved;
2271
}
2272
2273
2274
int
2275
NBEdgeCont::attachRemoved(NBNodeCont& nc, NBDistrictCont& dc, const double maxDist) {
2276
int numSplit = 0;
2277
std::map<std::string, std::vector<std::string> > node2edge;
2278
for (auto item : myEdges) {
2279
if (item.second->hasParameter(SUMO_PARAM_REMOVED_NODES)) {
2280
for (std::string& nodeID : StringTokenizer(item.second->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2281
node2edge[nodeID].push_back(item.first);
2282
}
2283
}
2284
}
2285
for (auto item : nc) {
2286
NBNode* n = item.second;
2287
auto itRN = node2edge.find(n->getID());
2288
if (itRN != node2edge.end()) {
2289
bool rebuildConnections = false;
2290
// make a copy because we modify the original
2291
std::vector<std::string> edgeIDs = itRN->second;
2292
for (const std::string& eID : edgeIDs) {
2293
NBEdge* edge = retrieve(eID);
2294
assert(edge != nullptr);
2295
const double dist = edge->getGeometry().distance2D(n->getPosition(), true);
2296
if (dist != GeomHelper::INVALID_OFFSET && dist <= maxDist) {
2297
std::string idAfter = edge->getID();
2298
int index = 1;
2299
size_t spos = idAfter.find("#");
2300
if (spos != std::string::npos && spos > 1) {
2301
idAfter = idAfter.substr(0, spos);
2302
}
2303
while (retrieve(idAfter + "#" + toString(index), true) != nullptr) {
2304
index++;
2305
}
2306
idAfter += "#" + toString(index);
2307
const bool ok = splitAt(dc, edge, n, edge->getID(), idAfter, edge->getNumLanes(), edge->getNumLanes());
2308
if (ok) {
2309
rebuildConnections = true;
2310
numSplit++;
2311
NBEdge* secondEdge = retrieve(eID); // original was extracted on splitting
2312
for (std::string& nodeID : StringTokenizer(secondEdge->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2313
node2edge[nodeID].push_back(idAfter);
2314
}
2315
}
2316
}
2317
}
2318
if (rebuildConnections) {
2319
for (NBEdge* e : n->getIncomingEdges()) {
2320
e->invalidateConnections(true);
2321
}
2322
}
2323
}
2324
}
2325
return numSplit;
2326
}
2327
2328
/****************************************************************************/
2329
2330