Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBEdgeCont.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 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().length() / edge->getLoadedLength();
656
}
657
std::pair<PositionVector, PositionVector> geoms = edge->getGeometry().splitAt(geomPos);
658
// reduce inaccuracies and preserve bidi
659
if (geoms.first[-1].almostSame(node->getPosition()) || edge->isBidi()) {
660
geoms.first[-1] = node->getPosition();
661
geoms.second[0] = node->getPosition();
662
}
663
// build and insert the edges
664
NBEdge* one = new NBEdge(firstEdgeName, edge->myFrom, node, edge, geoms.first, noLanesFirstEdge);
665
NBEdge* two = new NBEdge(secondEdgeName, node, edge->myTo, edge, geoms.second, noLanesSecondEdge);
666
if (OptionsCont::getOptions().getBool("output.original-names")) {
667
const std::string origID = edge->getLaneStruct(0).getParameter(SUMO_PARAM_ORIGID, edge->getID());
668
if (firstEdgeName != origID) {
669
one->setOrigID(origID, false);
670
}
671
if (secondEdgeName != origID) {
672
two->setOrigID(origID, false);
673
}
674
}
675
two->copyConnectionsFrom(edge);
676
if (speed != -1.) {
677
two->setSpeed(-1, speed);
678
}
679
if (friction != -1.) {
680
two->setFriction(-1, friction);
681
}
682
if (edge->getDistance() != 0) {
683
one->setDistance(edge->getDistance());
684
two->setDistance(one->getDistance() + pos);
685
}
686
if (edge->hasLoadedLength()) {
687
one->setLoadedLength(pos);
688
two->setLoadedLength(edge->getLoadedLength() - pos);
689
}
690
// replace information about this edge within the nodes
691
edge->myFrom->replaceOutgoing(edge, one, 0);
692
edge->myTo->replaceIncoming(edge, two, 0);
693
// patch tls
694
for (NBTrafficLightDefinition* const tld : edge->myFrom->getControllingTLS()) {
695
tld->replaceRemoved(edge, -1, one, -1, false);
696
}
697
for (NBTrafficLightDefinition* const tld : edge->myTo->getControllingTLS()) {
698
tld->replaceRemoved(edge, -1, two, -1, true);
699
}
700
// the edge is now occuring twice in both nodes...
701
// clean up
702
edge->myFrom->removeDoubleEdges();
703
edge->myTo->removeDoubleEdges();
704
// add connections from the first to the second edge
705
// there will be as many connections as there are lanes on the second edge
706
// by default lanes will be added / discontinued on the right side
707
// (appropriate for highway on-/off-ramps)
708
const int offset = (int)one->getNumLanes() - (int)two->getNumLanes() + changedLeft;
709
for (int i2 = 0; i2 < (int)two->getNumLanes(); i2++) {
710
const int i1 = MIN2(MAX2((int)0, i2 + offset), (int)one->getNumLanes());
711
if (!one->addLane2LaneConnection(i1, two, i2, NBEdge::Lane2LaneInfoType::COMPUTED)) {
712
throw ProcessError(TL("Could not set connection!"));
713
}
714
}
715
if (myRemoveEdgesAfterLoading) {
716
if (myEdges2Keep.count(edge->getID()) != 0) {
717
myEdges2Keep.insert(one->getID());
718
myEdges2Keep.insert(two->getID());
719
}
720
if (myEdges2Remove.count(edge->getID()) != 0) {
721
myEdges2Remove.insert(one->getID());
722
myEdges2Remove.insert(two->getID());
723
}
724
}
725
// erase the splitted edge
726
patchRoundabouts(edge, one, two, myRoundabouts);
727
patchRoundabouts(edge, one, two, myGuessedRoundabouts);
728
const std::string oldID = edge->getID();
729
extract(dc, edge, true);
730
insert(one, true); // duplicate id check happened earlier
731
insert(two, true); // duplicate id check happened earlier
732
myEdgesSplit[edge] = {one, two};
733
myWasSplit.insert(one);
734
myWasSplit.insert(two);
735
return true;
736
}
737
738
739
void
740
NBEdgeCont::patchRoundabouts(NBEdge* orig, NBEdge* part1, NBEdge* part2, std::set<EdgeSet>& roundabouts) {
741
std::set<EdgeSet> addLater;
742
for (std::set<EdgeSet>::iterator it = roundabouts.begin(); it != roundabouts.end(); ++it) {
743
EdgeSet roundaboutSet = *it;
744
if (roundaboutSet.count(orig) > 0) {
745
roundaboutSet.erase(orig);
746
roundaboutSet.insert(part1);
747
roundaboutSet.insert(part2);
748
}
749
addLater.insert(roundaboutSet);
750
}
751
roundabouts.clear();
752
roundabouts.insert(addLater.begin(), addLater.end());
753
}
754
755
756
// ----- container access methods
757
std::vector<std::string>
758
NBEdgeCont::getAllNames() const {
759
std::vector<std::string> ret;
760
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
761
ret.push_back((*i).first);
762
}
763
return ret;
764
}
765
766
767
NBEdge*
768
NBEdgeCont::getSplitBase(const std::string& edgeID) const {
769
NBEdge* longest = nullptr;
770
for (auto item : myEdgesSplit) {
771
if (item.first->getID() == edgeID) {
772
if (longest == nullptr || longest->getLoadedLength() < item.first->getLoadedLength()) {
773
longest = const_cast<NBEdge*>(item.first);
774
}
775
}
776
}
777
return longest;
778
}
779
780
// ----- Adapting the input
781
int
782
NBEdgeCont::removeUnwishedEdges(NBDistrictCont& dc) {
783
EdgeVector toRemove;
784
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
785
NBEdge* edge = (*i).second;
786
if (!myEdges2Keep.count(edge->getID())) {
787
edge->getFromNode()->removeEdge(edge);
788
edge->getToNode()->removeEdge(edge);
789
toRemove.push_back(edge);
790
}
791
}
792
for (EdgeVector::iterator j = toRemove.begin(); j != toRemove.end(); ++j) {
793
erase(dc, *j);
794
}
795
return (int)toRemove.size();
796
}
797
798
799
void
800
NBEdgeCont::splitGeometry(NBDistrictCont& dc, NBNodeCont& nc) {
801
// make a copy of myEdges because splitting will modify it
802
EdgeCont edges = myEdges;
803
for (auto& item : edges) {
804
NBEdge* edge = item.second;
805
if (edge->getGeometry().size() < 3) {
806
continue;
807
}
808
PositionVector geom = edge->getGeometry();
809
const std::string id = edge->getID();
810
double offset = 0;
811
for (int i = 1; i < (int)geom.size() - 1; i++) {
812
offset += geom[i - 1].distanceTo(geom[i]);
813
std::string nodeID = id + "." + toString((int)offset);
814
if (!nc.insert(nodeID, geom[i])) {
815
WRITE_WARNING("Could not split geometry of edge '" + id + "' at index " + toString(i));
816
continue;
817
}
818
NBNode* node = nc.retrieve(nodeID);
819
splitAt(dc, edge, node, edge->getID(), nodeID, edge->getNumLanes(), edge->getNumLanes());
820
edge = retrieve(nodeID);
821
}
822
}
823
}
824
825
826
void
827
NBEdgeCont::reduceGeometries(const double minDist) {
828
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
829
(*i).second->reduceGeometry(minDist);
830
}
831
}
832
833
834
void
835
NBEdgeCont::checkGeometries(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool fixRailways, bool silent) {
836
if (maxAngle > 0 || minRadius > 0) {
837
for (auto& item : myEdges) {
838
if ((item.second->getPermissions() & (SVC_PUBLIC_CLASSES | SVC_PASSENGER)) == 0) {
839
continue;
840
}
841
item.second->checkGeometry(maxAngle, fixAngle, minRadius, fix || (fixRailways && isRailway(item.second->getPermissions())), silent);
842
}
843
}
844
}
845
846
847
// ----- processing methods
848
void
849
NBEdgeCont::clearControllingTLInformation() const {
850
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); i++) {
851
(*i).second->clearControllingTLInformation();
852
}
853
}
854
855
856
void
857
NBEdgeCont::sortOutgoingLanesConnections() {
858
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
859
(*i).second->sortOutgoingConnectionsByAngle();
860
}
861
}
862
863
864
void
865
NBEdgeCont::computeEdge2Edges(bool noLeftMovers) {
866
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
867
(*i).second->computeEdge2Edges(noLeftMovers);
868
}
869
}
870
871
872
void
873
NBEdgeCont::computeLanes2Edges() {
874
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
875
(*i).second->computeLanes2Edges();
876
}
877
}
878
879
880
void
881
NBEdgeCont::recheckLanes() {
882
const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
883
for (const auto& edgeIt : myEdges) {
884
NBEdge* const edge = edgeIt.second;
885
edge->recheckLanes();
886
edge->recheckOpposite(*this, fixOppositeLengths);
887
}
888
}
889
890
891
void
892
NBEdgeCont::appendTurnarounds(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike) {
893
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
894
(*i).second->appendTurnaround(noTLSControlled, noFringe, onlyDeadends, onlyTurnlane, noGeometryLike, true);
895
}
896
}
897
898
899
void
900
NBEdgeCont::appendTurnarounds(const std::set<std::string>& ids, bool noTLSControlled) {
901
for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); it++) {
902
myEdges[*it]->appendTurnaround(noTLSControlled, false, false, false, false, false);
903
}
904
}
905
906
907
void
908
NBEdgeCont::appendRailwayTurnarounds(const NBPTStopCont& sc) {
909
std::set<std::string> stopEdgeIDs;
910
for (auto& stopItem : sc.getStops()) {
911
stopEdgeIDs.insert(stopItem.second->getEdgeId());
912
}
913
for (auto& item : myEdges) {
914
NBEdge* edge = item.second;
915
if (edge->isBidiRail()
916
&& (stopEdgeIDs.count(item.first) > 0 ||
917
stopEdgeIDs.count(edge->getTurnDestination(true)->getID()) > 0)) {
918
NBEdge* to = edge->getTurnDestination(true);
919
assert(to != 0);
920
edge->setConnection(edge->getNumLanes() - 1,
921
to, to->getNumLanes() - 1, NBEdge::Lane2LaneInfoType::VALIDATED, false, false,
922
KEEPCLEAR_UNSPECIFIED,
923
NBEdge::UNSPECIFIED_CONTPOS, NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE,
924
SUMO_const_haltingSpeed);
925
}
926
}
927
}
928
929
void
930
NBEdgeCont::computeEdgeShapes(double smoothElevationThreshold) {
931
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
932
(*i).second->computeEdgeShape(smoothElevationThreshold);
933
}
934
// equalize length of opposite edges
935
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); i++) {
936
NBEdge* edge = i->second;
937
const std::string& oppositeID = edge->getLanes().back().oppositeID;
938
if (oppositeID != "" && oppositeID != "-") {
939
NBEdge* oppEdge = retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
940
if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
941
continue;
942
}
943
if (fabs(oppEdge->getLength() - edge->getLength()) > NUMERICAL_EPS) {
944
double avgLength = (oppEdge->getLength() + edge->getLength()) / 2;
945
edge->setAverageLengthWithOpposite(avgLength);
946
oppEdge->setAverageLengthWithOpposite(avgLength);
947
}
948
}
949
}
950
}
951
952
953
void
954
NBEdgeCont::computeLaneShapes() {
955
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
956
(*i).second->computeLaneShapes();
957
}
958
}
959
960
961
void
962
NBEdgeCont::joinSameNodeConnectingEdges(NBDistrictCont& dc,
963
NBTrafficLightLogicCont& tlc,
964
EdgeVector edges) {
965
// !!! Attention!
966
// No merging of the geometry to come is being done
967
// The connections are moved from one edge to another within
968
// the replacement where the edge is a node's incoming edge.
969
970
// count the number of lanes, the speed and the id
971
int nolanes = 0;
972
double speed = 0;
973
int priority = -1;
974
bool joinEdges = true;
975
std::string id;
976
sort(edges.begin(), edges.end(), NBContHelper::same_connection_edge_sorter());
977
// retrieve the connected nodes
978
NBEdge* tpledge = *(edges.begin());
979
NBNode* from = tpledge->getFromNode();
980
NBNode* to = tpledge->getToNode();
981
EdgeVector::const_iterator i;
982
int myPriority = (*edges.begin())->getPriority();
983
for (i = edges.begin(); i != edges.end(); i++) {
984
// some assertions
985
assert((*i)->getFromNode() == from);
986
assert((*i)->getToNode() == to);
987
// ad the number of lanes the current edge has
988
nolanes += (*i)->getNumLanes();
989
// build the id
990
if (i != edges.begin()) {
991
id += "+";
992
}
993
id += (*i)->getID();
994
// compute the speed
995
speed += (*i)->getSpeed();
996
// build the priority
997
// merged edges should have the same inherited priority
998
if (myPriority == (*i)->getPriority()) {
999
priority = myPriority;
1000
} else {
1001
priority = -1;
1002
joinEdges = false;
1003
}
1004
}
1005
if (joinEdges) {
1006
speed /= (double)edges.size();
1007
// build the new edge
1008
NBEdge* newEdge = new NBEdge(id, from, to, "", speed, NBEdge::UNSPECIFIED_FRICTION, nolanes, priority,
1009
NBEdge::UNSPECIFIED_WIDTH, NBEdge::UNSPECIFIED_OFFSET,
1010
tpledge->myLaneSpreadFunction, tpledge->getStreetName());
1011
// copy lane attributes
1012
int laneIndex = 0;
1013
for (i = edges.begin(); i != edges.end(); ++i) {
1014
const std::vector<NBEdge::Lane>& lanes = (*i)->getLanes();
1015
for (int j = 0; j < (int)lanes.size(); ++j) {
1016
newEdge->setPermissions(lanes[j].permissions, laneIndex);
1017
newEdge->setLaneWidth(laneIndex, lanes[j].width);
1018
newEdge->setEndOffset(laneIndex, lanes[j].endOffset);
1019
laneIndex++;
1020
}
1021
}
1022
insert(newEdge, true);
1023
// replace old edge by current within the nodes
1024
// and delete the old
1025
from->replaceOutgoing(edges, newEdge);
1026
to->replaceIncoming(edges, newEdge);
1027
// patch connections
1028
// add edge2edge-information
1029
for (i = edges.begin(); i != edges.end(); i++) {
1030
EdgeVector ev = (*i)->getConnectedEdges();
1031
for (EdgeVector::iterator j = ev.begin(); j != ev.end(); j++) {
1032
newEdge->addEdge2EdgeConnection(*j);
1033
}
1034
}
1035
// copy outgoing connections to the new edge
1036
int currLane = 0;
1037
for (i = edges.begin(); i != edges.end(); i++) {
1038
newEdge->moveOutgoingConnectionsFrom(*i, currLane);
1039
currLane += (*i)->getNumLanes();
1040
}
1041
// patch tl-information
1042
currLane = 0;
1043
for (i = edges.begin(); i != edges.end(); i++) {
1044
int noLanes = (*i)->getNumLanes();
1045
for (int j = 0; j < noLanes; j++, currLane++) {
1046
// replace in traffic lights
1047
tlc.replaceRemoved(*i, j, newEdge, currLane, true);
1048
tlc.replaceRemoved(*i, j, newEdge, currLane, false);
1049
}
1050
}
1051
// delete joined edges
1052
for (i = edges.begin(); i != edges.end(); i++) {
1053
extract(dc, *i, true);
1054
}
1055
}
1056
}
1057
1058
1059
void
1060
NBEdgeCont::guessOpposites() {
1061
//@todo magic values
1062
const bool fixOppositeLengths = OptionsCont::getOptions().getBool("opposites.guess.fix-lengths");
1063
// ensure consistency of loaded values before starting to guess
1064
for (const auto& edgeIt : myEdges) {
1065
NBEdge* const edge = edgeIt.second;
1066
edge->recheckOpposite(*this, fixOppositeLengths);
1067
}
1068
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1069
NBEdge* edge = i->second;
1070
edge->guessOpposite();
1071
}
1072
}
1073
1074
1075
void
1076
NBEdgeCont::recheckLaneSpread() {
1077
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1078
NBEdge* opposite = getOppositeByID(i->first);
1079
if (opposite != nullptr) {
1080
i->second->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1081
opposite->setLaneSpreadFunction(LaneSpreadFunction::RIGHT);
1082
} else {
1083
i->second->setLaneSpreadFunction(LaneSpreadFunction::CENTER);
1084
}
1085
}
1086
}
1087
1088
1089
NBEdge*
1090
NBEdgeCont::getOppositeByID(const std::string& edgeID) const {
1091
const std::string oppositeID = edgeID[0] == '-' ? edgeID.substr(1) : "-" + edgeID;
1092
EdgeCont::const_iterator it = myEdges.find(oppositeID);
1093
return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1094
}
1095
1096
NBEdge*
1097
NBEdgeCont::getByID(const std::string& edgeID) const {
1098
EdgeCont::const_iterator it = myEdges.find(edgeID);
1099
return it != myEdges.end() ? it->second : (NBEdge*)nullptr;
1100
}
1101
1102
// ----- other
1103
void
1104
NBEdgeCont::addPostProcessConnection(const std::string& from, int fromLane, const std::string& to, int toLane, bool mayDefinitelyPass,
1105
KeepClear keepClear, double contPos, double visibility, double speed, double friction, double length,
1106
const PositionVector& customShape, bool uncontrolled, bool warnOnly,
1107
SVCPermissions permissions, bool indirectLeft, const std::string& edgeType, SVCPermissions changeLeft, SVCPermissions changeRight) {
1108
myConnections[from].push_back(PostProcessConnection(from, fromLane, to, toLane, mayDefinitelyPass, keepClear, contPos, visibility,
1109
speed, friction, length, customShape, uncontrolled, warnOnly, permissions, indirectLeft, edgeType, changeLeft, changeRight));
1110
}
1111
1112
bool
1113
NBEdgeCont::hasPostProcessConnection(const std::string& from, const std::string& to) {
1114
if (myConnections.count(from) == 0) {
1115
return false;
1116
} else {
1117
if (to == "") {
1118
// wildcard
1119
return true;
1120
}
1121
for (const auto& ppc : myConnections[from]) {
1122
if (ppc.to == to) {
1123
return true;
1124
}
1125
}
1126
return false;
1127
}
1128
}
1129
1130
void
1131
NBEdgeCont::recheckPostProcessConnections() {
1132
const bool warnOnly = OptionsCont::getOptions().exists("ignore-errors.connections") && OptionsCont::getOptions().getBool("ignore-errors.connections");
1133
for (const auto& item : myConnections) {
1134
for (std::vector<PostProcessConnection>::const_iterator i = item.second.begin(); i != item.second.end(); ++i) {
1135
NBEdge* from = retrievePossiblySplit((*i).from, true);
1136
NBEdge* to = retrievePossiblySplit((*i).to, false);
1137
if (from == nullptr || to == nullptr ||
1138
!from->addLane2LaneConnection((*i).fromLane, to, (*i).toLane, NBEdge::Lane2LaneInfoType::USER, true, (*i).mayDefinitelyPass,
1139
(*i).keepClear, (*i).contPos, (*i).visibility, (*i).speed, (*i).friction, (*i).customLength, (*i).customShape,
1140
(*i).uncontrolled, (*i).permissions, (*i).indirectLeft, (*i).edgeType, (*i).changeLeft, (*i).changeRight,
1141
true)) {
1142
const std::string msg = "Could not insert connection between '" + (*i).from + "' and '" + (*i).to + "' after build.";
1143
if (warnOnly || (*i).warnOnly) {
1144
WRITE_WARNING(msg);
1145
} else {
1146
WRITE_ERROR(msg);
1147
}
1148
}
1149
}
1150
}
1151
// during loading we also kept some ambiguous connections in hope they might be valid after processing
1152
// we need to make sure that all invalid connections are removed now
1153
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); ++it) {
1154
NBEdge* edge = it->second;
1155
NBNode* to = edge->getToNode();
1156
// make a copy because we may delete connections
1157
std::vector<NBEdge::Connection> connections = edge->getConnections();
1158
for (std::vector<NBEdge::Connection>::iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1159
NBEdge::Connection& c = *it_con;
1160
if (c.toEdge != nullptr && c.toEdge->getFromNode() != to) {
1161
WRITE_WARNING("Found and removed invalid connection from edge '" + edge->getID() +
1162
"' to edge '" + c.toEdge->getID() + "' via junction '" + to->getID() + "'.");
1163
edge->removeFromConnections(c.toEdge);
1164
}
1165
}
1166
}
1167
}
1168
1169
1170
EdgeVector
1171
NBEdgeCont::getGeneratedFrom(const std::string& id) const {
1172
int len = (int)id.length();
1173
EdgeVector ret;
1174
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1175
std::string curr = (*i).first;
1176
// the next check makes it possibly faster - we don not have
1177
// to compare the names
1178
if ((int)curr.length() <= len) {
1179
continue;
1180
}
1181
// the name must be the same as the given id but something
1182
// beginning with a '[' must be appended to it
1183
if (curr.substr(0, len) == id && curr[len] == '[') {
1184
ret.push_back((*i).second);
1185
continue;
1186
}
1187
// ok, maybe the edge is a compound made during joining of edges
1188
std::string::size_type pos = curr.find(id);
1189
// surely not
1190
if (pos == std::string::npos) {
1191
continue;
1192
}
1193
// check leading char
1194
if (pos > 0) {
1195
if (curr[pos - 1] != ']' && curr[pos - 1] != '+') {
1196
// actually, this is another id
1197
continue;
1198
}
1199
}
1200
if (pos + id.length() < curr.length()) {
1201
if (curr[pos + id.length()] != '[' && curr[pos + id.length()] != '+') {
1202
// actually, this is another id
1203
continue;
1204
}
1205
}
1206
ret.push_back((*i).second);
1207
}
1208
return ret;
1209
}
1210
1211
1212
int
1213
NBEdgeCont::guessRoundabouts() {
1214
myGuessedRoundabouts.clear();
1215
std::set<NBEdge*> loadedRoundaboutEdges;
1216
for (std::set<EdgeSet>::const_iterator it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1217
loadedRoundaboutEdges.insert(it->begin(), it->end());
1218
}
1219
// step 1: keep only those edges which have no turnarounds and which are not
1220
// part of a loaded roundabout
1221
std::set<NBEdge*> candidates;
1222
SVCPermissions valid = SVCAll & ~SVC_PEDESTRIAN;
1223
for (EdgeCont::const_iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1224
NBEdge* e = (*i).second;
1225
NBNode* const to = e->getToNode();
1226
if (e->getTurnDestination() == nullptr
1227
&& to->getConnectionTo(e->getFromNode()) == nullptr
1228
&& (e->getPermissions() & valid) != 0) {
1229
candidates.insert(e);
1230
}
1231
}
1232
1233
// step 2:
1234
std::set<NBEdge*> visited;
1235
for (std::set<NBEdge*>::const_iterator i = candidates.begin(); i != candidates.end(); ++i) {
1236
EdgeVector loopEdges;
1237
// start with a random edge (this doesn't have to be a roundabout edge)
1238
// loop over connected edges (using always the leftmost one)
1239
// and keep the list in loopEdges
1240
// continue until we loop back onto a loopEdges and extract the loop
1241
NBEdge* e = (*i);
1242
if (visited.count(e) > 0) {
1243
// already seen
1244
continue;
1245
}
1246
loopEdges.push_back(e);
1247
bool doLoop = true;
1248
#ifdef DEBUG_GUESS_ROUNDABOUT
1249
gDebugFlag1 = e->getID() == DEBUG_EDGE_ID;
1250
#endif
1251
do {
1252
#ifdef DEBUG_GUESS_ROUNDABOUT
1253
if (gDebugFlag1) {
1254
std::cout << " e=" << e->getID() << " loopEdges=" << toString(loopEdges) << "\n";
1255
gDebugFlag1 = true;
1256
}
1257
#endif
1258
visited.insert(e);
1259
const EdgeVector& edges = e->getToNode()->getEdges();
1260
if ((e->getToNode()->getType() == SumoXMLNodeType::RIGHT_BEFORE_LEFT || e->getToNode()->getType() == SumoXMLNodeType::LEFT_BEFORE_RIGHT)
1261
&& !e->getToNode()->typeWasGuessed()) {
1262
doLoop = false;
1263
#ifdef DEBUG_GUESS_ROUNDABOUT
1264
if (gDebugFlag1) {
1265
std::cout << " rbl\n";
1266
}
1267
gDebugFlag1 = false;
1268
#endif
1269
break;
1270
}
1271
if (edges.size() < 2) {
1272
doLoop = false;
1273
#ifdef DEBUG_GUESS_ROUNDABOUT
1274
if (gDebugFlag1) {
1275
std::cout << " deadend\n";
1276
}
1277
gDebugFlag1 = false;
1278
#endif
1279
break;
1280
}
1281
if (e->getTurnDestination(true) != nullptr || e->getToNode()->getConnectionTo(e->getFromNode()) != nullptr) {
1282
// do not follow turn-arounds while in a (tentative) loop
1283
doLoop = false;
1284
#ifdef DEBUG_GUESS_ROUNDABOUT
1285
if (gDebugFlag1) {
1286
std::cout << " invalid turnAround e=" << e->getID() << " dest=" << Named::getIDSecure(e->getTurnDestination()) << "\n";
1287
}
1288
gDebugFlag1 = false;
1289
#endif
1290
break;
1291
}
1292
EdgeVector::const_iterator me = std::find(edges.begin(), edges.end(), e);
1293
NBContHelper::nextCW(edges, me);
1294
NBEdge* left = *me;
1295
while ((left->getPermissions() & valid) == 0 && left != e) {
1296
NBContHelper::nextCW(edges, me);
1297
left = *me;
1298
}
1299
if (left == e) {
1300
// no usable continuation edge found
1301
doLoop = false;
1302
#ifdef DEBUG_GUESS_ROUNDABOUT
1303
if (gDebugFlag1) {
1304
std::cout << " noContinuation\n";
1305
}
1306
gDebugFlag1 = false;
1307
#endif
1308
break;
1309
}
1310
NBContHelper::nextCW(edges, me);
1311
NBEdge* nextLeft = *me;
1312
double angle = fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), left->getAngleAtNode(e->getToNode())));
1313
double nextAngle = nextLeft == e ? 180 : fabs(NBHelpers::relAngle(e->getAngleAtNode(e->getToNode()), nextLeft->getAngleAtNode(e->getToNode())));
1314
#ifdef DEBUG_GUESS_ROUNDABOUT
1315
if (gDebugFlag1) {
1316
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";
1317
}
1318
#endif
1319
// there should be no straigher edge further left
1320
if (angle >= 90 && nextAngle < 45) {
1321
doLoop = false;
1322
#ifdef DEBUG_GUESS_ROUNDABOUT
1323
if (gDebugFlag1) {
1324
std::cout << " failed nextAngle=" << nextAngle << "\n";
1325
}
1326
gDebugFlag1 = false;
1327
#endif
1328
break;
1329
}
1330
// roundabouts do not have sharp turns (or they wouldn't be called 'round')
1331
// 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)
1332
if (angle >= 90) {
1333
double edgeAngle = fabs(NBHelpers::relAngle(e->getStartAngle(), e->getEndAngle()));
1334
double edgeAngle2 = fabs(NBHelpers::relAngle(left->getStartAngle(), left->getEndAngle()));
1335
double edgeRadius = e->getGeometry().length2D() / DEG2RAD(edgeAngle);
1336
double edgeRadius2 = left->getGeometry().length2D() / DEG2RAD(edgeAngle2);
1337
const double avgRadius = 0.5 * (edgeRadius + edgeRadius2);
1338
double junctionRadius = e->getLaneShape(0).back().distanceTo2D(left->getLaneShape(0).front()) / DEG2RAD(angle);
1339
//std::cout << " junction=" << e->getToNode()->getID() << " e=" << e->getID() << " left=" << left->getID() << " angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1340
if (junctionRadius < 0.8 * avgRadius) {
1341
doLoop = false;
1342
#ifdef DEBUG_GUESS_ROUNDABOUT
1343
if (gDebugFlag1) {
1344
std::cout << " failed angle=" << angle << " eRadius=" << edgeRadius << " eRadius2=" << edgeRadius2 << " jRadius3=" << junctionRadius << "\n";
1345
}
1346
gDebugFlag1 = false;
1347
#endif
1348
break;
1349
}
1350
}
1351
1352
EdgeVector::const_iterator loopClosed = std::find(loopEdges.begin(), loopEdges.end(), left);
1353
const int loopSize = (int)(loopEdges.end() - loopClosed);
1354
if (loopSize > 0) {
1355
// loop found
1356
if (loopSize < 3) {
1357
doLoop = false; // need at least 3 edges for a roundabout
1358
} else if (loopSize < (int)loopEdges.size()) {
1359
// remove initial edges not belonging to the loop
1360
EdgeVector(loopEdges.begin() + (loopEdges.size() - loopSize), loopEdges.end()).swap(loopEdges);
1361
}
1362
// count attachments to the outside. need at least 3 or a roundabout doesn't make much sense
1363
int attachments = 0;
1364
for (EdgeVector::const_iterator j = loopEdges.begin(); j != loopEdges.end(); ++j) {
1365
if ((*j)->getToNode()->getEdges().size() > 2) {
1366
attachments++;
1367
}
1368
}
1369
if (attachments < 3) {
1370
doLoop = false;
1371
#ifdef DEBUG_GUESS_ROUNDABOUT
1372
if (gDebugFlag1) {
1373
std::cout << " attachments=" << attachments << "\n";
1374
}
1375
gDebugFlag1 = false;
1376
#endif
1377
}
1378
break;
1379
}
1380
if (visited.count(left) > 0) {
1381
doLoop = false;
1382
} else {
1383
// keep going
1384
loopEdges.push_back(left);
1385
e = left;
1386
}
1387
} while (doLoop);
1388
if (doLoop) {
1389
// check form factor to avoid elongated shapes (circle: 1, square: ~0.79)
1390
#ifdef DEBUG_GUESS_ROUNDABOUT
1391
if (gDebugFlag1) {
1392
std::cout << " formFactor=" << formFactor(loopEdges) << "\n";
1393
}
1394
#endif
1395
double loopLength = 0;
1396
for (const NBEdge* const le : loopEdges) {
1397
loopLength += le->getLoadedLength();
1398
}
1399
if (formFactor(loopEdges) > 0.6
1400
&& loopLength < OptionsCont::getOptions().getFloat("roundabouts.guess.max-length")) {
1401
// collected edges are marked in markRoundabouts
1402
EdgeSet guessed(loopEdges.begin(), loopEdges.end());
1403
if (loadedRoundaboutEdges.count(loopEdges.front()) != 0) {
1404
if (find(myRoundabouts.begin(), myRoundabouts.end(), guessed) == myRoundabouts.end()) {
1405
for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); it++) {
1406
if ((*it).count(loopEdges.front()) != 0) {
1407
WRITE_WARNINGF(TL("Replacing loaded roundabout '%' with '%'."), toString(*it), toString(guessed));
1408
myRoundabouts.erase(it);
1409
break;
1410
}
1411
}
1412
myGuessedRoundabouts.insert(guessed);
1413
}
1414
} else {
1415
myGuessedRoundabouts.insert(guessed);
1416
#ifdef DEBUG_GUESS_ROUNDABOUT
1417
if (gDebugFlag1) {
1418
std::cout << " foundRoundabout=" << toString(loopEdges) << "\n";
1419
}
1420
#endif
1421
}
1422
}
1423
}
1424
#ifdef DEBUG_GUESS_ROUNDABOUT
1425
gDebugFlag1 = false;
1426
#endif
1427
}
1428
return (int)myGuessedRoundabouts.size();
1429
}
1430
1431
1432
int
1433
NBEdgeCont::extractRoundabouts() {
1434
std::set<NBEdge*> candidateEdges;
1435
for (const auto& edge : myEdges) {
1436
NBEdge* const e = edge.second;
1437
if (e->getJunctionPriority(e->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT || e->getJunctionPriority(e->getFromNode()) == NBEdge::JunctionPriority::ROUNDABOUT) {
1438
candidateEdges.insert(e);
1439
}
1440
}
1441
std::set<NBEdge*> visited;
1442
int extracted = 0;
1443
for (const auto& edgeIt : candidateEdges) {
1444
EdgeVector loopEdges;
1445
NBEdge* e = edgeIt;
1446
if (visited.count(e) > 0) {
1447
// already seen
1448
continue;
1449
}
1450
loopEdges.push_back(e);
1451
bool doLoop = true;
1452
//
1453
do {
1454
if (std::find(visited.begin(), visited.end(), e) != visited.end()) {
1455
if (loopEdges.size() > 1) {
1456
addRoundabout(EdgeSet(loopEdges.begin(), loopEdges.end()));
1457
++extracted;
1458
}
1459
doLoop = false;
1460
break;
1461
}
1462
visited.insert(e);
1463
loopEdges.push_back(e);
1464
const EdgeVector& outgoingEdges = e->getToNode()->getOutgoingEdges();
1465
EdgeVector::const_iterator me = std::find_if(outgoingEdges.begin(), outgoingEdges.end(), [](const NBEdge * outgoingEdge) {
1466
return outgoingEdge->getJunctionPriority(outgoingEdge->getToNode()) == NBEdge::JunctionPriority::ROUNDABOUT;
1467
});
1468
if (me == outgoingEdges.end()) { // no closed loop
1469
doLoop = false;
1470
} else {
1471
e = *me;
1472
}
1473
} while (doLoop);
1474
}
1475
return extracted;
1476
}
1477
1478
1479
void
1480
NBEdgeCont::cleanupRoundabouts() {
1481
// only loaded roundabouts are of concern here since guessing comes later
1482
std::set<EdgeSet> validRoundabouts;
1483
std::set<NBEdge*> validEdges;
1484
for (auto item : myEdges) {
1485
validEdges.insert(item.second);
1486
}
1487
for (EdgeSet roundabout : myRoundabouts) {
1488
EdgeSet validRoundabout;
1489
for (NBEdge* cand : roundabout) {
1490
if (validEdges.count(cand) != 0) {
1491
validRoundabout.insert(cand);
1492
}
1493
}
1494
if (validRoundabout.size() > 0) {
1495
validRoundabouts.insert(validRoundabout);
1496
}
1497
}
1498
myRoundabouts = validRoundabouts;
1499
}
1500
1501
1502
double
1503
NBEdgeCont::formFactor(const EdgeVector& loopEdges) {
1504
// A circle (which maximizes area per circumference) has a formfactor of 1, non-circular shapes have a smaller value
1505
PositionVector points;
1506
for (EdgeVector::const_iterator it = loopEdges.begin(); it != loopEdges.end(); ++it) {
1507
points.append((*it)->getGeometry());
1508
}
1509
double circumference = points.length2D();
1510
return 4 * M_PI * points.area() / (circumference * circumference);
1511
}
1512
1513
1514
const std::set<EdgeSet>
1515
NBEdgeCont::getRoundabouts() const {
1516
std::set<EdgeSet> result = myRoundabouts;
1517
result.insert(myGuessedRoundabouts.begin(), myGuessedRoundabouts.end());
1518
return result;
1519
}
1520
1521
1522
void
1523
NBEdgeCont::addRoundabout(const EdgeSet& roundabout) {
1524
if (roundabout.size() > 0) {
1525
if (find(myRoundabouts.begin(), myRoundabouts.end(), roundabout) != myRoundabouts.end()) {
1526
WRITE_WARNING("Ignoring duplicate roundabout: " + toString(roundabout));
1527
} else {
1528
myRoundabouts.insert(roundabout);
1529
}
1530
}
1531
}
1532
1533
void
1534
NBEdgeCont::removeRoundabout(const NBNode* node) {
1535
for (auto it = myRoundabouts.begin(); it != myRoundabouts.end(); ++it) {
1536
for (NBEdge* e : *it) {
1537
if (e->getToNode() == node) {
1538
myRoundabouts.erase(it);
1539
return;
1540
}
1541
}
1542
}
1543
}
1544
1545
void
1546
NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove) {
1547
removeRoundaboutEdges(toRemove, myRoundabouts);
1548
removeRoundaboutEdges(toRemove, myGuessedRoundabouts);
1549
}
1550
1551
void
1552
NBEdgeCont::removeRoundaboutEdges(const EdgeSet& toRemove, std::set<EdgeSet>& roundabouts) {
1553
// members of a set are constant so we have to do some tricks
1554
std::vector<EdgeSet> rList;
1555
for (const EdgeSet& r : roundabouts) {
1556
EdgeSet r2;
1557
std::set_difference(r.begin(), r.end(), toRemove.begin(), toRemove.end(), std::inserter(r2, r2.end()));
1558
rList.push_back(r2);
1559
}
1560
roundabouts.clear();
1561
roundabouts.insert(rList.begin(), rList.end());
1562
}
1563
1564
1565
void
1566
NBEdgeCont::markRoundabouts() {
1567
for (const EdgeSet& roundaboutSet : getRoundabouts()) {
1568
for (NBEdge* const edge : roundaboutSet) {
1569
// disable turnarounds on incoming edges
1570
NBNode* const node = edge->getToNode();
1571
for (NBEdge* const inEdge : node->getIncomingEdges()) {
1572
if (roundaboutSet.count(inEdge) > 0) {
1573
continue;
1574
}
1575
if (inEdge->getStep() >= NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1576
continue;
1577
}
1578
if (inEdge->getTurnDestination() != nullptr) {
1579
inEdge->removeFromConnections(inEdge->getTurnDestination(), -1);
1580
} else {
1581
// also remove connections that are effecively a turnaround but
1582
// where not correctly detector due to geometrical quirks
1583
const std::vector<NBEdge::Connection> cons = inEdge->getConnections();
1584
for (const NBEdge::Connection& con : cons) {
1585
if (con.toEdge && roundaboutSet.count(con.toEdge) == 0) {
1586
const double angle = fabs(NBHelpers::normRelAngle(inEdge->getAngleAtNode(node), con.toEdge->getAngleAtNode(node)));
1587
if (angle > 160) {
1588
inEdge->removeFromConnections(con.toEdge, -1);
1589
}
1590
}
1591
}
1592
}
1593
1594
}
1595
// let the connections to succeeding roundabout edge have a higher priority
1596
edge->setJunctionPriority(node, NBEdge::JunctionPriority::ROUNDABOUT);
1597
edge->setJunctionPriority(edge->getFromNode(), NBEdge::JunctionPriority::ROUNDABOUT);
1598
node->setRoundabout();
1599
}
1600
}
1601
}
1602
1603
1604
void
1605
NBEdgeCont::generateStreetSigns() {
1606
for (EdgeCont::iterator i = myEdges.begin(); i != myEdges.end(); ++i) {
1607
NBEdge* e = i->second;
1608
const double offset = MAX2(0., e->getLength() - 3);
1609
if (e->getToNode()->isSimpleContinuation(false)) {
1610
// not a "real" junction?
1611
continue;
1612
}
1613
const SumoXMLNodeType nodeType = e->getToNode()->getType();
1614
switch (nodeType) {
1615
case SumoXMLNodeType::PRIORITY:
1616
// yield or major?
1617
if (e->getJunctionPriority(e->getToNode()) > 0) {
1618
e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1619
} else {
1620
e->addSign(NBSign(NBSign::SIGN_TYPE_YIELD, offset));
1621
}
1622
break;
1623
case SumoXMLNodeType::PRIORITY_STOP:
1624
// yield or major?
1625
if (e->getJunctionPriority(e->getToNode()) > 0) {
1626
e->addSign(NBSign(NBSign::SIGN_TYPE_PRIORITY, offset));
1627
} else {
1628
e->addSign(NBSign(NBSign::SIGN_TYPE_STOP, offset));
1629
}
1630
break;
1631
case SumoXMLNodeType::ALLWAY_STOP:
1632
e->addSign(NBSign(NBSign::SIGN_TYPE_ALLWAY_STOP, offset));
1633
break;
1634
case SumoXMLNodeType::RIGHT_BEFORE_LEFT:
1635
e->addSign(NBSign(NBSign::SIGN_TYPE_RIGHT_BEFORE_LEFT, offset));
1636
break;
1637
case SumoXMLNodeType::LEFT_BEFORE_RIGHT:
1638
e->addSign(NBSign(NBSign::SIGN_TYPE_LEFT_BEFORE_RIGHT, offset));
1639
break;
1640
default:
1641
break;
1642
}
1643
}
1644
}
1645
1646
1647
int
1648
NBEdgeCont::guessSpecialLanes(SUMOVehicleClass svc, double width, double minSpeed, double maxSpeed, bool fromPermissions, const std::string& excludeOpt,
1649
NBTrafficLightLogicCont& tlc) {
1650
int lanesCreated = 0;
1651
std::vector<std::string> edges;
1652
if (excludeOpt != "") {
1653
edges = OptionsCont::getOptions().getStringVector(excludeOpt);
1654
}
1655
std::set<std::string> exclude(edges.begin(), edges.end());
1656
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1657
NBEdge* edge = it->second;
1658
if (// not excluded
1659
exclude.count(edge->getID()) == 0
1660
// does not yet have a sidewalk/bikelane
1661
&& !edge->hasRestrictedLane(svc)
1662
// needs a sidewalk/bikelane
1663
&& ((edge->getPermissions() & ~SVC_VULNERABLE) != 0 || (edge->getPermissions() & svc) == 0)
1664
&& (
1665
// guess.from-permissions
1666
(fromPermissions && (edge->getPermissions() & svc) != 0)
1667
// guess from speed
1668
|| (!fromPermissions && edge->getSpeed() > minSpeed && edge->getSpeed() <= maxSpeed)
1669
)) {
1670
edge->addRestrictedLane(width, svc);
1671
lanesCreated += 1;
1672
if (svc != SVC_PEDESTRIAN) {
1673
if (edge->getStep() == NBEdge::EdgeBuildingStep::LANES2LANES_USER) {
1674
// preserve existing connections and only add new ones
1675
edge->declareConnectionsAsLoaded(NBEdge::EdgeBuildingStep::LANES2LANES_DONE);
1676
edge->getFromNode()->recheckVClassConnections(edge);
1677
for (NBEdge* to : edge->getToNode()->getOutgoingEdges()) {
1678
edge->getToNode()->recheckVClassConnections(to);
1679
}
1680
// patching TLS is not feasible because existing states may
1681
// change from 'G' to 'g' when bike lanes are added (i.e. right-turns)
1682
} else {
1683
edge->invalidateConnections(true);
1684
edge->getFromNode()->invalidateOutgoingConnections(true);
1685
}
1686
edge->getFromNode()->invalidateTLS(tlc, true, false);
1687
edge->getToNode()->invalidateTLS(tlc, true, false);
1688
}
1689
}
1690
}
1691
return lanesCreated;
1692
}
1693
1694
1695
void
1696
NBEdgeCont::updateAllChangeRestrictions(SVCPermissions ignoring) {
1697
for (auto item : myEdges) {
1698
item.second->updateChangeRestrictions(ignoring);
1699
}
1700
}
1701
1702
1703
void
1704
NBEdgeCont::addPrefix(const std::string& prefix) {
1705
// make a copy of node containers
1706
const auto nodeContainerCopy = myEdges;
1707
myEdges.clear();
1708
for (const auto& node : nodeContainerCopy) {
1709
node.second->setID(prefix + node.second->getID());
1710
myEdges[node.second->getID()] = node.second;
1711
}
1712
}
1713
1714
1715
int
1716
NBEdgeCont::remapIDs(bool numericaIDs, bool reservedIDs, bool keptIDs, const std::string& prefix, NBPTStopCont& sc) {
1717
bool startGiven = !OptionsCont::getOptions().isDefault("numerical-ids.edge-start");
1718
if (!numericaIDs && !reservedIDs && prefix == "" && !startGiven) {
1719
return 0;
1720
}
1721
std::vector<std::string> avoid;
1722
if (startGiven) {
1723
avoid.push_back(toString(OptionsCont::getOptions().getInt("numerical-ids.edge-start") - 1));
1724
} else {
1725
avoid = getAllNames();
1726
}
1727
std::set<std::string> reserve;
1728
if (reservedIDs) {
1729
NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("reserved-ids"), "edge:", reserve);
1730
avoid.insert(avoid.end(), reserve.begin(), reserve.end());
1731
}
1732
IDSupplier idSupplier("", avoid);
1733
std::set<NBEdge*, ComparatorIdLess> toChange;
1734
for (EdgeCont::iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1735
if (startGiven) {
1736
toChange.insert(it->second);
1737
continue;
1738
}
1739
if (numericaIDs) {
1740
try {
1741
StringUtils::toLong(it->first);
1742
} catch (NumberFormatException&) {
1743
toChange.insert(it->second);
1744
}
1745
}
1746
if (reservedIDs && reserve.count(it->first) > 0) {
1747
toChange.insert(it->second);
1748
}
1749
}
1750
std::set<std::string> keep;
1751
if (keptIDs) {
1752
NBHelpers::loadPrefixedIDsFomFile(OptionsCont::getOptions().getString("kept-ids"), "edge:", keep);
1753
for (auto it = toChange.begin(); it != toChange.end();) {
1754
if (keep.count((*it)->getID()) != 0) {
1755
toChange.erase(it++);
1756
} else {
1757
it++;
1758
}
1759
}
1760
}
1761
1762
std::map<std::string, std::vector<std::shared_ptr<NBPTStop> > > stopsOnEdge;
1763
for (const auto& item : sc.getStops()) {
1764
stopsOnEdge[item.second->getEdgeId()].push_back(item.second);
1765
}
1766
1767
const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1768
for (NBEdge* edge : toChange) {
1769
myEdges.erase(edge->getID());
1770
}
1771
for (NBEdge* edge : toChange) {
1772
const std::string origID = edge->getID();
1773
if (origNames) {
1774
edge->setOrigID(origID, false);
1775
}
1776
edge->setID(idSupplier.getNext());
1777
myEdges[edge->getID()] = edge;
1778
for (std::shared_ptr<NBPTStop> stop : stopsOnEdge[origID]) {
1779
stop->setEdgeId(prefix + edge->getID(), *this);
1780
}
1781
}
1782
if (prefix.empty()) {
1783
return (int)toChange.size();
1784
} else {
1785
int renamed = 0;
1786
// make a copy because we will modify the map
1787
auto oldEdges = myEdges;
1788
for (auto item : oldEdges) {
1789
if (!StringUtils::startsWith(item.first, prefix) && keep.count(item.first) == 0) {
1790
rename(item.second, prefix + item.first);
1791
renamed++;
1792
}
1793
}
1794
return renamed;
1795
}
1796
}
1797
1798
1799
void
1800
NBEdgeCont::checkOverlap(double threshold, double zThreshold) const {
1801
for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1802
const NBEdge* e1 = it->second;
1803
Boundary b1 = e1->getGeometry().getBoxBoundary();
1804
b1.grow(e1->getTotalWidth());
1805
PositionVector outline1 = e1->getCCWBoundaryLine(*e1->getFromNode());
1806
outline1.append(e1->getCCWBoundaryLine(*e1->getToNode()));
1807
// check is symmetric. only check once per pair
1808
for (EdgeCont::const_iterator it2 = it; it2 != myEdges.end(); it2++) {
1809
const NBEdge* e2 = it2->second;
1810
if (e1 == e2) {
1811
continue;
1812
}
1813
Boundary b2 = e2->getGeometry().getBoxBoundary();
1814
b2.grow(e2->getTotalWidth());
1815
if (b1.overlapsWith(b2)) {
1816
PositionVector outline2 = e2->getCCWBoundaryLine(*e2->getFromNode());
1817
outline2.append(e2->getCCWBoundaryLine(*e2->getToNode()));
1818
const double overlap = outline1.getOverlapWith(outline2, zThreshold);
1819
if (overlap > threshold) {
1820
WRITE_WARNINGF(TL("Edge '%' overlaps with edge '%' by %."), e1->getID(), e2->getID(), overlap);
1821
}
1822
}
1823
}
1824
}
1825
}
1826
1827
1828
void
1829
NBEdgeCont::checkGrade(double threshold) const {
1830
for (EdgeCont::const_iterator it = myEdges.begin(); it != myEdges.end(); it++) {
1831
const NBEdge* edge = it->second;
1832
for (int i = 0; i < (int)edge->getNumLanes(); i++) {
1833
double maxJump = 0;
1834
const double grade = edge->getLaneShape(i).getMaxGrade(maxJump);
1835
if (maxJump > 0.01) {
1836
WRITE_WARNINGF(TL("Edge '%' has a vertical jump of %m."), edge->getID(), maxJump);
1837
} else if (grade > threshold) {
1838
WRITE_WARNINGF(TL("Edge '%' has a grade of %%."), edge->getID(), grade * 100, "%");
1839
break;
1840
}
1841
}
1842
const std::vector<NBEdge::Connection>& connections = edge->getConnections();
1843
for (std::vector<NBEdge::Connection>::const_iterator it_con = connections.begin(); it_con != connections.end(); ++it_con) {
1844
const NBEdge::Connection& c = *it_con;
1845
double maxJump = 0;
1846
const double grade = MAX2(c.shape.getMaxGrade(maxJump), c.viaShape.getMaxGrade(maxJump));
1847
if (maxJump > 0.01) {
1848
WRITE_WARNINGF(TL("Connection '%' has a vertical jump of %m."), c.getDescription(edge), maxJump);
1849
} else if (grade > threshold) {
1850
WRITE_WARNINGF(TL("Connection '%' has a grade of %%."), c.getDescription(edge), grade * 100, "%");
1851
break;
1852
}
1853
}
1854
}
1855
}
1856
1857
1858
int
1859
NBEdgeCont::joinLanes(SVCPermissions perms) {
1860
int affectedEdges = 0;
1861
for (auto item : myEdges) {
1862
if (item.second->joinLanes(perms)) {
1863
affectedEdges++;
1864
}
1865
}
1866
return affectedEdges;
1867
}
1868
1869
1870
bool
1871
NBEdgeCont::MinLaneComparatorIdLess::operator()(const std::pair<NBEdge*, int>& a, const std::pair<NBEdge*, int>& b) const {
1872
if (a.first->getID() == b.first->getID()) {
1873
return a.second < b.second;
1874
}
1875
return a.first->getID() < b.first->getID();
1876
}
1877
1878
int
1879
NBEdgeCont::joinTramEdges(NBDistrictCont& dc, NBPTStopCont& sc, NBPTLineCont& lc, double maxDist) {
1880
// this is different from joinSimilarEdges because there don't need to be
1881
// shared nodes and tram edges may be split
1882
std::vector<NBEdge*> tramEdges;
1883
std::vector<NBEdge*> targetEdges;
1884
for (auto item : myEdges) {
1885
SVCPermissions permissions = item.second->getPermissions();
1886
if (isTram(permissions)) {
1887
if (item.second->getNumLanes() == 1) {
1888
tramEdges.push_back(item.second);
1889
} else {
1890
WRITE_WARNINGF(TL("Not joining tram edge '%' with % lanes."), item.second->getID(), item.second->getNumLanes());
1891
}
1892
} else if ((permissions & (SVC_PASSENGER | SVC_BUS)) != 0) {
1893
targetEdges.push_back(item.second);
1894
}
1895
}
1896
if (tramEdges.empty() || targetEdges.empty()) {
1897
return 0;
1898
}
1899
int numJoined = 0;
1900
NamedRTree tramTree;
1901
for (NBEdge* const edge : tramEdges) {
1902
const Boundary& bound = edge->getGeometry().getBoxBoundary();
1903
float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1904
float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1905
tramTree.Insert(min, max, edge);
1906
}
1907
// {targetEdge, laneIndex : tramEdge}
1908
std::map<std::pair<NBEdge*, int>, NBEdge*, MinLaneComparatorIdLess> matches;
1909
1910
for (NBEdge* const edge : targetEdges) {
1911
Boundary bound = edge->getGeometry().getBoxBoundary();
1912
bound.grow(maxDist + edge->getTotalWidth());
1913
float min[2] = { static_cast<float>(bound.xmin()), static_cast<float>(bound.ymin()) };
1914
float max[2] = { static_cast<float>(bound.xmax()), static_cast<float>(bound.ymax()) };
1915
std::set<const Named*> near;
1916
Named::StoringVisitor visitor(near);
1917
tramTree.Search(min, max, visitor);
1918
// the nearby set is actually just re-sorting according to the id to make the tests comparable
1919
std::set<NBEdge*, ComparatorIdLess> nearby;
1920
for (const Named* namedEdge : near) {
1921
nearby.insert(const_cast<NBEdge*>(static_cast<const NBEdge*>(namedEdge)));
1922
}
1923
for (NBEdge* const tramEdge : nearby) {
1924
// find a continous stretch of tramEdge that runs along one of the lanes of the road edge
1925
PositionVector tramShape = tramEdge->getGeometry();
1926
if (tramEdge->getToNode() == edge->getToNode()) {
1927
tramShape.extrapolate(tramShape.back().distanceTo2D(edge->getGeometry().back()), false, true);
1928
}
1929
double minEdgeDist = maxDist + 1;
1930
int minLane = -1;
1931
// find the lane where the maximum distance from the tram geometry
1932
// is minimal and within maxDist
1933
for (int i = 0; i < edge->getNumLanes(); i++) {
1934
double maxLaneDist = -1;
1935
if ((edge->getPermissions(i) & (SVC_PASSENGER | SVC_BUS)) != 0) {
1936
const PositionVector& laneShape = edge->getLaneShape(i);
1937
for (Position pos : laneShape) {
1938
const double dist = tramShape.distance2D(pos, false);
1939
#ifdef DEBUG_JOIN_TRAM
1940
//if (edge->getID() == "106838214#1") {
1941
// std::cout << " edge=" << edge->getID() << " tramEdge=" << tramEdge->getID() << " lane=" << i << " pos=" << pos << " dist=" << dist << "\n";
1942
//}
1943
#endif
1944
if (dist == GeomHelper::INVALID_OFFSET || dist > maxDist) {
1945
maxLaneDist = -1;
1946
break;
1947
}
1948
maxLaneDist = MAX2(maxLaneDist, dist);
1949
}
1950
if (maxLaneDist >= 0 && maxLaneDist < minEdgeDist) {
1951
minEdgeDist = maxLaneDist;
1952
minLane = i;
1953
}
1954
}
1955
}
1956
if (minLane >= 0) {
1957
// edge could run in the wrong direction and still fit the threshold we check the angle as well
1958
const PositionVector& laneShape = edge->getLaneShape(minLane);
1959
const double offset1 = tramShape.nearest_offset_to_point2D(laneShape.front(), false);
1960
const double offset2 = tramShape.nearest_offset_to_point2D(laneShape.back(), false);
1961
Position p1 = tramShape.positionAtOffset2D(offset1);
1962
Position p2 = tramShape.positionAtOffset2D(offset2);
1963
double tramAngle = GeomHelper::legacyDegree(p1.angleTo2D(p2), true);
1964
bool angleOK = GeomHelper::getMinAngleDiff(tramAngle, edge->getTotalAngle()) < JOIN_TRAM_MAX_ANGLE;
1965
if (angleOK && offset2 > offset1) {
1966
std::pair<NBEdge*, int> key = std::make_pair(edge, minLane);
1967
if (matches.count(key) == 0) {
1968
matches[key] = tramEdge;
1969
} else {
1970
WRITE_WARNINGF(TL("Ambiguous tram edges '%' and '%' for lane '%'."), matches[key]->getID(), tramEdge->getID(), edge->getLaneID(minLane));
1971
}
1972
#ifdef DEBUG_JOIN_TRAM
1973
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";
1974
#endif
1975
}
1976
}
1977
}
1978
}
1979
if (matches.size() == 0) {
1980
return 0;
1981
}
1982
const bool origNames = OptionsCont::getOptions().getBool("output.original-names");
1983
// find continous runs of matched edges for each tramEdge
1984
for (NBEdge* tramEdge : tramEdges) {
1985
std::vector<std::pair<double, std::pair<NBEdge*, int> > > roads;
1986
for (auto item : matches) {
1987
if (item.second == tramEdge) {
1988
NBEdge* road = item.first.first;
1989
int laneIndex = item.first.second;
1990
const PositionVector& laneShape = road->getLaneShape(laneIndex);
1991
double tramPos = tramEdge->getGeometry().nearest_offset_to_point2D(laneShape.front(), false);
1992
//std::cout << " road=" << road->getID() << " tramEdge=" << tramEdge->getID() << " tramShape=" << tramEdge->getGeometry() << " laneFront=" << laneShape.front() << " tramPos=" << tramPos << "\n";
1993
roads.push_back(std::make_pair(tramPos, item.first));
1994
}
1995
}
1996
if (roads.size() != 0) {
1997
1998
sort(roads.begin(), roads.end());
1999
#ifdef DEBUG_JOIN_TRAM
2000
std::cout << " tramEdge=" << tramEdge->getID() << " roads=";
2001
for (auto item : roads) {
2002
std::cout << item.second.first->getLaneID(item.second.second) << ",";
2003
}
2004
std::cout << " offsets=";
2005
for (auto item : roads) {
2006
std::cout << item.first << ",";
2007
}
2008
std::cout << "\n";
2009
#endif
2010
// merge tramEdge into road lanes
2011
EdgeVector replacement;
2012
double pos = 0;
2013
int tramPart = 0;
2014
std::string tramEdgeID = tramEdge->getID();
2015
NBNode* tramFrom = tramEdge->getFromNode();
2016
PositionVector tramShape = tramEdge->getGeometry();
2017
const double tramLength = tramShape.length();
2018
EdgeVector incoming = tramFrom->getIncomingEdges();
2019
bool erasedLast = false;
2020
for (const auto& item : roads) {
2021
const double gap = item.first - pos;
2022
NBEdge* road = item.second.first;
2023
int laneIndex = item.second.second;
2024
if (gap >= JOIN_TRAM_MIN_LENGTH && road->getFromNode() != tramEdge->getFromNode()) {
2025
#ifdef DEBUG_JOIN_TRAM
2026
std::cout << " splitting tramEdge=" << tramEdge->getID() << " at " << item.first << " (gap=" << gap << ")\n";
2027
#endif
2028
const std::string firstPartID = tramEdgeID + "#" + toString(tramPart++);
2029
splitAt(dc, tramEdge, gap, road->getFromNode(), firstPartID, tramEdgeID, 1, 1);
2030
tramEdge = retrieve(tramEdgeID); // second part;
2031
NBEdge* firstPart = retrieve(firstPartID);
2032
firstPart->invalidateConnections(true);
2033
incoming.clear();
2034
incoming.push_back(firstPart);
2035
replacement.push_back(firstPart);
2036
}
2037
pos = item.first + road->getGeometry().length();
2038
numJoined++;
2039
replacement.push_back(road);
2040
// merge section of tramEdge into road lane
2041
if (road->getToNode() != tramEdge->getToNode() && (tramLength - pos) >= JOIN_TRAM_MIN_LENGTH) {
2042
tramEdge->reinitNodes(road->getToNode(), tramEdge->getToNode());
2043
tramEdge->setGeometry(tramShape.getSubpart(pos, tramShape.length()));
2044
erasedLast = false;
2045
#ifdef DEBUG_JOIN_TRAM
2046
std::cout << " shorted tramEdge=" << tramEdge->getID() << " (joined with roadEdge=" << road->getID() << "\n";
2047
#endif
2048
} else {
2049
#ifdef DEBUG_JOIN_TRAM
2050
std::cout << " erased tramEdge=" << tramEdge->getID() << "\n";
2051
#endif
2052
extract(dc, tramEdge, true);
2053
erasedLast = true;
2054
}
2055
road->setPermissions(road->getPermissions(laneIndex) | SVC_TRAM, laneIndex);
2056
if (origNames) {
2057
road->setOrigID(tramEdgeID, true, laneIndex);
2058
}
2059
for (NBEdge* in : incoming) {
2060
if (isTram(in->getPermissions()) && !in->isConnectedTo(road)) {
2061
if (in->getFromNode() != road->getFromNode()) {
2062
in->reinitNodes(in->getFromNode(), road->getFromNode());
2063
} else {
2064
extract(dc, in, true);
2065
#ifdef DEBUG_JOIN_TRAM
2066
std::cout << " erased incoming tramEdge=" << in->getID() << "\n";
2067
#endif
2068
}
2069
}
2070
}
2071
incoming.clear();
2072
}
2073
NBEdge* lastRoad = roads.back().second.first;
2074
if (erasedLast) {
2075
// copy to avoid concurrent modification
2076
auto outEdges = tramEdge->getToNode()->getOutgoingEdges();
2077
for (NBEdge* out : outEdges) {
2078
if (isTram(out->getPermissions()) && !lastRoad->isConnectedTo(out)) {
2079
if (lastRoad->getToNode() != out->getToNode()) {
2080
out->reinitNodes(lastRoad->getToNode(), out->getToNode());
2081
} else {
2082
extract(dc, out, true);
2083
#ifdef DEBUG_JOIN_TRAM
2084
std::cout << " erased outgoing tramEdge=" << out->getID() << "\n";
2085
#endif
2086
2087
}
2088
}
2089
}
2090
} else {
2091
replacement.push_back(tramEdge);
2092
}
2093
// update ptstops and ptlines
2094
sc.replaceEdge(tramEdgeID, replacement);
2095
lc.replaceEdge(tramEdgeID, replacement);
2096
}
2097
}
2098
2099
return numJoined;
2100
}
2101
2102
2103
EdgeVector
2104
NBEdgeCont::getAllEdges() const {
2105
EdgeVector result;
2106
for (auto item : myEdges) {
2107
item.second->setNumericalID((int)result.size());
2108
result.push_back(item.second);
2109
}
2110
return result;
2111
}
2112
2113
RouterEdgeVector
2114
NBEdgeCont::getAllRouterEdges() const {
2115
EdgeVector all = getAllEdges();
2116
return RouterEdgeVector(all.begin(), all.end());
2117
}
2118
2119
bool
2120
NBEdgeCont::checkConsistency(const NBNodeCont& nc) {
2121
bool ok = true;
2122
for (const auto& item : myEdges) {
2123
NBEdge* e = item.second;
2124
if (nc.retrieve(e->getFromNode()->getID()) == nullptr) {
2125
WRITE_ERRORF(TL("Edge's '%' from-node '%' is not known."), e->getID(), e->getFromNode()->getID());
2126
ok = false;
2127
}
2128
if (nc.retrieve(e->getToNode()->getID()) == nullptr) {
2129
WRITE_ERRORF(TL("Edge's '%' to-node '%' is not known."), e->getID(), e->getToNode()->getID());
2130
ok = false;
2131
}
2132
2133
}
2134
return ok;
2135
}
2136
2137
2138
void
2139
NBEdgeCont::fixSplitCustomLength() {
2140
for (auto item : myEdges) {
2141
NBEdge* e = item.second;
2142
if (e->hasLoadedLength() && myWasSplit.count(e) != 0) {
2143
// subtract half the length of the longest incoming / outgoing connection
2144
double maxLengthOut = 0;
2145
for (const NBEdge::Connection& c : e->getConnections()) {
2146
maxLengthOut = MAX2(maxLengthOut, c.length + c.viaLength);
2147
}
2148
double maxLengthIn = 0;
2149
for (const NBEdge* in : e->getIncomingEdges()) {
2150
for (const NBEdge::Connection& c : in->getConnectionsFromLane(-1, e, -1)) {
2151
maxLengthIn = MAX2(maxLengthIn, c.length + c.viaLength);
2152
}
2153
}
2154
e->setLoadedLength(MAX2(POSITION_EPS, e->getLoadedLength() - (maxLengthIn + maxLengthOut) / 2));
2155
}
2156
}
2157
}
2158
2159
void
2160
NBEdgeCont::computeAngles() {
2161
for (auto item : myEdges) {
2162
item.second->computeAngle();
2163
}
2164
}
2165
2166
2167
std::set<std::string>
2168
NBEdgeCont::getUsedTypes() const {
2169
std::set<std::string> result;
2170
for (auto item : myEdges) {
2171
if (item.second->getTypeID() != "") {
2172
result.insert(item.second->getTypeID());
2173
}
2174
}
2175
return result;
2176
}
2177
2178
2179
int
2180
NBEdgeCont::removeEdgesBySpeed(NBDistrictCont& dc) {
2181
EdgeSet toRemove;
2182
for (auto item : myEdges) {
2183
NBEdge* edge = item.second;
2184
// remove edges which allow a speed below a set one (set using "keep-edges.min-speed")
2185
if (edge->getSpeed() < myEdgesMinSpeed) {
2186
toRemove.insert(edge);
2187
}
2188
}
2189
int numRemoved = 0;
2190
for (NBEdge* edge : toRemove) {
2191
// explicit whitelist overrides removal
2192
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2193
extract(dc, edge);
2194
numRemoved++;
2195
}
2196
}
2197
return numRemoved;
2198
}
2199
2200
2201
int
2202
NBEdgeCont::removeEdgesByPermissions(NBDistrictCont& dc) {
2203
EdgeSet toRemove;
2204
for (auto item : myEdges) {
2205
NBEdge* edge = item.second;
2206
// check whether the edge shall be removed because it does not allow any of the wished classes
2207
if (myVehicleClasses2Keep != 0 && (myVehicleClasses2Keep & edge->getPermissions()) == 0) {
2208
toRemove.insert(edge);
2209
}
2210
// check whether the edge shall be removed due to allowing unwished classes only
2211
if (myVehicleClasses2Remove != 0 && (myVehicleClasses2Remove | edge->getPermissions()) == myVehicleClasses2Remove) {
2212
toRemove.insert(edge);
2213
}
2214
}
2215
int numRemoved = 0;
2216
for (NBEdge* edge : toRemove) {
2217
// explicit whitelist overrides removal
2218
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2219
extract(dc, edge);
2220
numRemoved++;
2221
}
2222
}
2223
return numRemoved;
2224
}
2225
2226
2227
int
2228
NBEdgeCont::removeLanesByWidth(NBDistrictCont& dc, const double minWidth) {
2229
EdgeSet toRemove;
2230
for (auto item : myEdges) {
2231
NBEdge* const edge = item.second;
2232
std::vector<int> indices;
2233
int idx = 0;
2234
for (const auto& lane : edge->getLanes()) {
2235
if (lane.width != NBEdge::UNSPECIFIED_WIDTH && lane.width < minWidth) {
2236
indices.push_back(idx);
2237
}
2238
idx++;
2239
}
2240
if ((int)indices.size() == edge->getNumLanes()) {
2241
toRemove.insert(edge);
2242
} else {
2243
std::reverse(indices.begin(), indices.end());
2244
for (const int i : indices) {
2245
edge->deleteLane(i, false, true);
2246
}
2247
}
2248
}
2249
int numRemoved = 0;
2250
for (NBEdge* edge : toRemove) {
2251
// explicit whitelist overrides removal
2252
if (myEdges2Keep.size() == 0 || myEdges2Keep.count(edge->getID()) == 0) {
2253
extract(dc, edge);
2254
numRemoved++;
2255
}
2256
}
2257
return numRemoved;
2258
}
2259
2260
2261
int
2262
NBEdgeCont::attachRemoved(NBNodeCont& nc, NBDistrictCont& dc, const double maxDist) {
2263
int numSplit = 0;
2264
std::map<std::string, std::vector<std::string> > node2edge;
2265
for (auto item : myEdges) {
2266
if (item.second->hasParameter(SUMO_PARAM_REMOVED_NODES)) {
2267
for (std::string& nodeID : StringTokenizer(item.second->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2268
node2edge[nodeID].push_back(item.first);
2269
}
2270
}
2271
}
2272
for (auto item : nc) {
2273
NBNode* n = item.second;
2274
auto itRN = node2edge.find(n->getID());
2275
if (itRN != node2edge.end()) {
2276
bool rebuildConnections = false;
2277
// make a copy because we modify the original
2278
std::vector<std::string> edgeIDs = itRN->second;
2279
for (const std::string& eID : edgeIDs) {
2280
NBEdge* edge = retrieve(eID);
2281
assert(edge != nullptr);
2282
const double dist = edge->getGeometry().distance2D(n->getPosition(), true);
2283
if (dist != GeomHelper::INVALID_OFFSET && dist <= maxDist) {
2284
std::string idAfter = edge->getID();
2285
int index = 1;
2286
size_t spos = idAfter.find("#");
2287
if (spos != std::string::npos && spos > 1) {
2288
idAfter = idAfter.substr(0, spos);
2289
}
2290
while (retrieve(idAfter + "#" + toString(index), true) != nullptr) {
2291
index++;
2292
}
2293
idAfter += "#" + toString(index);
2294
const bool ok = splitAt(dc, edge, n, edge->getID(), idAfter, edge->getNumLanes(), edge->getNumLanes());
2295
if (ok) {
2296
rebuildConnections = true;
2297
numSplit++;
2298
NBEdge* secondEdge = retrieve(eID); // original was extracted on splitting
2299
for (std::string& nodeID : StringTokenizer(secondEdge->getParameter(SUMO_PARAM_REMOVED_NODES)).getVector()) {
2300
node2edge[nodeID].push_back(idAfter);
2301
}
2302
}
2303
}
2304
}
2305
if (rebuildConnections) {
2306
for (NBEdge* e : n->getIncomingEdges()) {
2307
e->invalidateConnections(true);
2308
}
2309
}
2310
}
2311
}
2312
return numSplit;
2313
}
2314
2315
/****************************************************************************/
2316
2317