Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netbuild/NBEdge.cpp
193677 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 NBEdge.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Jakob Erdmann
17
/// @author Sascha Krieg
18
/// @author Michael Behrisch
19
/// @author Laura Bieker
20
/// @author Leonhard Luecken
21
/// @date Tue, 20 Nov 2001
22
///
23
// Methods for the representation of a single edge
24
/****************************************************************************/
25
#include <config.h>
26
27
#include <vector>
28
#include <string>
29
#include <algorithm>
30
#include <cmath>
31
#include <iomanip>
32
#include <utils/common/MsgHandler.h>
33
#include <utils/common/StringTokenizer.h>
34
#include <utils/common/StringUtils.h>
35
#include <utils/common/ToString.h>
36
#include <utils/common/UtilExceptions.h>
37
#include <utils/common/StdDefs.h>
38
#include <utils/geom/GeomHelper.h>
39
#include <utils/options/OptionsCont.h>
40
#include "NBEdgeCont.h"
41
#include "NBNode.h"
42
#include "NBNodeCont.h"
43
#include "NBContHelper.h"
44
#include "NBHelpers.h"
45
#include "NBTrafficLightDefinition.h"
46
#include "NBOwnTLDef.h"
47
#include "NBTypeCont.h"
48
#include "NBEdge.h"
49
50
//#define ADDITIONAL_WARNINGS
51
//#define DEBUG_CONNECTION_GUESSING
52
//#define DEBUG_CONNECTION_CHECKING
53
//#define DEBUG_ANGLES
54
//#define DEBUG_NODE_BORDER
55
//#define DEBUG_REPLACECONNECTION
56
//#define DEBUG_JUNCTIONPRIO
57
//#define DEBUG_TURNSIGNS
58
//#define DEBUG_CUT_LANES
59
#define DEBUGID ""
60
#define DEBUGCOND (getID() == DEBUGID)
61
//#define DEBUGCOND (StringUtils::startsWith(getID(), DEBUGID))
62
//#define DEBUGCOND (getID() == "22762377#1" || getID() == "146511467")
63
#define DEBUGCOND2(obj) ((obj != 0 && (obj)->getID() == DEBUGID))
64
//#define DEBUGCOND (true)
65
66
// ===========================================================================
67
// static members
68
// ===========================================================================
69
const double NBEdge::UNSPECIFIED_WIDTH = -1;
70
const double NBEdge::UNSPECIFIED_OFFSET = 0;
71
const double NBEdge::UNSPECIFIED_SPEED = -1;
72
const double NBEdge::UNSPECIFIED_FRICTION = 1.;
73
const double NBEdge::UNSPECIFIED_CONTPOS = -1;
74
const double NBEdge::UNSPECIFIED_VISIBILITY_DISTANCE = -1;
75
76
const double NBEdge::UNSPECIFIED_SIGNAL_OFFSET = -1;
77
const double NBEdge::UNSPECIFIED_LOADED_LENGTH = -1;
78
const double NBEdge::ANGLE_LOOKAHEAD = 10.0;
79
const int NBEdge::UNSPECIFIED_INTERNAL_LANE_INDEX = -1;
80
const bool NBEdge::UNSPECIFIED_CONNECTION_UNCONTROLLED = false;
81
82
double NBEdge::myDefaultConnectionLength = NBEdge::UNSPECIFIED_LOADED_LENGTH;
83
84
NBEdge NBEdge::DummyEdge;
85
86
ConstRouterEdgePairVector NBEdge::Connection::myViaSuccessors = ConstRouterEdgePairVector({ std::pair<NBRouterEdge*, NBRouterEdge*>(nullptr, nullptr) });
87
88
// ===========================================================================
89
// method definitions
90
// ===========================================================================
91
std::string
92
NBEdge::Connection::getInternalLaneID() const {
93
return id + "_" + toString(internalLaneIndex);
94
}
95
96
97
std::string
98
NBEdge::Connection::getInternalViaLaneID() const {
99
return viaID + "_" + toString(internalViaLaneIndex);
100
}
101
102
103
std::string
104
NBEdge::Connection::getDescription(const NBEdge* parent) const {
105
return (Named::getIDSecure(parent) + "_" + toString(fromLane) + "->" + Named::getIDSecure(toEdge) + "_" + toString(toLane)
106
+ (permissions == SVC_UNSPECIFIED ? "" : " (" + getVehicleClassNames(permissions) + ")"));
107
}
108
109
110
NBEdge::Connection::Connection(int fromLane_, NBEdge* toEdge_, int toLane_, const bool mayDefinitelyPass_) :
111
fromLane(fromLane_),
112
toEdge(toEdge_),
113
toLane(toLane_),
114
mayDefinitelyPass(mayDefinitelyPass_),
115
customLength(myDefaultConnectionLength),
116
id(toEdge_ == nullptr ? "" : toEdge->getFromNode()->getID()) {
117
}
118
119
120
NBEdge::Lane::Lane(NBEdge* e, const std::string& origID_) :
121
speed(e->getSpeed()),
122
friction(e->getFriction()),
123
permissions(SVCAll),
124
preferred(0),
125
changeLeft(SVCAll),
126
changeRight(SVCAll),
127
endOffset(e->getEndOffset()),
128
laneStopOffset(e->getEdgeStopOffset()),
129
width(e->getLaneWidth()),
130
accelRamp(false),
131
connectionsDone(false) {
132
if (origID_ != "") {
133
setParameter(SUMO_PARAM_ORIGID, origID_);
134
}
135
}
136
137
138
/* -------------------------------------------------------------------------
139
* NBEdge::ToEdgeConnectionsAdder-methods
140
* ----------------------------------------------------------------------- */
141
void
142
NBEdge::ToEdgeConnectionsAdder::execute(const int lane, const int virtEdge) {
143
// check
144
assert((int)myTransitions.size() > virtEdge);
145
// get the approached edge
146
NBEdge* succEdge = myTransitions[virtEdge];
147
std::vector<int> lanes;
148
149
// check whether the currently regarded, approached edge has already
150
// a connection starting at the edge which is currently being build
151
std::map<NBEdge*, std::vector<int> >::iterator i = myConnections.find(succEdge);
152
if (i != myConnections.end()) {
153
// if there were already lanes assigned, get them
154
lanes = (*i).second;
155
}
156
157
// check whether the current lane was already used to connect the currently
158
// regarded approached edge
159
std::vector<int>::iterator j = std::find(lanes.begin(), lanes.end(), lane);
160
if (j == lanes.end()) {
161
// if not, add it to the list
162
lanes.push_back(lane);
163
}
164
// set information about connecting lanes
165
myConnections[succEdge] = lanes;
166
}
167
168
169
170
/* -------------------------------------------------------------------------
171
* NBEdge::MainDirections-methods
172
* ----------------------------------------------------------------------- */
173
NBEdge::MainDirections::MainDirections(const EdgeVector& outgoing, NBEdge* parent, NBNode* to, const std::vector<int>& availableLanes) : myStraightest(-1) {
174
NBContHelper::edge_similar_direction_sorter sorter(parent);
175
const NBEdge* straight = nullptr;
176
for (const NBEdge* const out : outgoing) {
177
const SVCPermissions outPerms = out->getPermissions();
178
for (const int l : availableLanes) {
179
if ((parent->myLanes[l].permissions & outPerms) != 0) {
180
if (straight == nullptr || sorter(out, straight)) {
181
straight = out;
182
}
183
break;
184
}
185
}
186
}
187
if (straight == nullptr) {
188
return;
189
}
190
myStraightest = (int)std::distance(outgoing.begin(), std::find(outgoing.begin(), outgoing.end(), straight));
191
192
// check whether the right turn has a higher priority
193
assert(outgoing.size() > 0);
194
const LinkDirection straightestDir = to->getDirection(parent, straight);
195
#ifdef DEBUG_CONNECTION_GUESSING
196
if (DEBUGCOND2(parent)) {
197
std::cout << " MainDirections edge=" << parent->getID() << " straightest=" << straight->getID() << " dir=" << toString(straightestDir) << "\n";
198
}
199
#endif
200
if (NBNode::isTrafficLight(to->getType()) &&
201
(straightestDir == LinkDirection::STRAIGHT || straightestDir == LinkDirection::PARTLEFT || straightestDir == LinkDirection::PARTRIGHT)) {
202
myDirs.push_back(MainDirections::Direction::FORWARD);
203
return;
204
}
205
if (outgoing[0]->getJunctionPriority(to) == 1) {
206
myDirs.push_back(MainDirections::Direction::RIGHTMOST);
207
}
208
// check whether the left turn has a higher priority
209
if (outgoing.back()->getJunctionPriority(to) == 1) {
210
// ok, the left turn belongs to the higher priorised edges on the junction
211
// let's check, whether it has also a higher priority (lane number/speed)
212
// than the current
213
if (outgoing.back()->getPriority() > straight->getPriority() ||
214
outgoing.back()->getNumLanes() > straight->getNumLanes()) {
215
myDirs.push_back(MainDirections::Direction::LEFTMOST);
216
}
217
}
218
// check whether the forward direction has a higher priority
219
// check whether it has a higher priority and is going straight
220
if (straight->getJunctionPriority(to) == 1 && to->getDirection(parent, straight) == LinkDirection::STRAIGHT) {
221
myDirs.push_back(MainDirections::Direction::FORWARD);
222
}
223
}
224
225
226
NBEdge::MainDirections::~MainDirections() {}
227
228
229
bool
230
NBEdge::MainDirections::empty() const {
231
return myDirs.empty();
232
}
233
234
235
bool
236
NBEdge::MainDirections::includes(Direction d) const {
237
return std::find(myDirs.begin(), myDirs.end(), d) != myDirs.end();
238
}
239
240
241
/* -------------------------------------------------------------------------
242
* NBEdge::connections_relative_edgelane_sorter-methods
243
* ----------------------------------------------------------------------- */
244
int
245
NBEdge::connections_relative_edgelane_sorter::operator()(const Connection& c1, const Connection& c2) const {
246
if (c1.toEdge != c2.toEdge) {
247
return NBContHelper::relative_outgoing_edge_sorter(myEdge)(c1.toEdge, c2.toEdge);
248
}
249
return c1.toLane < c2.toLane;
250
}
251
252
253
/* -------------------------------------------------------------------------
254
* NBEdge-methods
255
* ----------------------------------------------------------------------- */
256
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
257
std::string type, double speed, double friction, int nolanes,
258
int priority, double laneWidth, double endOffset,
259
LaneSpreadFunction spread, const std::string& streetName) :
260
Named(StringUtils::convertUmlaute(id)),
261
myStep(EdgeBuildingStep::INIT),
262
myType(StringUtils::convertUmlaute(type)),
263
myFrom(from), myTo(to),
264
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
265
myPriority(priority), mySpeed(speed), myFriction(friction),
266
myDistance(0),
267
myTurnDestination(nullptr),
268
myPossibleTurnDestination(nullptr),
269
myFromJunctionPriority(-1), myToJunctionPriority(-1),
270
myLaneSpreadFunction(spread), myEndOffset(endOffset),
271
myLaneWidth(laneWidth),
272
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
273
myAmInTLS(false), myAmMacroscopicConnector(false),
274
myStreetName(streetName),
275
mySignalPosition(Position::INVALID),
276
mySignalNode(nullptr),
277
myIsOffRamp(false),
278
myIsBidi(false),
279
myIndex(-1) {
280
init(nolanes, false, "");
281
}
282
283
284
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to,
285
std::string type, double speed, double friction, int nolanes,
286
int priority, double laneWidth, double endOffset,
287
PositionVector geom,
288
LaneSpreadFunction spread,
289
const std::string& streetName,
290
const std::string& origID,
291
bool tryIgnoreNodePositions) :
292
Named(StringUtils::convertUmlaute(id)),
293
myStep(EdgeBuildingStep::INIT),
294
myType(StringUtils::convertUmlaute(type)),
295
myFrom(from), myTo(to),
296
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
297
myPriority(priority), mySpeed(speed), myFriction(friction),
298
myDistance(0),
299
myTurnDestination(nullptr),
300
myPossibleTurnDestination(nullptr),
301
myFromJunctionPriority(-1), myToJunctionPriority(-1),
302
myGeom(geom), myLaneSpreadFunction(spread), myEndOffset(endOffset),
303
myLaneWidth(laneWidth),
304
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
305
myAmInTLS(false), myAmMacroscopicConnector(false),
306
myStreetName(streetName),
307
mySignalPosition(Position::INVALID),
308
mySignalNode(nullptr),
309
myIsOffRamp(false),
310
myIsBidi(false),
311
myIndex(-1) {
312
init(nolanes, tryIgnoreNodePositions, origID);
313
}
314
315
316
NBEdge::NBEdge(const std::string& id, NBNode* from, NBNode* to, const NBEdge* tpl, const PositionVector& geom, int numLanes) :
317
Named(StringUtils::convertUmlaute(id)),
318
myStep(EdgeBuildingStep::INIT),
319
myType(tpl->getTypeID()),
320
myFrom(from), myTo(to),
321
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
322
myPriority(tpl->getPriority()), mySpeed(tpl->getSpeed()),
323
myFriction(tpl->getFriction()),
324
myDistance(0),
325
myTurnDestination(nullptr),
326
myPossibleTurnDestination(nullptr),
327
myFromJunctionPriority(-1), myToJunctionPriority(-1),
328
myGeom(geom),
329
myLaneSpreadFunction(tpl->getLaneSpreadFunction()),
330
myEndOffset(tpl->getEndOffset()),
331
myEdgeStopOffset(tpl->getEdgeStopOffset()),
332
myLaneWidth(tpl->getLaneWidth()),
333
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
334
myAmInTLS(false),
335
myAmMacroscopicConnector(false),
336
myStreetName(tpl->getStreetName()),
337
mySignalPosition(to == tpl->myTo ? tpl->mySignalPosition : Position::INVALID),
338
mySignalNode(to == tpl->myTo ? tpl->mySignalNode : nullptr),
339
myIsOffRamp(false),
340
myIsBidi(tpl->myIsBidi),
341
myIndex(-1) {
342
init(numLanes > 0 ? numLanes : tpl->getNumLanes(), myGeom.size() > 0, "");
343
for (int i = 0; i < getNumLanes(); i++) {
344
const int tplIndex = MIN2(i, tpl->getNumLanes() - 1);
345
setSpeed(i, tpl->getLaneSpeed(tplIndex));
346
setFriction(i, tpl->getLaneFriction(tplIndex));
347
setPermissions(tpl->getPermissions(tplIndex), i);
348
setLaneWidth(i, tpl->myLanes[tplIndex].width);
349
setLaneType(i, tpl->myLanes[tplIndex].type);
350
myLanes[i].updateParameters(tpl->myLanes[tplIndex].getParametersMap());
351
if (to == tpl->myTo) {
352
setEndOffset(i, tpl->myLanes[tplIndex].endOffset);
353
setEdgeStopOffset(i, tpl->myLanes[tplIndex].laneStopOffset);
354
}
355
}
356
if (tpl->myLoadedLength > 0 && to == tpl->getFromNode() && from == tpl->getToNode() && geom == tpl->getGeometry().reverse()) {
357
myLoadedLength = tpl->myLoadedLength;
358
}
359
updateParameters(tpl->getParametersMap());
360
}
361
362
363
NBEdge::NBEdge() :
364
Named("DUMMY"),
365
myStep(EdgeBuildingStep::INIT),
366
myFrom(nullptr), myTo(nullptr),
367
myStartAngle(0), myEndAngle(0), myTotalAngle(0),
368
myPriority(0), mySpeed(0), myFriction(UNSPECIFIED_FRICTION),
369
myDistance(0),
370
myTurnDestination(nullptr),
371
myPossibleTurnDestination(nullptr),
372
myFromJunctionPriority(-1), myToJunctionPriority(-1),
373
myLaneSpreadFunction(LaneSpreadFunction::RIGHT),
374
myEndOffset(0),
375
myEdgeStopOffset(StopOffset()),
376
myLaneWidth(0),
377
myLoadedLength(UNSPECIFIED_LOADED_LENGTH),
378
myAmInTLS(false),
379
myAmMacroscopicConnector(false),
380
mySignalPosition(Position::INVALID),
381
mySignalNode(nullptr) {
382
}
383
384
385
void
386
NBEdge::reinit(NBNode* from, NBNode* to, const std::string& type,
387
double speed, double friction, int nolanes, int priority,
388
PositionVector geom, double laneWidth, double endOffset,
389
const std::string& streetName,
390
LaneSpreadFunction spread,
391
bool tryIgnoreNodePositions) {
392
if (myFrom != from) {
393
myFrom->removeEdge(this, false);
394
}
395
if (myTo != to) {
396
myTo->removeEdge(this, false);
397
}
398
myType = StringUtils::convertUmlaute(type);
399
myFrom = from;
400
myTo = to;
401
myPriority = priority;
402
//?myTurnDestination(0),
403
//?myFromJunctionPriority(-1), myToJunctionPriority(-1),
404
myGeom = geom;
405
myLaneSpreadFunction = spread;
406
myLoadedLength = UNSPECIFIED_LOADED_LENGTH;
407
myStreetName = streetName;
408
//?, myAmTurningWithAngle(0), myAmTurningOf(0),
409
//?myAmInTLS(false), myAmMacroscopicConnector(false)
410
411
// preserve lane-specific settings (geometry must be recomputed)
412
// if new lanes are added they copy the values from the leftmost lane (if specified)
413
const std::vector<Lane> oldLanes = myLanes;
414
init(nolanes, tryIgnoreNodePositions, oldLanes.empty() ? "" : oldLanes[0].getParameter(SUMO_PARAM_ORIGID));
415
for (int i = 0; i < (int)nolanes; ++i) {
416
PositionVector newShape = myLanes[i].shape;
417
myLanes[i] = oldLanes[MIN2(i, (int)oldLanes.size() - 1)];
418
myLanes[i].shape = newShape;
419
}
420
// however, if the new edge defaults are explicityly given, they override the old settings
421
if (endOffset != UNSPECIFIED_OFFSET) {
422
setEndOffset(-1, endOffset);
423
}
424
if (laneWidth != UNSPECIFIED_WIDTH) {
425
setLaneWidth(-1, laneWidth);
426
}
427
if (speed != UNSPECIFIED_SPEED) {
428
setSpeed(-1, speed);
429
}
430
if (friction != UNSPECIFIED_FRICTION) {
431
setFriction(-1, friction);
432
}
433
}
434
435
436
void
437
NBEdge::reinitNodes(NBNode* from, NBNode* to) {
438
// connections may still be valid
439
if (from == nullptr || to == nullptr) {
440
throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
441
}
442
if (myFrom != from) {
443
myFrom->removeEdge(this, false);
444
}
445
if (myTo != to) {
446
myTo->removeEdge(this, false);
447
}
448
// remove first from both nodes and then add to the new nodes
449
// (otherwise reversing does not work)
450
if (myFrom != from) {
451
myFrom = from;
452
myFrom->addOutgoingEdge(this);
453
}
454
if (myTo != to) {
455
myTo = to;
456
myTo->addIncomingEdge(this);
457
}
458
computeAngle();
459
}
460
461
462
void
463
NBEdge::init(int noLanes, bool tryIgnoreNodePositions, const std::string& origID) {
464
if (noLanes == 0) {
465
throw ProcessError(TLF("Edge '%' needs at least one lane.", myID));
466
}
467
if (myFrom == nullptr || myTo == nullptr) {
468
throw ProcessError(TLF("At least one of edge's '%' nodes is not known.", myID));
469
}
470
if (!SUMOXMLDefinitions::isValidNetID(myID)) {
471
throw ProcessError(TLF("Invalid edge id '%'.", myID));
472
}
473
// revisit geometry
474
// should have at least two points at the end...
475
// and in dome cases, the node positions must be added
476
// attempt symmetrical removal for forward and backward direction
477
// (very important for bidiRail)
478
if (myFrom->getID() < myTo->getID()) {
479
PositionVector reverse = myGeom.reverse();
480
reverse.removeDoublePoints(POSITION_EPS, true);
481
myGeom = reverse.reverse();
482
} else {
483
myGeom.removeDoublePoints(POSITION_EPS, true);
484
}
485
486
if (!tryIgnoreNodePositions || myGeom.size() < 2) {
487
if (myGeom.size() == 0) {
488
myGeom.push_back(myFrom->getPosition());
489
myGeom.push_back(myTo->getPosition());
490
} else {
491
myGeom.push_back_noDoublePos(myTo->getPosition());
492
myGeom.push_front_noDoublePos(myFrom->getPosition());
493
}
494
}
495
if (myGeom.size() < 2) {
496
myGeom.clear();
497
myGeom.push_back(myFrom->getPosition());
498
myGeom.push_back(myTo->getPosition());
499
}
500
if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
501
WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
502
int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
503
myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
504
}
505
// avoid degeneration of near-0-length geometrie when shifting later
506
myGeom.ensureMinLength(gPrecision);
507
//
508
myFrom->addOutgoingEdge(this);
509
myTo->addIncomingEdge(this);
510
// prepare container
511
assert(myGeom.size() >= 2);
512
myLength = myGeom.length();
513
if ((int)myLanes.size() > noLanes) {
514
// remove connections starting at the removed lanes
515
for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
516
removeFromConnections(nullptr, lane, -1);
517
}
518
// remove connections targeting the removed lanes
519
const EdgeVector& incoming = myFrom->getIncomingEdges();
520
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
521
for (int lane = noLanes; lane < (int)myLanes.size(); ++lane) {
522
(*i)->removeFromConnections(this, -1, lane);
523
}
524
}
525
}
526
myLanes.clear();
527
for (int i = 0; i < noLanes; i++) {
528
myLanes.push_back(Lane(this, origID));
529
}
530
computeLaneShapes();
531
computeAngle();
532
533
#ifdef DEBUG_CONNECTION_GUESSING
534
if (DEBUGCOND) {
535
std::cout << "init edge=" << getID() << "\n";
536
for (Connection& c : myConnections) {
537
std::cout << " conn " << c.getDescription(this) << "\n";
538
}
539
for (Connection& c : myConnectionsToDelete) {
540
std::cout << " connToDelete " << c.getDescription(this) << "\n";
541
}
542
}
543
#endif
544
}
545
546
547
NBEdge::~NBEdge() {}
548
549
550
// ----------- Applying offset
551
void
552
NBEdge::reshiftPosition(double xoff, double yoff) {
553
myGeom.add(xoff, yoff, 0);
554
for (Lane& lane : myLanes) {
555
lane.customShape.add(xoff, yoff, 0);
556
}
557
computeLaneShapes(); // old shapes are dubious if computed with large coordinates
558
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
559
(*i).customShape.add(xoff, yoff, 0);
560
}
561
if (mySignalPosition != Position::INVALID) {
562
mySignalPosition.add(xoff, yoff);
563
}
564
myFromBorder.add(xoff, yoff, 0);
565
myToBorder.add(xoff, yoff, 0);
566
computeEdgeShape();
567
computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
568
}
569
570
571
void
572
NBEdge::roundGeometry() {
573
myGeom.round(gPrecision);
574
for (Lane& lane : myLanes) {
575
lane.customShape.round(gPrecision);
576
}
577
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
578
(*i).customShape.round(gPrecision);
579
}
580
}
581
582
583
void
584
NBEdge::roundSpeed() {
585
mySpeed = roundDecimalToEven(mySpeed, gPrecision);
586
// lane speeds are not used for computation but are compared to mySpeed in hasLaneSpecificSpeed
587
for (Lane& l : myLanes) {
588
l.speed = roundDecimalToEven(l.speed, gPrecision);
589
}
590
}
591
592
void
593
NBEdge::mirrorX() {
594
myGeom.mirrorX();
595
for (int i = 0; i < (int)myLanes.size(); i++) {
596
myLanes[i].shape.mirrorX();
597
myLanes[i].customShape.mirrorX();
598
}
599
for (Connection& c : myConnections) {
600
c.shape.mirrorX();
601
c.viaShape.mirrorX();
602
c.customShape.mirrorX();
603
}
604
if (mySignalPosition != Position::INVALID) {
605
mySignalPosition.sety(-mySignalPosition.y());
606
}
607
computeAngle(); // update angles because they are numerically sensitive (especially where based on centroids)
608
}
609
610
611
// ----------- Edge geometry access and computation
612
const PositionVector
613
NBEdge::getInnerGeometry() const {
614
return myGeom.getSubpartByIndex(1, (int)myGeom.size() - 2);
615
}
616
617
618
bool
619
NBEdge::hasDefaultGeometry() const {
620
return myGeom.size() == 2 && hasDefaultGeometryEndpoints();
621
}
622
623
624
bool
625
NBEdge::hasDefaultGeometryEndpoints() const {
626
return myGeom.front().almostSame(myFrom->getPosition(), 0.01) &&
627
myGeom.back().almostSame(myTo->getPosition(), 0.01);
628
}
629
630
631
bool
632
NBEdge::hasDefaultGeometryEndpointAtNode(const NBNode* node) const {
633
// do not extend past the node position
634
if (node == myFrom) {
635
return myGeom.front() == node->getPosition();
636
} else {
637
assert(node == myTo);
638
return myGeom.back() == node->getPosition();
639
}
640
}
641
642
Position
643
NBEdge::getEndpointAtNode(const NBNode* node) const {
644
return node == myFrom ? myGeom.front() : myGeom.back();
645
}
646
647
void
648
NBEdge::resetEndpointAtNode(const NBNode* node) {
649
assert(myGeom.size() >= 2);
650
if (node == myFrom) {
651
myGeom[0] = myFrom->getPosition();
652
} else if (node == myTo) {
653
myGeom[-1] = myTo->getPosition();
654
} else {
655
assert(false);
656
}
657
}
658
659
void
660
NBEdge::setGeometry(const PositionVector& s, bool inner) {
661
Position begin = myGeom.front(); // may differ from node position
662
Position end = myGeom.back(); // may differ from node position
663
myGeom = s;
664
if (inner) {
665
myGeom.insert(myGeom.begin(), begin);
666
myGeom.push_back(end);
667
}
668
// ensure non-zero length (see ::init)
669
if (myGeom.size() == 2 && myGeom[0] == myGeom[1]) {
670
WRITE_WARNINGF(TL("Edge's '%' from- and to-node are at the same position."), myID);
671
int patchIndex = myFrom->getID() < myTo->getID() ? 1 : 0;
672
myGeom[patchIndex].add(Position(POSITION_EPS, POSITION_EPS));
673
}
674
computeLaneShapes();
675
computeAngle();
676
myLength = myGeom.length();
677
}
678
679
680
void
681
NBEdge::extendGeometryAtNode(const NBNode* node, double maxExtent) {
682
//std::cout << "extendGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " extent=" << maxExtent << " geom=" << myGeom;
683
if (node == myFrom) {
684
myGeom.extrapolate(maxExtent, true);
685
double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
686
//std::cout << " geom2=" << myGeom << " offset=" << offset;
687
if (offset != GeomHelper::INVALID_OFFSET) {
688
myGeom = myGeom.getSubpart2D(MIN2(offset, myGeom.length2D() - 2 * POSITION_EPS), myGeom.length2D());
689
}
690
} else {
691
assert(node == myTo);
692
myGeom.extrapolate(maxExtent, false, true);
693
double offset = myGeom.nearest_offset_to_point2D(node->getPosition());
694
//std::cout << " geom2=" << myGeom << " offset=" << offset;
695
if (offset != GeomHelper::INVALID_OFFSET) {
696
myGeom = myGeom.getSubpart2D(0, MAX2(offset, 2 * POSITION_EPS));
697
}
698
}
699
//std::cout << " geom3=" << myGeom << "\n";
700
}
701
702
703
void
704
NBEdge::shortenGeometryAtNode(const NBNode* node, double reduction) {
705
//std::cout << "shortenGeometryAtNode edge=" << getID() << " node=" << node->getID() << " nodePos=" << node->getPosition() << " reduction=" << reduction << " geom=" << myGeom;
706
reduction = MIN2(reduction, myGeom.length2D() - 2 * POSITION_EPS);
707
if (node == myFrom) {
708
myGeom = myGeom.getSubpart2D(reduction, myGeom.length2D());
709
} else {
710
myGeom = myGeom.getSubpart2D(0, myGeom.length2D() - reduction);
711
}
712
computeLaneShapes();
713
//std::cout << " geom2=" << myGeom << "\n";
714
}
715
716
717
void
718
NBEdge::setNodeBorder(const NBNode* node, const Position& p, const Position& p2, bool rectangularCut) {
719
PositionVector border;
720
if (rectangularCut) {
721
const double extend = 100;
722
border = myGeom.getOrthogonal(p, extend, node == myTo);
723
} else {
724
border.push_back(p);
725
border.push_back(p2);
726
}
727
if (border.size() == 2) {
728
border.extrapolate2D(getTotalWidth());
729
if (node == myFrom) {
730
myFromBorder = border;
731
} else {
732
assert(node == myTo);
733
myToBorder = border;
734
}
735
}
736
#ifdef DEBUG_NODE_BORDER
737
gDebugFlag1 = DEBUGCOND;
738
if (DEBUGCOND) std::cout << "setNodeBorder edge=" << getID() << " node=" << node->getID()
739
<< " rect=" << rectangularCut
740
<< " p=" << p << " p2=" << p2
741
<< " border=" << border
742
<< " myGeom=" << myGeom
743
<< "\n";
744
745
#endif
746
}
747
748
749
const PositionVector&
750
NBEdge::getNodeBorder(const NBNode* node) const {
751
if (node == myFrom) {
752
return myFromBorder;
753
} else {
754
assert(node == myTo);
755
return myToBorder;
756
}
757
}
758
759
760
void
761
NBEdge::resetNodeBorder(const NBNode* node) {
762
if (node == myFrom) {
763
myFromBorder.clear();
764
} else {
765
assert(node == myTo);
766
myToBorder.clear();
767
}
768
}
769
770
771
bool
772
NBEdge::isBidiRail(bool ignoreSpread) const {
773
return (isRailway(getPermissions())
774
&& (ignoreSpread || myLaneSpreadFunction == LaneSpreadFunction::CENTER)
775
&& myPossibleTurnDestination != nullptr
776
&& myPossibleTurnDestination->myPossibleTurnDestination == this
777
&& (ignoreSpread || myPossibleTurnDestination->getLaneSpreadFunction() == LaneSpreadFunction::CENTER)
778
&& isRailway(myPossibleTurnDestination->getPermissions())
779
&& myPossibleTurnDestination->getGeometry().reverse() == getGeometry());
780
}
781
782
783
bool
784
NBEdge::isBidiEdge(bool checkPotential) const {
785
return myPossibleTurnDestination != nullptr
786
&& myPossibleTurnDestination->myPossibleTurnDestination == this
787
&& (myIsBidi || myPossibleTurnDestination->myIsBidi || checkPotential)
788
&& myPossibleTurnDestination->getToNode() == getFromNode()
789
&& myPossibleTurnDestination->getLaneSpreadFunction() == myLaneSpreadFunction
790
// geometry check a) full overlap geometry
791
&& ((myLaneSpreadFunction == LaneSpreadFunction::CENTER
792
&& (myPossibleTurnDestination->getGeometry().reverse() == getGeometry()
793
|| (checkPotential && getGeometry().size() == 2 && myPossibleTurnDestination->getGeometry().size() == 2)))
794
// b) TWLT (Two-Way-Left-Turn-lane)
795
|| (myLanes.back().shape.reverse().almostSame(myPossibleTurnDestination->myLanes.back().shape, POSITION_EPS))
796
);
797
798
}
799
800
801
bool
802
NBEdge::isRailDeadEnd() const {
803
if (!isRailway(getPermissions())) {
804
return false;
805
}
806
for (NBEdge* out : myTo->getOutgoingEdges()) {
807
if (isRailway(out->getPermissions()) &&
808
out != getTurnDestination(true)) {
809
return true;
810
}
811
}
812
return true;
813
}
814
815
816
PositionVector
817
NBEdge::cutAtIntersection(const PositionVector& old) const {
818
PositionVector shape = old;
819
shape = startShapeAt(shape, myFrom, myFromBorder);
820
#ifdef DEBUG_CUT_LANES
821
if (DEBUGCOND) {
822
std::cout << getID() << " cutFrom=" << shape << "\n";
823
}
824
#endif
825
if (shape.size() < 2) {
826
// only keep the last snippet
827
const double oldLength = old.length();
828
shape = old.getSubpart(oldLength - 2 * POSITION_EPS, oldLength);
829
#ifdef DEBUG_CUT_LANES
830
if (DEBUGCOND) {
831
std::cout << getID() << " cutFromFallback=" << shape << "\n";
832
}
833
#endif
834
}
835
shape = startShapeAt(shape.reverse(), myTo, myToBorder).reverse();
836
#ifdef DEBUG_CUT_LANES
837
if (DEBUGCOND) {
838
std::cout << getID() << " cutTo=" << shape << "\n";
839
}
840
#endif
841
// sanity checks
842
if (shape.length() < POSITION_EPS) {
843
if (old.length() < 2 * POSITION_EPS) {
844
shape = old;
845
} else {
846
const double midpoint = old.length() / 2;
847
// EPS*2 because otherwhise shape has only a single point
848
shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
849
assert(shape.size() >= 2);
850
assert(shape.length() > 0);
851
#ifdef DEBUG_CUT_LANES
852
if (DEBUGCOND) {
853
std::cout << getID() << " fallBackShort=" << shape << "\n";
854
}
855
#endif
856
}
857
} else {
858
// @note If the node shapes are overlapping we may get a shape which goes in the wrong direction
859
// in this case the result shape should shortened
860
if (DEG2RAD(135) < fabs(GeomHelper::angleDiff(shape.beginEndAngle(), old.beginEndAngle()))) {
861
// eliminate intermediate points
862
PositionVector tmp;
863
tmp.push_back(shape[0]);
864
tmp.push_back(shape[-1]);
865
shape = tmp;
866
if (tmp.length() < POSITION_EPS) {
867
// fall back to original shape
868
if (old.length() < 2 * POSITION_EPS) {
869
shape = old;
870
} else {
871
const double midpoint = old.length() / 2;
872
// EPS*2 because otherwhise shape has only a single point
873
shape = old.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
874
assert(shape.size() >= 2);
875
assert(shape.length() > 0);
876
}
877
#ifdef DEBUG_CUT_LANES
878
if (DEBUGCOND) {
879
std::cout << getID() << " fallBackReversed=" << shape << "\n";
880
}
881
#endif
882
} else {
883
const double midpoint = shape.length() / 2;
884
// cut to size and reverse
885
shape = shape.getSubpart(midpoint - POSITION_EPS, midpoint + POSITION_EPS);
886
if (shape.length() < POSITION_EPS) {
887
assert(false);
888
// the shape has a sharp turn near the midpoint
889
}
890
shape = shape.reverse();
891
#ifdef DEBUG_CUT_LANES
892
if (DEBUGCOND) {
893
std::cout << getID() << " fallBackReversed2=" << shape << " mid=" << midpoint << "\n";
894
}
895
#endif
896
}
897
// make short edge flat (length <= 2 * POSITION_EPS)
898
const double z = (shape[0].z() + shape[1].z()) / 2;
899
shape[0].setz(z);
900
shape[1].setz(z);
901
}
902
}
903
return shape;
904
}
905
906
907
void
908
NBEdge::computeEdgeShape(double smoothElevationThreshold) {
909
if (smoothElevationThreshold > 0 && myGeom.hasElevation()) {
910
PositionVector cut = cutAtIntersection(myGeom);
911
// cutting and patching z-coordinate may cause steep grades which should be smoothed
912
if (!myFrom->geometryLike()) {
913
cut[0].setz(myFrom->getPosition().z());
914
const double d = cut[0].distanceTo2D(cut[1]);
915
const double dZ = fabs(cut[0].z() - cut[1].z());
916
if (dZ / smoothElevationThreshold > d) {
917
cut = cut.smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold));
918
}
919
}
920
if (!myTo->geometryLike()) {
921
cut[-1].setz(myTo->getPosition().z());
922
const double d = cut[-1].distanceTo2D(cut[-2]);
923
const double dZ = fabs(cut[-1].z() - cut[-2].z());
924
if (dZ / smoothElevationThreshold > d) {
925
cut = cut.reverse().smoothedZFront(MIN2(cut.length2D() / 2, dZ / smoothElevationThreshold)).reverse();
926
}
927
}
928
cut[0] = myGeom[0];
929
cut[-1] = myGeom[-1];
930
if (cut != myGeom) {
931
myGeom = cut;
932
computeLaneShapes();
933
}
934
}
935
for (int i = 0; i < (int)myLanes.size(); i++) {
936
myLanes[i].shape = cutAtIntersection(myLanes[i].shape);
937
}
938
// recompute edge's length as the average of lane lengths
939
double avgLength = 0;
940
for (int i = 0; i < (int)myLanes.size(); i++) {
941
avgLength += myLanes[i].shape.length();
942
}
943
myLength = avgLength / (double) myLanes.size();
944
computeAngle(); // update angles using the finalized node and lane shapes
945
}
946
947
948
PositionVector
949
NBEdge::startShapeAt(const PositionVector& laneShape, const NBNode* startNode, PositionVector nodeShape) {
950
if (nodeShape.size() == 0) {
951
nodeShape = startNode->getShape();
952
nodeShape.closePolygon();
953
}
954
PositionVector lb = laneShape;
955
lb.extrapolate2D(100.0);
956
if (nodeShape.intersects(laneShape)) {
957
// shape intersects directly
958
std::vector<double> pbv = laneShape.intersectsAtLengths2D(nodeShape);
959
assert(pbv.size() > 0);
960
// ensure that the subpart has at least two points
961
double pb = MIN2(laneShape.length2D() - POSITION_EPS - NUMERICAL_EPS, VectorHelper<double>::maxValue(pbv));
962
if (pb < 0) {
963
return laneShape;
964
}
965
PositionVector ns = laneShape.getSubpart2D(pb, laneShape.length2D());
966
//PositionVector ns = pb < (laneShape.length() - POSITION_EPS) ? laneShape.getSubpart2D(pb, laneShape.length()) : laneShape;
967
const double delta = ns[0].z() - laneShape[0].z();
968
//std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
969
if (fabs(delta) > 2 * POSITION_EPS && (!startNode->geometryLike() || pb < 1)) {
970
// make "real" intersections and small intersections flat
971
//std::cout << "a) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << ns[0].z() << " delta=" << delta << "\n";
972
ns[0].setz(startNode->getPosition().z());
973
}
974
assert(ns.size() >= 2);
975
return ns;
976
} else if (nodeShape.intersects(lb)) {
977
// extension of first segment intersects
978
std::vector<double> pbv = lb.intersectsAtLengths2D(nodeShape);
979
assert(pbv.size() > 0);
980
double pb = VectorHelper<double>::maxValue(pbv);
981
assert(pb >= 0);
982
PositionVector result = laneShape.getSubpartByIndex(1, (int)laneShape.size() - 1);
983
Position np = lb.positionAtOffset2D(pb);
984
const double delta = np.z() - laneShape[0].z();
985
//std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
986
if (fabs(delta) > 2 * POSITION_EPS && !startNode->geometryLike()) {
987
// avoid z-overshoot when extrapolating
988
//std::cout << "b) startNode=" << startNode->getID() << " z=" << startNode->getPosition().z() << " oldZ=" << laneShape[0].z() << " cutZ=" << np.z() << " delta=" << delta << "\n";
989
np.setz(startNode->getPosition().z());
990
}
991
result.push_front_noDoublePos(np);
992
return result;
993
//if (result.size() >= 2) {
994
// return result;
995
//} else {
996
// WRITE_WARNING(error + " (resulting shape is too short)");
997
// return laneShape;
998
//}
999
} else {
1000
// could not find proper intersection. Probably the edge is very short
1001
// and lies within nodeShape
1002
// @todo enable warning WRITE_WARNING(error + " (laneShape lies within nodeShape)");
1003
return laneShape;
1004
}
1005
}
1006
1007
1008
const PositionVector&
1009
NBEdge::getLaneShape(int i) const {
1010
return myLanes[i].shape;
1011
}
1012
1013
1014
void
1015
NBEdge::setLaneSpreadFunction(LaneSpreadFunction spread) {
1016
myLaneSpreadFunction = spread;
1017
}
1018
1019
1020
LaneSpreadFunction
1021
NBEdge::getLaneSpreadFunction() const {
1022
return myLaneSpreadFunction;
1023
}
1024
1025
1026
void
1027
NBEdge::addGeometryPoint(int index, const Position& p) {
1028
if (index >= 0) {
1029
myGeom.insert(myGeom.begin() + index, p);
1030
} else {
1031
myGeom.insert(myGeom.end() + index, p);
1032
}
1033
}
1034
1035
1036
void
1037
NBEdge::reduceGeometry(const double minDist) {
1038
// attempt symmetrical removal for forward and backward direction
1039
// (very important for bidiRail)
1040
if (myFrom->getID() < myTo->getID()) {
1041
PositionVector reverse = myGeom.reverse();
1042
reverse.removeDoublePoints(minDist, true, 0, 0, true);
1043
myGeom = reverse.reverse();
1044
for (Lane& lane : myLanes) {
1045
reverse = lane.customShape.reverse();
1046
reverse.removeDoublePoints(minDist, true, 0, 0, true);
1047
lane.customShape = reverse.reverse();
1048
}
1049
} else {
1050
myGeom.removeDoublePoints(minDist, true, 0, 0, true);
1051
for (Lane& lane : myLanes) {
1052
lane.customShape.removeDoublePoints(minDist, true, 0, 0, true);
1053
}
1054
}
1055
}
1056
1057
1058
void
1059
NBEdge::checkGeometry(const double maxAngle, bool fixAngle, const double minRadius, bool fix, bool silent) {
1060
if (myGeom.size() < 3) {
1061
return;
1062
}
1063
//std::cout << "checking geometry of " << getID() << " geometry = " << toString(myGeom) << "\n";
1064
std::vector<double> angles; // absolute segment angles
1065
//std::cout << " absolute angles:";
1066
for (int i = 0; i < (int)myGeom.size() - 1; ++i) {
1067
angles.push_back(myGeom.angleAt2D(i));
1068
//std::cout << " " << angles.back();
1069
}
1070
//std::cout << "\n relative angles: ";
1071
NBEdge* bidi = const_cast<NBEdge*>(getBidiEdge());
1072
for (int i = 0; i < (int)angles.size() - 1; ++i) {
1073
const double relAngle = fabs(GeomHelper::angleDiff(angles[i], angles[i + 1]));
1074
//std::cout << relAngle << " ";
1075
if (maxAngle > 0 && relAngle > maxAngle) {
1076
if (fixAngle) {
1077
WRITE_MESSAGEF(TL("Removing sharp angle of % degrees at edge '%', segment %."),
1078
toString(relAngle), getID(), i);
1079
myGeom.erase(myGeom.begin() + i + 1);
1080
if (bidi != nullptr) {
1081
bidi->myGeom = myGeom.reverse();
1082
}
1083
checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1084
return;
1085
} else if (!silent) {
1086
WRITE_WARNINGF(TL("Found angle of % degrees at edge '%', segment %."), RAD2DEG(relAngle), getID(), i);
1087
}
1088
}
1089
if (relAngle < DEG2RAD(1)) {
1090
continue;
1091
}
1092
if (i == 0 || i == (int)angles.size() - 2) {
1093
const bool start = i == 0;
1094
const double dist = (start ? myGeom[0].distanceTo2D(myGeom[1]) : myGeom[-2].distanceTo2D(myGeom[-1]));
1095
const double r = tan(0.5 * (M_PI - relAngle)) * dist;
1096
//std::cout << (start ? " start" : " end") << " length=" << dist << " radius=" << r << " ";
1097
if (minRadius > 0 && r < minRadius) {
1098
if (fix) {
1099
WRITE_MESSAGEF(TL("Removing sharp turn with radius % at the % of edge '%'."),
1100
toString(r), start ? TL("start") : TL("end"), getID());
1101
myGeom.erase(myGeom.begin() + (start ? 1 : i + 1));
1102
if (bidi != nullptr) {
1103
bidi->myGeom = myGeom.reverse();
1104
}
1105
checkGeometry(maxAngle, fixAngle, minRadius, fix, silent);
1106
return;
1107
} else if (!silent) {
1108
WRITE_WARNINGF(TL("Found sharp turn with radius % at the % of edge '%'."),
1109
toString(r), start ? TL("start") : TL("end"), getID());
1110
}
1111
}
1112
}
1113
}
1114
//std::cout << "\n";
1115
}
1116
1117
1118
// ----------- Setting and getting connections
1119
bool
1120
NBEdge::addEdge2EdgeConnection(NBEdge* dest, bool overrideRemoval, SVCPermissions permissions) {
1121
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1122
return true;
1123
}
1124
// check whether the node was merged and now a connection between
1125
// not matching edges is tried to be added
1126
// This happens f.e. within the ptv VISSIM-example "Beijing"
1127
if (dest != nullptr && myTo != dest->myFrom) {
1128
return false;
1129
}
1130
if (dest == nullptr) {
1131
invalidateConnections();
1132
myConnections.push_back(Connection(-1, dest, -1));
1133
myStep = EdgeBuildingStep::LANES2LANES_USER;
1134
} else if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(dest)) == myConnections.end()) {
1135
myConnections.push_back(Connection(-1, dest, -1));
1136
myConnections.back().permissions = permissions;
1137
}
1138
if (overrideRemoval) {
1139
// override earlier delete decision
1140
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1141
if (it->toEdge == dest) {
1142
it = myConnectionsToDelete.erase(it);
1143
} else {
1144
it++;
1145
}
1146
}
1147
}
1148
if (myStep < EdgeBuildingStep::EDGE2EDGES) {
1149
myStep = EdgeBuildingStep::EDGE2EDGES;
1150
}
1151
return true;
1152
}
1153
1154
1155
bool
1156
NBEdge::addLane2LaneConnection(int from, NBEdge* dest,
1157
int toLane, Lane2LaneInfoType type,
1158
bool mayUseSameDestination,
1159
bool mayDefinitelyPass,
1160
KeepClear keepClear,
1161
double contPos,
1162
double visibility,
1163
double speed,
1164
double friction,
1165
double length,
1166
const PositionVector& customShape,
1167
bool uncontrolled,
1168
SVCPermissions permissions,
1169
bool indirectLeft,
1170
const std::string& edgeType,
1171
SVCPermissions changeLeft,
1172
SVCPermissions changeRight,
1173
bool postProcess) {
1174
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1175
return true;
1176
}
1177
// check whether the node was merged and now a connection between
1178
// not matching edges is tried to be added
1179
// This happens f.e. within the ptv VISSIM-example "Beijing"
1180
if (myTo != dest->myFrom) {
1181
return false;
1182
}
1183
if (!addEdge2EdgeConnection(dest)) {
1184
return false;
1185
}
1186
return setConnection(from, dest, toLane, type, mayUseSameDestination, mayDefinitelyPass, keepClear, contPos, visibility, speed, friction, length,
1187
customShape, uncontrolled, permissions, indirectLeft, edgeType, changeLeft, changeRight, postProcess);
1188
}
1189
1190
1191
bool
1192
NBEdge::addLane2LaneConnections(int fromLane,
1193
NBEdge* dest, int toLane,
1194
int no, Lane2LaneInfoType type,
1195
bool invalidatePrevious,
1196
bool mayDefinitelyPass) {
1197
if (invalidatePrevious) {
1198
invalidateConnections(true);
1199
}
1200
bool ok = true;
1201
for (int i = 0; i < no && ok; i++) {
1202
ok &= addLane2LaneConnection(fromLane + i, dest, toLane + i, type, false, mayDefinitelyPass);
1203
}
1204
return ok;
1205
}
1206
1207
1208
bool
1209
NBEdge::setConnection(int lane, NBEdge* destEdge,
1210
int destLane, Lane2LaneInfoType type,
1211
bool mayUseSameDestination,
1212
bool mayDefinitelyPass,
1213
KeepClear keepClear,
1214
double contPos,
1215
double visibility,
1216
double speed,
1217
double friction,
1218
double length,
1219
const PositionVector& customShape,
1220
bool uncontrolled,
1221
SVCPermissions permissions,
1222
bool indirectLeft,
1223
const std::string& edgeType,
1224
SVCPermissions changeLeft,
1225
SVCPermissions changeRight,
1226
bool postProcess) {
1227
if (myStep == EdgeBuildingStep::INIT_REJECT_CONNECTIONS) {
1228
return false;
1229
}
1230
// some kind of a misbehaviour which may occure when the junction's outgoing
1231
// edge priorities were not properly computed, what may happen due to
1232
// an incomplete or not proper input
1233
// what happens is that under some circumstances a single lane may set to
1234
// be approached more than once by the one of our lanes.
1235
// This must not be!
1236
// we test whether it is the case and do nothing if so - the connection
1237
// will be refused
1238
//
1239
if (!mayUseSameDestination && hasConnectionTo(destEdge, destLane)) {
1240
return false;
1241
}
1242
if (find_if(myConnections.begin(), myConnections.end(), connections_finder(lane, destEdge, destLane)) != myConnections.end()) {
1243
return true;
1244
}
1245
if ((int)myLanes.size() <= lane || destEdge->getNumLanes() <= (int)destLane) {
1246
// problem might be corrigible in post-processing
1247
WRITE_WARNINGF(TL("Could not set connection from '%' to '%'."), getLaneID(lane), destEdge->getLaneID(destLane));
1248
return false;
1249
}
1250
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1251
if ((*i).toEdge == destEdge && ((*i).fromLane == -1 || (*i).toLane == -1)) {
1252
if (permissions == SVC_UNSPECIFIED) {
1253
// @note: in case we were to add multiple connections from the
1254
// same lane the second one wouldn't get the special permissions!
1255
permissions = (*i).permissions;
1256
}
1257
i = myConnections.erase(i);
1258
} else {
1259
++i;
1260
}
1261
}
1262
myConnections.push_back(Connection(lane, destEdge, destLane));
1263
if (mayDefinitelyPass) {
1264
myConnections.back().mayDefinitelyPass = true;
1265
}
1266
myConnections.back().keepClear = keepClear;
1267
myConnections.back().contPos = contPos;
1268
myConnections.back().visibility = visibility;
1269
myConnections.back().permissions = permissions;
1270
myConnections.back().indirectLeft = indirectLeft;
1271
myConnections.back().edgeType = edgeType;
1272
myConnections.back().changeLeft = changeLeft;
1273
myConnections.back().changeRight = changeRight;
1274
myConnections.back().speed = speed;
1275
myConnections.back().friction = friction;
1276
myConnections.back().customLength = length;
1277
myConnections.back().customShape = customShape;
1278
myConnections.back().uncontrolled = uncontrolled;
1279
if (type == Lane2LaneInfoType::USER) {
1280
myStep = EdgeBuildingStep::LANES2LANES_USER;
1281
} else {
1282
// check whether we have to take another look at it later
1283
if (type == Lane2LaneInfoType::COMPUTED) {
1284
// yes, the connection was set using an algorithm which requires a recheck
1285
myStep = EdgeBuildingStep::LANES2LANES_RECHECK;
1286
} else {
1287
// ok, let's only not recheck it if we did no add something that has to be rechecked
1288
if (myStep != EdgeBuildingStep::LANES2LANES_RECHECK) {
1289
myStep = EdgeBuildingStep::LANES2LANES_DONE;
1290
}
1291
}
1292
}
1293
if (postProcess) {
1294
// override earlier delete decision
1295
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end();) {
1296
if ((it->fromLane < 0 || it->fromLane == lane)
1297
&& (it->toEdge == nullptr || it->toEdge == destEdge)
1298
&& (it->toLane < 0 || it->toLane == destLane)) {
1299
it = myConnectionsToDelete.erase(it);
1300
} else {
1301
it++;
1302
}
1303
}
1304
}
1305
return true;
1306
}
1307
1308
1309
std::vector<NBEdge::Connection>
1310
NBEdge::getConnectionsFromLane(int lane, const NBEdge* to, int toLane) const {
1311
std::vector<NBEdge::Connection> ret;
1312
for (const Connection& c : myConnections) {
1313
if ((lane < 0 || c.fromLane == lane)
1314
&& (to == nullptr || to == c.toEdge)
1315
&& (toLane < 0 || toLane == c.toLane)) {
1316
ret.push_back(c);
1317
}
1318
}
1319
return ret;
1320
}
1321
1322
1323
const NBEdge::Connection&
1324
NBEdge::getConnection(int fromLane, const NBEdge* to, int toLane) const {
1325
for (const Connection& c : myConnections) {
1326
if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1327
return c;
1328
}
1329
}
1330
throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1331
+ " to " + to->getID() + "_" + toString(toLane) + " not found");
1332
}
1333
1334
1335
NBEdge::Connection&
1336
NBEdge::getConnectionRef(int fromLane, const NBEdge* to, int toLane) {
1337
for (Connection& c : myConnections) {
1338
if (c.fromLane == fromLane && c.toEdge == to && c.toLane == toLane) {
1339
return c;
1340
}
1341
}
1342
throw ProcessError("Connection from " + getID() + "_" + toString(fromLane)
1343
+ " to " + to->getID() + "_" + toString(toLane) + " not found");
1344
}
1345
1346
1347
bool
1348
NBEdge::hasConnectionTo(const NBEdge* destEdge, int destLane, int fromLane) const {
1349
return destEdge != nullptr && find_if(myConnections.begin(), myConnections.end(), connections_toedgelane_finder(destEdge, destLane, fromLane)) != myConnections.end();
1350
}
1351
1352
1353
bool
1354
NBEdge::isConnectedTo(const NBEdge* e, const bool ignoreTurnaround) const {
1355
if (!ignoreTurnaround && (e == myTurnDestination)) {
1356
return true;
1357
}
1358
return
1359
find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(e))
1360
!=
1361
myConnections.end();
1362
1363
}
1364
1365
1366
const EdgeVector*
1367
NBEdge::getConnectedSorted() {
1368
// check whether connections exist and if not, use edges from the node
1369
EdgeVector outgoing;
1370
if (myConnections.size() == 0) {
1371
outgoing = myTo->getOutgoingEdges();
1372
} else {
1373
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1374
if (find(outgoing.begin(), outgoing.end(), (*i).toEdge) == outgoing.end()) {
1375
outgoing.push_back((*i).toEdge);
1376
}
1377
}
1378
}
1379
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
1380
if (it->fromLane < 0 && it->toLane < 0) {
1381
// found an edge that shall not be connected
1382
EdgeVector::iterator forbidden = std::find(outgoing.begin(), outgoing.end(), it->toEdge);
1383
if (forbidden != outgoing.end()) {
1384
outgoing.erase(forbidden);
1385
}
1386
}
1387
}
1388
// allocate the sorted container
1389
int size = (int) outgoing.size();
1390
EdgeVector* edges = new EdgeVector();
1391
edges->reserve(size);
1392
for (EdgeVector::const_iterator i = outgoing.begin(); i != outgoing.end(); i++) {
1393
NBEdge* outedge = *i;
1394
if (outedge != nullptr && outedge != myTurnDestination) {
1395
edges->push_back(outedge);
1396
}
1397
}
1398
std::sort(edges->begin(), edges->end(), NBContHelper::relative_outgoing_edge_sorter(this));
1399
return edges;
1400
}
1401
1402
1403
EdgeVector
1404
NBEdge::getConnectedEdges() const {
1405
EdgeVector ret;
1406
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1407
if (find(ret.begin(), ret.end(), (*i).toEdge) == ret.end()) {
1408
ret.push_back((*i).toEdge);
1409
}
1410
}
1411
return ret;
1412
}
1413
1414
1415
EdgeVector
1416
NBEdge::getIncomingEdges() const {
1417
EdgeVector ret;
1418
const EdgeVector& candidates = myFrom->getIncomingEdges();
1419
for (EdgeVector::const_iterator i = candidates.begin(); i != candidates.end(); i++) {
1420
if ((*i)->isConnectedTo(this)) {
1421
ret.push_back(*i);
1422
}
1423
}
1424
return ret;
1425
}
1426
1427
1428
std::vector<int>
1429
NBEdge::getConnectionLanes(NBEdge* currentOutgoing, bool withBikes) const {
1430
std::vector<int> ret;
1431
if (currentOutgoing != myTurnDestination) {
1432
for (const Connection& c : myConnections) {
1433
if (c.toEdge == currentOutgoing && (withBikes || getPermissions(c.fromLane) != SVC_BICYCLE)) {
1434
ret.push_back(c.fromLane);
1435
}
1436
}
1437
}
1438
return ret;
1439
}
1440
1441
1442
void
1443
NBEdge::sortOutgoingConnectionsByAngle() {
1444
sort(myConnections.begin(), myConnections.end(), connections_relative_edgelane_sorter(this));
1445
}
1446
1447
1448
void
1449
NBEdge::sortOutgoingConnectionsByIndex() {
1450
sort(myConnections.begin(), myConnections.end(), connections_sorter);
1451
}
1452
1453
1454
void
1455
NBEdge::remapConnections(const EdgeVector& incoming) {
1456
EdgeVector connected = getConnectedEdges();
1457
for (EdgeVector::const_iterator i = incoming.begin(); i != incoming.end(); i++) {
1458
NBEdge* inc = *i;
1459
// We have to do this
1460
inc->myStep = EdgeBuildingStep::EDGE2EDGES;
1461
// add all connections
1462
for (EdgeVector::iterator j = connected.begin(); j != connected.end(); j++) {
1463
inc->addEdge2EdgeConnection(*j);
1464
}
1465
inc->removeFromConnections(this);
1466
}
1467
}
1468
1469
1470
void
1471
NBEdge::removeFromConnections(NBEdge* toEdge, int fromLane, int toLane, bool tryLater, const bool adaptToLaneRemoval,
1472
const bool keepPossibleTurns) {
1473
// remove from "myConnections"
1474
const int fromLaneRemoved = adaptToLaneRemoval && fromLane >= 0 ? fromLane : -1;
1475
const int toLaneRemoved = adaptToLaneRemoval && toLane >= 0 ? toLane : -1;
1476
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
1477
Connection& c = *i;
1478
if ((toEdge == nullptr || c.toEdge == toEdge)
1479
&& (fromLane < 0 || c.fromLane == fromLane)
1480
&& (toLane < 0 || c.toLane == toLane)) {
1481
if (myTo->isTLControlled()) {
1482
std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1483
for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1484
(*it)->removeConnection(NBConnection(this, c.fromLane, c.toEdge, c.toLane));
1485
}
1486
}
1487
i = myConnections.erase(i);
1488
tryLater = false;
1489
} else {
1490
if (fromLaneRemoved >= 0 && c.fromLane > fromLaneRemoved) {
1491
if (myTo->isTLControlled()) {
1492
std::set<NBTrafficLightDefinition*> tldefs = myTo->getControllingTLS();
1493
for (std::set<NBTrafficLightDefinition*>::iterator it = tldefs.begin(); it != tldefs.end(); it++) {
1494
for (NBConnectionVector::iterator tlcon = (*it)->getControlledLinks().begin(); tlcon != (*it)->getControlledLinks().end(); ++tlcon) {
1495
NBConnection& tc = *tlcon;
1496
if (tc.getTo() == c.toEdge && tc.getFromLane() == c.fromLane && tc.getToLane() == c.toLane) {
1497
tc.shiftLaneIndex(this, -1);
1498
}
1499
}
1500
}
1501
}
1502
//std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceFromLane=" << c.fromLane << " (to=" << c.toLane << ")\n";
1503
c.fromLane--;
1504
}
1505
if (toLaneRemoved >= 0 && c.toLane > toLaneRemoved && (toEdge == nullptr || c.toEdge == toEdge)) {
1506
//std::cout << getID() << " removeFromConnections fromLane=" << fromLane << " to=" << Named::getIDSecure(toEdge) << " toLane=" << toLane << " reduceToLane=" << c.toLane << " (from=" << c.fromLane << ")\n";
1507
c.toLane--;
1508
}
1509
++i;
1510
}
1511
}
1512
// check whether it was the turn destination
1513
if (myTurnDestination == toEdge && fromLane < 0) {
1514
myTurnDestination = nullptr;
1515
}
1516
if (myPossibleTurnDestination == toEdge && fromLane < 0 && !keepPossibleTurns) {
1517
myPossibleTurnDestination = nullptr;
1518
}
1519
if (tryLater) {
1520
myConnectionsToDelete.push_back(Connection(fromLane, toEdge, toLane));
1521
#ifdef DEBUG_CONNECTION_GUESSING
1522
if (DEBUGCOND) {
1523
std::cout << "removeFromConnections " << getID() << "_" << fromLane << "->" << toEdge->getID() << "_" << toLane << "\n";
1524
for (Connection& c : myConnections) {
1525
std::cout << " conn " << c.getDescription(this) << "\n";
1526
}
1527
for (Connection& c : myConnectionsToDelete) {
1528
std::cout << " connToDelete " << c.getDescription(this) << "\n";
1529
}
1530
}
1531
#endif
1532
}
1533
}
1534
1535
1536
bool
1537
NBEdge::removeFromConnections(const NBEdge::Connection& connectionToRemove) {
1538
// iterate over connections
1539
for (auto i = myConnections.begin(); i != myConnections.end(); i++) {
1540
if ((i->toEdge == connectionToRemove.toEdge) && (i->fromLane == connectionToRemove.fromLane) && (i->toLane == connectionToRemove.toLane)) {
1541
// remove connection
1542
myConnections.erase(i);
1543
return true;
1544
}
1545
}
1546
// assert(false);
1547
return false;
1548
}
1549
1550
1551
void
1552
NBEdge::invalidateConnections(bool reallowSetting) {
1553
myTurnDestination = nullptr;
1554
myConnections.clear();
1555
if (reallowSetting) {
1556
myStep = EdgeBuildingStep::INIT;
1557
} else {
1558
myStep = EdgeBuildingStep::INIT_REJECT_CONNECTIONS;
1559
}
1560
}
1561
1562
1563
void
1564
NBEdge::replaceInConnections(NBEdge* which, NBEdge* by, int laneOff) {
1565
// replace in "_connectedEdges"
1566
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1567
if ((*i).toEdge == which) {
1568
(*i).toEdge = by;
1569
(*i).toLane += laneOff;
1570
}
1571
}
1572
// check whether it was the turn destination
1573
if (myTurnDestination == which) {
1574
myTurnDestination = by;
1575
}
1576
}
1577
1578
void
1579
NBEdge::replaceInConnections(NBEdge* which, const std::vector<NBEdge::Connection>& origConns) {
1580
std::map<int, int> laneMap;
1581
int minLane = -1;
1582
int maxLane = -1;
1583
// get lanes used to approach the edge to remap
1584
bool wasConnected = false;
1585
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1586
if ((*i).toEdge != which) {
1587
continue;
1588
}
1589
wasConnected = true;
1590
if ((*i).fromLane != -1) {
1591
int fromLane = (*i).fromLane;
1592
laneMap[(*i).toLane] = fromLane;
1593
if (minLane == -1 || minLane > fromLane) {
1594
minLane = fromLane;
1595
}
1596
if (maxLane == -1 || maxLane < fromLane) {
1597
maxLane = fromLane;
1598
}
1599
}
1600
}
1601
if (!wasConnected) {
1602
return;
1603
}
1604
// add new connections
1605
std::vector<NBEdge::Connection> conns = origConns;
1606
EdgeVector origTargets = getSuccessors();
1607
for (std::vector<NBEdge::Connection>::iterator i = conns.begin(); i != conns.end(); ++i) {
1608
if ((*i).toEdge == which || (*i).toEdge == this
1609
// if we already have connections to the target edge, do not add new ones as they are probably from a circular replacement
1610
|| std::find(origTargets.begin(), origTargets.end(), (*i).toEdge) != origTargets.end()) {
1611
#ifdef DEBUG_REPLACECONNECTION
1612
if (DEBUGCOND) {
1613
std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID()
1614
<< " origTargets=" << toString(origTargets) << " newTarget=" << i->toEdge->getID() << " skipped\n";
1615
}
1616
#endif
1617
continue;
1618
}
1619
if (which->getStep() == EdgeBuildingStep::EDGE2EDGES) {
1620
// do not set lane-level connections
1621
replaceInConnections(which, (*i).toEdge, 0);
1622
continue;
1623
}
1624
int fromLane = (*i).fromLane;
1625
int toUse = -1;
1626
if (laneMap.find(fromLane) == laneMap.end()) {
1627
if (fromLane >= 0 && fromLane <= minLane) {
1628
toUse = minLane;
1629
// patch laneMap to avoid crossed-over connections
1630
for (auto& item : laneMap) {
1631
if (item.first < fromLane) {
1632
item.second = MIN2(item.second, minLane);
1633
}
1634
}
1635
}
1636
if (fromLane >= 0 && fromLane >= maxLane) {
1637
toUse = maxLane;
1638
// patch laneMap to avoid crossed-over connections
1639
for (auto& item : laneMap) {
1640
if (item.first > fromLane) {
1641
item.second = MAX2(item.second, maxLane);
1642
}
1643
}
1644
}
1645
} else {
1646
toUse = laneMap[fromLane];
1647
}
1648
if (toUse == -1) {
1649
toUse = 0;
1650
}
1651
#ifdef DEBUG_REPLACECONNECTION
1652
if (DEBUGCOND) {
1653
std::cout << " replaceInConnections edge=" << getID() << " which=" << which->getID() << " origTargets=" << toString(origTargets)
1654
<< " origFrom=" << fromLane << " laneMap=" << joinToString(laneMap, ":", ",") << " minLane=" << minLane << " maxLane=" << maxLane
1655
<< " newTarget=" << i->toEdge->getID() << " fromLane=" << toUse << " toLane=" << i->toLane << "\n";
1656
}
1657
#endif
1658
setConnection(toUse, i->toEdge, i->toLane, Lane2LaneInfoType::COMPUTED, false, i->mayDefinitelyPass, i->keepClear,
1659
i->contPos, i->visibility, i->speed, i->friction, i->customLength, i->customShape, i->uncontrolled);
1660
}
1661
// remove the remapped edge from connections
1662
removeFromConnections(which);
1663
}
1664
1665
1666
void
1667
NBEdge::copyConnectionsFrom(NBEdge* src) {
1668
myStep = src->myStep;
1669
myConnections = src->myConnections;
1670
}
1671
1672
1673
bool
1674
NBEdge::canMoveConnection(const Connection& con, int newFromLane) const {
1675
// only allow using newFromLane if at least 1 vClass is permitted to use
1676
// this connection. If the connection shall be moved to a sidewalk, only create the connection if there is no walking area
1677
const SVCPermissions common = (getPermissions(newFromLane) & con.toEdge->getPermissions(con.toLane));
1678
return (common > 0 && common != SVC_PEDESTRIAN);
1679
}
1680
1681
1682
void
1683
NBEdge::moveConnectionToLeft(int lane) {
1684
#ifdef DEBUG_CONNECTION_CHECKING
1685
std::cout << " moveConnectionToLeft " << getID() << " lane=" << lane << "\n";
1686
#endif
1687
int index = 0;
1688
for (int i = 0; i < (int)myConnections.size(); ++i) {
1689
if (myConnections[i].fromLane == (int)(lane) && canMoveConnection(myConnections[i], lane + 1)) {
1690
index = i;
1691
}
1692
}
1693
std::vector<Connection>::iterator i = myConnections.begin() + index;
1694
Connection c = *i;
1695
myConnections.erase(i);
1696
setConnection(lane + 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1697
}
1698
1699
1700
void
1701
NBEdge::moveConnectionToRight(int lane) {
1702
#ifdef DEBUG_CONNECTION_CHECKING
1703
std::cout << " moveConnectionToRight " << getID() << " lane=" << lane << "\n";
1704
#endif
1705
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1706
if ((*i).fromLane == (int)lane && canMoveConnection(*i, lane - 1)) {
1707
Connection c = *i;
1708
i = myConnections.erase(i);
1709
setConnection(lane - 1, c.toEdge, c.toLane, Lane2LaneInfoType::VALIDATED, false);
1710
return;
1711
}
1712
}
1713
}
1714
1715
1716
double
1717
NBEdge::buildInnerEdges(const NBNode& n, int noInternalNoSplits, int& linkIndex, int& splitIndex) {
1718
const OptionsCont& oc = OptionsCont::getOptions();
1719
const int numPoints = oc.getInt("junctions.internal-link-detail");
1720
const bool joinTurns = oc.getBool("junctions.join-turns");
1721
const double limitTurnSpeed = oc.getFloat("junctions.limit-turn-speed");
1722
const double limitTurnSpeedMinAngle = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle"));
1723
const double limitTurnSpeedMinAngleRail = DEG2RAD(oc.getFloat("junctions.limit-turn-speed.min-angle.railway"));
1724
const double limitTurnSpeedWarnStraight = oc.getFloat("junctions.limit-turn-speed.warn.straight");
1725
const double limitTurnSpeedWarnTurn = oc.getFloat("junctions.limit-turn-speed.warn.turn");
1726
const bool higherSpeed = oc.getBool("junctions.higher-speed");
1727
const double interalJunctionVehicleWidth = oc.getFloat("internal-junctions.vehicle-width");
1728
const double defaultContPos = oc.getFloat("default.connection.cont-pos");
1729
const bool fromRail = isRailway(getPermissions());
1730
std::string innerID = ":" + n.getID();
1731
NBEdge* toEdge = nullptr;
1732
int edgeIndex = linkIndex;
1733
int internalLaneIndex = 0;
1734
int numLanes = 0; // number of lanes that share the same edge
1735
double lengthSum = 0; // total shape length of all lanes that share the same edge
1736
int avoidedIntersectingLeftOriginLane = std::numeric_limits<int>::max();
1737
bool averageLength = true;
1738
double maxCross = 0.;
1739
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
1740
Connection& con = *i;
1741
con.haveVia = false; // reset first since this may be called multiple times
1742
if (con.toEdge == nullptr) {
1743
continue;
1744
}
1745
LinkDirection dir = n.getDirection(this, con.toEdge);
1746
const bool isRightTurn = (dir == LinkDirection::RIGHT || dir == LinkDirection::PARTRIGHT);
1747
const bool isTurn = (isRightTurn || dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT);
1748
// put turning internal lanes on separate edges
1749
if (con.toEdge != toEdge) {
1750
// skip indices to keep some correspondence between edge ids and link indices:
1751
// internalEdgeIndex + internalLaneIndex = linkIndex
1752
edgeIndex = linkIndex;
1753
toEdge = con.toEdge;
1754
internalLaneIndex = 0;
1755
maxCross = MAX2(maxCross, assignInternalLaneLength(i, numLanes, lengthSum, averageLength));
1756
numLanes = 0;
1757
lengthSum = 0;
1758
}
1759
averageLength = !isTurn || joinTurns; // legacy behavior
1760
SVCPermissions conPermissions = getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane);
1761
const int conShapeFlag = (conPermissions & ~SVC_PEDESTRIAN) != 0 ? 0 : NBNode::SCURVE_IGNORE;
1762
PositionVector shape = n.computeInternalLaneShape(this, con, numPoints, myTo, conShapeFlag);
1763
std::vector<int> foeInternalLinks;
1764
1765
if (dir != LinkDirection::STRAIGHT && shape.length() < POSITION_EPS && !(isBidiRail() && getTurnDestination(true) == con.toEdge)) {
1766
WRITE_WARNINGF(TL("Connection '%_%->%_%' is only %m short."), getID(), con.fromLane, con.toEdge->getID(), con.toLane, shape.length());
1767
}
1768
1769
// crossingPosition, list of foe link indices
1770
std::pair<double, std::vector<int> > crossingPositions(-1, std::vector<int>());
1771
std::set<std::string> tmpFoeIncomingLanes;
1772
if (dir != LinkDirection::STRAIGHT || con.contPos != UNSPECIFIED_CONTPOS) {
1773
int index = 0;
1774
std::vector<PositionVector> otherShapes;
1775
const double width1 = MIN2(interalJunctionVehicleWidth / 2, getLaneWidth(con.fromLane) / 2);
1776
const double width1OppositeLeft = 0; // using width1 changes a lot of curves even though they are rarely responsible for collisions
1777
for (const NBEdge* i2 : n.getIncomingEdges()) {
1778
for (const Connection& k2 : i2->getConnections()) {
1779
if (k2.toEdge == nullptr) {
1780
continue;
1781
}
1782
// vehicles are typically less wide than the lane
1783
// they drive on but but bicycle lanes should be kept clear for their whole width
1784
double width2 = k2.toEdge->getLaneWidth(k2.toLane);
1785
if (k2.toEdge->getPermissions(k2.toLane) != SVC_BICYCLE) {
1786
width2 *= 0.5;
1787
}
1788
const bool foes = n.foes(this, con.toEdge, i2, k2.toEdge);
1789
LinkDirection dir2 = n.getDirection(i2, k2.toEdge);
1790
bool needsCont = !isRailway(conPermissions) && (n.needsCont(this, i2, con, k2) || (con.contPos != UNSPECIFIED_CONTPOS && !con.indirectLeft));
1791
const bool avoidIntersectCandidate = !foes && bothLeftTurns(dir, i2, dir2);
1792
bool oppositeLeftIntersect = avoidIntersectCandidate && haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2);
1793
int shapeFlag = 0;
1794
SVCPermissions warn = SVCAll & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_DELIVERY | SVC_RAIL_CLASSES);
1795
// do not warn if only bicycles, pedestrians or delivery vehicles are involved as this is a typical occurrence
1796
if (con.customShape.size() == 0
1797
&& k2.customShape.size() == 0
1798
&& (oppositeLeftIntersect || (avoidedIntersectingLeftOriginLane < con.fromLane && avoidIntersectCandidate))
1799
&& ((i2->getPermissions(k2.fromLane) & warn) != 0
1800
&& (k2.toEdge->getPermissions(k2.toLane) & warn) != 0)) {
1801
// recompute with different curve parameters (unless
1802
// the other connection is "unimportant"
1803
shapeFlag = NBNode::AVOID_INTERSECTING_LEFT_TURNS;
1804
PositionVector origShape = shape;
1805
shape = n.computeInternalLaneShape(this, con, numPoints, myTo, shapeFlag);
1806
oppositeLeftIntersect = haveIntersection(n, shape, i2, k2, numPoints, width1OppositeLeft, width2, shapeFlag);
1807
if (oppositeLeftIntersect
1808
&& (conPermissions & (SVCAll & ~(SVC_BICYCLE | SVC_PEDESTRIAN))) == 0) {
1809
shape = origShape;
1810
} else {
1811
// recompute previously computed crossing positions
1812
if (avoidedIntersectingLeftOriginLane == std::numeric_limits<int>::max()
1813
|| avoidedIntersectingLeftOriginLane < con.fromLane) {
1814
for (const PositionVector& otherShape : otherShapes) {
1815
const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1816
const double minDV = firstIntersection(shape, otherShape, width1OppositeLeft, width2,
1817
"Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1818
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1819
assert(minDV >= 0);
1820
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1821
crossingPositions.first = minDV;
1822
}
1823
}
1824
}
1825
}
1826
// make sure connections further to the left do not get a wider angle
1827
avoidedIntersectingLeftOriginLane = con.fromLane;
1828
}
1829
}
1830
const bool bothPrio = getJunctionPriority(&n) > 0 && i2->getJunctionPriority(&n) > 0;
1831
//std::cout << "n=" << n.getID() << " e1=" << getID() << " prio=" << getJunctionPriority(&n) << " e2=" << i2->getID() << " prio2=" << i2->getJunctionPriority(&n) << " both=" << bothPrio << " bothLeftIntersect=" << bothLeftIntersect(n, shape, dir, i2, k2, numPoints, width2) << " needsCont=" << needsCont << "\n";
1832
// the following special case might get obsolete once we have solved #9745
1833
const bool isBicycleLeftTurn = k2.indirectLeft || (dir2 == LinkDirection::LEFT && (i2->getPermissions(k2.fromLane) & k2.toEdge->getPermissions(k2.toLane)) == SVC_BICYCLE);
1834
// compute the crossing point
1835
if ((needsCont || (bothPrio && oppositeLeftIntersect && !isRailway(conPermissions))) && (!con.indirectLeft || dir2 == LinkDirection::STRAIGHT) && !isBicycleLeftTurn) {
1836
crossingPositions.second.push_back(index);
1837
const PositionVector otherShape = n.computeInternalLaneShape(i2, k2, numPoints, 0, shapeFlag);
1838
otherShapes.push_back(otherShape);
1839
const bool secondIntersection = con.indirectLeft && this == i2 && con.fromLane == k2.fromLane;
1840
const double minDV = firstIntersection(shape, otherShape, width1, width2,
1841
"Could not compute intersection of conflicting internal lanes at node '" + myTo->getID() + "'", secondIntersection);
1842
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) { // !!!?
1843
assert(minDV >= 0);
1844
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1845
crossingPositions.first = minDV;
1846
}
1847
}
1848
}
1849
const bool rightTurnConflict = NBNode::rightTurnConflict(
1850
this, con.toEdge, con.fromLane, i2, k2.toEdge, k2.fromLane);
1851
const bool indirectTurnConflit = con.indirectLeft && this == i2 && (dir2 == LinkDirection::STRAIGHT ||
1852
(con.fromLane < k2.fromLane && (dir2 == LinkDirection::RIGHT || dir2 == LinkDirection::PARTRIGHT)));
1853
const bool mergeConflict = myTo->mergeConflict(this, con, i2, k2, true);
1854
const bool mergeResponse = myTo->mergeConflict(this, con, i2, k2, false);
1855
const bool bidiConflict = myTo->bidiConflict(this, con, i2, k2, true);
1856
// compute foe internal lanes
1857
if (foes || rightTurnConflict || oppositeLeftIntersect || mergeConflict || indirectTurnConflit || bidiConflict) {
1858
foeInternalLinks.push_back(index);
1859
}
1860
// only warn once per pair of intersecting turns
1861
if (oppositeLeftIntersect && getID() > i2->getID()
1862
&& (getPermissions(con.fromLane) & warn) != 0
1863
&& (con.toEdge->getPermissions(con.toLane) & warn) != 0
1864
&& (i2->getPermissions(k2.fromLane) & warn) != 0
1865
&& (k2.toEdge->getPermissions(k2.toLane) & warn) != 0
1866
// do not warn for unregulated nodes
1867
&& n.getType() != SumoXMLNodeType::NOJUNCTION
1868
) {
1869
WRITE_WARNINGF(TL("Intersecting left turns at junction '%' from lane '%' and lane '%' (increase junction radius to avoid this)."),
1870
n.getID(), getLaneID(con.fromLane), i2->getLaneID(k2.fromLane));
1871
}
1872
// compute foe incoming lanes
1873
const bool signalised = hasSignalisedConnectionTo(con.toEdge);
1874
if ((n.forbids(i2, k2.toEdge, this, con.toEdge, signalised) || rightTurnConflict || indirectTurnConflit || mergeResponse)
1875
&& (needsCont || dir == LinkDirection::TURN || (!signalised && this != i2 && !con.indirectLeft))) {
1876
tmpFoeIncomingLanes.insert(i2->getID() + "_" + toString(k2.fromLane));
1877
}
1878
if (bothPrio && oppositeLeftIntersect && getID() < i2->getID()) {
1879
//std::cout << " c1=" << con.getDescription(this) << " c2=" << k2.getDescription(i2) << " bothPrio=" << bothPrio << " oppositeLeftIntersect=" << oppositeLeftIntersect << "\n";
1880
// break symmetry using edge id
1881
// only store link index and resolve actual lane id later (might be multi-lane internal edge)
1882
tmpFoeIncomingLanes.insert(":" + toString(index));
1883
}
1884
index++;
1885
}
1886
}
1887
if (dir == LinkDirection::TURN && crossingPositions.first < 0 && crossingPositions.second.size() != 0 && shape.length() > 2. * POSITION_EPS) {
1888
// let turnarounds wait in the middle if no other crossing point was found and it has a sensible length
1889
// (if endOffset is used, the crossing point is in the middle of the part within the junction shape)
1890
crossingPositions.first = (double)(shape.length() + getEndOffset(con.fromLane)) / 2.;
1891
}
1892
// foe pedestrian crossings
1893
std::vector<NBNode::Crossing*> crossings = n.getCrossings();
1894
for (auto c : crossings) {
1895
const NBNode::Crossing& crossing = *c;
1896
for (EdgeVector::const_iterator it_e = crossing.edges.begin(); it_e != crossing.edges.end(); ++it_e) {
1897
const NBEdge* edge = *it_e;
1898
// compute foe internal lanes
1899
if ((this == edge || con.toEdge == edge) && !isRailway(conPermissions)) {
1900
foeInternalLinks.push_back(index);
1901
if (con.toEdge == edge &&
1902
((isRightTurn && getJunctionPriority(&n) > 0) || (isTurn && con.tlID != ""))) {
1903
// build internal junctions (not for left turns at uncontrolled intersections)
1904
PositionVector crossingShape = crossing.shape;
1905
crossingShape.extrapolate(5.0); // sometimes shapes miss each other by a small margin
1906
const double minDV = firstIntersection(shape, crossingShape, 0, crossing.width / 2);
1907
if (minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS) {
1908
assert(minDV >= 0);
1909
if (crossingPositions.first < 0 || crossingPositions.first > minDV) {
1910
crossingPositions.first = minDV;
1911
}
1912
}
1913
} else if (this == edge && crossing.priority && !myTo->isTLControlled()) {
1914
crossingPositions.first = 0;
1915
}
1916
}
1917
}
1918
index++;
1919
}
1920
1921
}
1922
if (con.contPos == UNSPECIFIED_CONTPOS) {
1923
con.contPos = defaultContPos;
1924
}
1925
if (con.contPos != UNSPECIFIED_CONTPOS) {
1926
// apply custom internal junction position
1927
if (con.contPos <= 0 || con.contPos >= shape.length()) {
1928
// disable internal junction
1929
crossingPositions.first = -1;
1930
} else {
1931
// set custom position
1932
crossingPositions.first = con.contPos;
1933
}
1934
}
1935
1936
// @todo compute the maximum speed allowed based on angular velocity
1937
// see !!! for an explanation (with a_lat_mean ~0.3)
1938
/*
1939
double vmax = (double) 0.3 * (double) 9.80778 *
1940
getLaneShape(con.fromLane).back().distanceTo(
1941
con.toEdge->getLaneShape(con.toLane).front())
1942
/ (double) 2.0 / (double) M_PI;
1943
vmax = MIN2(vmax, ((getSpeed() + con.toEdge->getSpeed()) / (double) 2.0));
1944
*/
1945
if (con.speed == UNSPECIFIED_SPEED) {
1946
if (higherSpeed) {
1947
con.vmax = MAX2(myLanes[con.fromLane].speed, con.toEdge->getLanes()[con.toLane].speed);
1948
} else {
1949
con.vmax = (myLanes[con.fromLane].speed + con.toEdge->getLanes()[con.toLane].speed) / (double) 2.0;
1950
}
1951
if (limitTurnSpeed > 0) {
1952
// see [Odhams and Cole, Models of Driver Speed Choice in Curves, 2004]
1953
const double angleRaw = fabs(GeomHelper::angleDiff(
1954
getLaneShape(con.fromLane).angleAt2D(-2),
1955
con.toEdge->getLaneShape(con.toLane).angleAt2D(0)));
1956
const double angle = MAX2(0.0, angleRaw - (fromRail ? limitTurnSpeedMinAngleRail : limitTurnSpeedMinAngle));
1957
const double length = shape.length2D();
1958
// do not trust the radius of tiny junctions
1959
// formula adapted from [Odhams, Andre and Cole, David, Models of Driver Speed Choice in Curves, 2004]
1960
if (angle > 0 && length > 1) {
1961
// permit higher turning speed on wide lanes
1962
const double radius = length / angle + getLaneWidth(con.fromLane) / 4;
1963
const double limit = sqrt(limitTurnSpeed * radius);
1964
const double reduction = con.vmax - limit;
1965
// always treat connctions at roundabout as turns when warning
1966
const bool atRoundabout = getJunctionPriority(myTo) == JunctionPriority::ROUNDABOUT || con.toEdge->getJunctionPriority(myFrom) == JunctionPriority::ROUNDABOUT;
1967
const LinkDirection dir2 = atRoundabout ? LinkDirection::LEFT : dir;
1968
if ((dir2 == LinkDirection::STRAIGHT && reduction > limitTurnSpeedWarnStraight)
1969
|| (dir2 != LinkDirection::TURN && reduction > limitTurnSpeedWarnTurn)) {
1970
std::string dirType = std::string(dir == LinkDirection::STRAIGHT ? "straight" : "turning");
1971
if (atRoundabout) {
1972
dirType = "roundabout";
1973
}
1974
WRITE_WARNINGF(TL("Speed of % connection '%' reduced by % due to turning radius of % (length=%, angle=%)."),
1975
dirType, con.getDescription(this), reduction, radius, length, RAD2DEG(angleRaw));
1976
}
1977
con.vmax = MIN2(con.vmax, limit);
1978
// value is saved in <net> attribute. Must be set again when importing from .con.xml
1979
// con.speed = con.vmax;
1980
}
1981
assert(con.vmax > 0);
1982
//if (getID() == "-1017000.0.00") {
1983
// std::cout << con.getDescription(this) << " angleRaw=" << angleRaw << " angle=" << RAD2DEG(angle) << " length=" << length << " radius=" << length / angle
1984
// << " vmaxTurn=" << sqrt(limitTurnSpeed * length / angle) << " vmax=" << con.vmax << "\n";
1985
//}
1986
} else if (fromRail && dir == LinkDirection::TURN) {
1987
con.vmax = 0.01;
1988
}
1989
} else {
1990
con.vmax = con.speed;
1991
}
1992
if (con.friction == UNSPECIFIED_FRICTION) {
1993
con.friction = (myLanes[con.fromLane].friction + con.toEdge->getLanes()[con.toLane].friction) / 2.;
1994
}
1995
//
1996
assert(shape.size() >= 2);
1997
// get internal splits if any
1998
con.id = innerID + "_" + toString(edgeIndex);
1999
const double shapeLength = shape.length();
2000
double firstLength = shapeLength;
2001
if (crossingPositions.first > 0 && crossingPositions.first < shapeLength) {
2002
std::pair<PositionVector, PositionVector> split = shape.splitAt(crossingPositions.first);
2003
con.shape = split.first;
2004
con.foeIncomingLanes = std::vector<std::string>(tmpFoeIncomingLanes.begin(), tmpFoeIncomingLanes.end());
2005
con.foeInternalLinks = foeInternalLinks; // resolve link indices to lane ids later
2006
if (i != myConnections.begin() && (i - 1)->toEdge == con.toEdge && (i - 1)->haveVia) {
2007
--splitIndex;
2008
con.internalViaLaneIndex = (i - 1)->internalViaLaneIndex + 1;
2009
}
2010
con.viaID = innerID + "_" + toString(splitIndex + noInternalNoSplits);
2011
++splitIndex;
2012
con.viaShape = split.second;
2013
con.haveVia = true;
2014
firstLength = con.shape.length();
2015
} else {
2016
con.shape = shape;
2017
}
2018
con.internalLaneIndex = internalLaneIndex;
2019
++internalLaneIndex;
2020
++linkIndex;
2021
++numLanes;
2022
if (con.customLength != UNSPECIFIED_LOADED_LENGTH) {
2023
// split length proportionally
2024
lengthSum += (shapeLength != 0 ? firstLength / shapeLength : 1) * con.customLength;
2025
} else {
2026
lengthSum += firstLength;
2027
}
2028
}
2029
return MAX2(maxCross, assignInternalLaneLength(myConnections.end(), numLanes, lengthSum, averageLength));
2030
}
2031
2032
2033
double
2034
NBEdge::assignInternalLaneLength(std::vector<Connection>::iterator i, int numLanes, double lengthSum, bool averageLength) {
2035
// assign average length to all lanes of the same internal edge if averageLength is set
2036
// the lengthSum only covers the part up to the first internal junction
2037
// TODO This code assumes that either all connections in question have a via or none
2038
double maxCross = 0.;
2039
assert(i - myConnections.begin() >= numLanes);
2040
for (int prevIndex = 1; prevIndex <= numLanes; prevIndex++) {
2041
//std::cout << " con=" << (*(i - prevIndex)).getDescription(this) << " numLanes=" << numLanes << " avgLength=" << lengthSum / numLanes << "\n";
2042
Connection& c = (*(i - prevIndex));
2043
const double minLength = c.customLength != UNSPECIFIED_LOADED_LENGTH ? pow(10, -gPrecision) : POSITION_EPS;
2044
c.length = MAX2(minLength, averageLength ? lengthSum / numLanes : c.shape.length());
2045
if (c.haveVia) {
2046
c.viaLength = MAX2(minLength, c.viaShape.length());
2047
}
2048
if (c.customLength != UNSPECIFIED_LOADED_LENGTH) {
2049
if (c.haveVia) {
2050
// split length proportionally
2051
const double a = c.viaLength / (c.shape.length() + c.viaLength);
2052
c.viaLength = MAX2(minLength, a * c.customLength);
2053
}
2054
if (!averageLength) {
2055
c.length = MAX2(minLength, c.customLength - c.viaLength);
2056
}
2057
}
2058
if (c.haveVia) {
2059
// we need to be able to leave from the internal junction by accelerating from 0
2060
maxCross = MAX2(maxCross, sqrt(2. * c.viaLength)); // t = sqrt(2*s/a) and we assume 'a' is at least 1 (default value for tram in SUMOVTypeParameter)
2061
}
2062
// we need to be able to cross the junction in one go but not if we have an indirect left turn
2063
if (c.indirectLeft) {
2064
maxCross = MAX2(maxCross, MAX2(c.length, c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2065
} else {
2066
maxCross = MAX2(maxCross, (c.length + c.viaLength) / MAX2(c.vmax, NBOwnTLDef::MIN_SPEED_CROSSING_TIME));
2067
}
2068
}
2069
return maxCross;
2070
}
2071
2072
2073
double
2074
NBEdge::firstIntersection(const PositionVector& v1, const PositionVector& v2, double width1, double width2, const std::string& error, bool secondIntersection) {
2075
double intersect = std::numeric_limits<double>::max();
2076
if (v2.length() < POSITION_EPS) {
2077
return intersect;
2078
}
2079
try {
2080
PositionVector v1Right = v1;
2081
v1Right.move2side(width1);
2082
2083
PositionVector v1Left = v1;
2084
v1Left.move2side(-width1);
2085
2086
PositionVector v2Right = v2;
2087
v2Right.move2side(width2);
2088
2089
PositionVector v2Left = v2;
2090
v2Left.move2side(-width2);
2091
2092
// intersect all border combinations
2093
bool skip = secondIntersection;
2094
for (double cand : v1Left.intersectsAtLengths2D(v2Right)) {
2095
if (skip) {
2096
skip = false;
2097
continue;
2098
}
2099
intersect = MIN2(intersect, cand);
2100
}
2101
skip = secondIntersection;
2102
for (double cand : v1Left.intersectsAtLengths2D(v2Left)) {
2103
if (skip) {
2104
skip = false;
2105
continue;
2106
}
2107
intersect = MIN2(intersect, cand);
2108
}
2109
skip = secondIntersection;
2110
for (double cand : v1Right.intersectsAtLengths2D(v2Right)) {
2111
if (skip) {
2112
skip = false;
2113
continue;
2114
}
2115
intersect = MIN2(intersect, cand);
2116
}
2117
skip = secondIntersection;
2118
for (double cand : v1Right.intersectsAtLengths2D(v2Left)) {
2119
if (skip) {
2120
skip = false;
2121
continue;
2122
}
2123
intersect = MIN2(intersect, cand);
2124
}
2125
} catch (InvalidArgument&) {
2126
if (error != "") {
2127
WRITE_WARNING(error);
2128
}
2129
}
2130
//std::cout << " v1=" << v1 << " v2Right=" << v2Right << " v2Left=" << v2Left << "\n";
2131
//std::cout << " intersectsRight=" << toString(v1.intersectsAtLengths2D(v2Right)) << "\n";
2132
//std::cout << " intersectsLeft=" << toString(v1.intersectsAtLengths2D(v2Left)) << "\n";
2133
return intersect;
2134
}
2135
2136
2137
bool
2138
NBEdge::bothLeftTurns(LinkDirection dir, const NBEdge* otherFrom, LinkDirection dir2) const {
2139
if (otherFrom == this) {
2140
// not an opposite pair
2141
return false;
2142
}
2143
return (dir == LinkDirection::LEFT || dir == LinkDirection::PARTLEFT) && (dir2 == LinkDirection::LEFT || dir2 == LinkDirection::PARTLEFT);
2144
}
2145
2146
bool
2147
NBEdge::haveIntersection(const NBNode& n, const PositionVector& shape, const NBEdge* otherFrom, const NBEdge::Connection& otherCon, int numPoints,
2148
double width1, double width2, int shapeFlag) const {
2149
const PositionVector otherShape = n.computeInternalLaneShape(otherFrom, otherCon, numPoints, 0, shapeFlag);
2150
const double minDV = firstIntersection(shape, otherShape, width1, width2);
2151
return minDV < shape.length() - POSITION_EPS && minDV > POSITION_EPS;
2152
}
2153
2154
2155
// -----------
2156
int
2157
NBEdge::getJunctionPriority(const NBNode* const node) const {
2158
if (node == myFrom) {
2159
return myFromJunctionPriority;
2160
} else {
2161
return myToJunctionPriority;
2162
}
2163
}
2164
2165
2166
void
2167
NBEdge::setJunctionPriority(const NBNode* const node, int prio) {
2168
if (node == myFrom) {
2169
myFromJunctionPriority = prio;
2170
#ifdef DEBUG_JUNCTIONPRIO
2171
setParameter("fromPrio", toString(prio));
2172
#endif
2173
} else {
2174
myToJunctionPriority = prio;
2175
#ifdef DEBUG_JUNCTIONPRIO
2176
setParameter("toPrio", toString(prio));
2177
#endif
2178
}
2179
}
2180
2181
2182
double
2183
NBEdge::getAngleAtNode(const NBNode* const atNode) const {
2184
if (atNode == myFrom) {
2185
return GeomHelper::legacyDegree(myGeom.angleAt2D(0));
2186
}
2187
assert(atNode == myTo);
2188
return GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2189
}
2190
2191
2192
double
2193
NBEdge::getAngleAtNodeNormalized(const NBNode* const atNode) const {
2194
double res;
2195
if (atNode == myFrom) {
2196
res = GeomHelper::legacyDegree(myGeom.angleAt2D(0)) - 180;
2197
} else {
2198
assert(atNode == myTo);
2199
res = GeomHelper::legacyDegree(myGeom.angleAt2D(-2));
2200
}
2201
if (res < 0) {
2202
res += 360;
2203
}
2204
return res;
2205
}
2206
2207
2208
double
2209
NBEdge::getAngleAtNodeToCenter(const NBNode* const atNode) const {
2210
if (atNode == myFrom) {
2211
double res = myStartAngle - 180;
2212
if (res < 0) {
2213
res += 360;
2214
}
2215
return res;
2216
} else {
2217
assert(atNode == myTo);
2218
return myEndAngle;
2219
}
2220
}
2221
2222
2223
void
2224
NBEdge::setTurningDestination(NBEdge* e, bool onlyPossible) {
2225
if (!onlyPossible) {
2226
myTurnDestination = e;
2227
}
2228
myPossibleTurnDestination = e;
2229
}
2230
2231
2232
double
2233
NBEdge::getLaneSpeed(int lane) const {
2234
return myLanes[lane].speed;
2235
}
2236
2237
2238
double
2239
NBEdge::getLaneFriction(int lane) const {
2240
return myLanes[lane].friction;
2241
}
2242
2243
2244
void
2245
NBEdge::resetLaneShapes() {
2246
computeLaneShapes();
2247
}
2248
2249
2250
void
2251
NBEdge::updateChangeRestrictions(SVCPermissions ignoring) {
2252
for (Lane& lane : myLanes) {
2253
if (lane.changeLeft != SVCAll) {
2254
lane.changeLeft = ignoring;
2255
}
2256
if (lane.changeRight != SVCAll) {
2257
lane.changeRight = ignoring;
2258
}
2259
}
2260
for (Connection& con : myConnections) {
2261
if (con.changeLeft != SVC_UNSPECIFIED && con.changeLeft != SVCAll) {
2262
con.changeLeft = ignoring;
2263
}
2264
if (con.changeRight != SVC_UNSPECIFIED && con.changeRight != SVCAll) {
2265
con.changeRight = ignoring;
2266
}
2267
}
2268
}
2269
2270
2271
void
2272
NBEdge::computeLaneShapes() {
2273
// vissim needs this
2274
if (myFrom == myTo) {
2275
return;
2276
}
2277
// compute lane offset, first
2278
std::vector<double> offsets(myLanes.size(), 0.);
2279
double offset = 0;
2280
for (int i = (int)myLanes.size() - 2; i >= 0; --i) {
2281
offset += (getLaneWidth(i) + getLaneWidth(i + 1)) / 2.;
2282
offsets[i] = offset;
2283
}
2284
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
2285
double width = 0;
2286
for (int i = 0; i < (int)myLanes.size(); ++i) {
2287
width += getLaneWidth(i);
2288
}
2289
offset = -width / 2. + getLaneWidth((int)myLanes.size() - 1) / 2.;
2290
} else {
2291
double laneWidth = myLanes.back().width != UNSPECIFIED_WIDTH ? myLanes.back().width : SUMO_const_laneWidth;
2292
offset = laneWidth / 2.;
2293
}
2294
if (myLaneSpreadFunction == LaneSpreadFunction::ROADCENTER) {
2295
for (NBEdge* e : myTo->getOutgoingEdges()) {
2296
if (e->getToNode() == myFrom && getInnerGeometry().reverse() == e->getInnerGeometry()) {
2297
offset += (e->getTotalWidth() - getTotalWidth()) / 2;
2298
break;
2299
}
2300
}
2301
}
2302
2303
for (int i = 0; i < (int)myLanes.size(); ++i) {
2304
offsets[i] += offset;
2305
}
2306
2307
// build the shape of each lane
2308
for (int i = 0; i < (int)myLanes.size(); ++i) {
2309
if (myLanes[i].customShape.size() != 0) {
2310
myLanes[i].shape = myLanes[i].customShape;
2311
continue;
2312
}
2313
try {
2314
myLanes[i].shape = computeLaneShape(i, offsets[i]);
2315
} catch (InvalidArgument& e) {
2316
WRITE_WARNINGF(TL("In lane '%': lane shape could not be determined (%)."), getLaneID(i), e.what());
2317
myLanes[i].shape = myGeom;
2318
}
2319
}
2320
}
2321
2322
2323
PositionVector
2324
NBEdge::computeLaneShape(int lane, double offset) const {
2325
PositionVector shape = myGeom;
2326
try {
2327
shape.move2side(offset);
2328
} catch (InvalidArgument& e) {
2329
WRITE_WARNINGF(TL("In lane '%': Could not build shape (%)."), getLaneID(lane), e.what());
2330
}
2331
return shape;
2332
}
2333
2334
2335
void
2336
NBEdge::computeAngle() {
2337
// taking the angle at the first might be unstable, thus we take the angle
2338
// at a certain distance. (To compare two edges, additional geometry
2339
// segments are considered to resolve ambiguities)
2340
const bool hasFromShape = myFrom->getShape().size() > 0;
2341
const bool hasToShape = myTo->getShape().size() > 0;
2342
Position fromCenter = (hasFromShape ? myFrom->getShape().getCentroid() : myFrom->getPosition());
2343
Position toCenter = (hasToShape ? myTo->getShape().getCentroid() : myTo->getPosition());
2344
PositionVector shape = myGeom;
2345
if ((hasFromShape || hasToShape) && getNumLanes() > 0) {
2346
if (myLaneSpreadFunction == LaneSpreadFunction::RIGHT) {
2347
shape = myLanes[getNumLanes() - 1].shape ;
2348
} else {
2349
shape = myLanes[getNumLanes() / 2].shape;
2350
if (getNumLanes() % 2 == 0) {
2351
// there is no center lane. shift to get the center
2352
shape.move2side(getLaneWidth(getNumLanes() / 2) * 0.5);
2353
}
2354
}
2355
}
2356
2357
// if the junction shape is suspicious we cannot trust the angle to the centroid
2358
const bool suspiciousFromShape = hasFromShape && (myFrom->getShape().distance2D(shape[0]) > 2 * POSITION_EPS
2359
|| myFrom->getShape().around(shape[-1])
2360
|| !(myFrom->getShape().around(fromCenter)));
2361
const bool suspiciousToShape = hasToShape && (myTo->getShape().distance2D(shape[-1]) > 2 * POSITION_EPS
2362
|| myTo->getShape().around(shape[0])
2363
|| !(myTo->getShape().around(toCenter)));
2364
2365
const double angleLookahead = MIN2(shape.length2D() / 2, ANGLE_LOOKAHEAD);
2366
const Position referencePosStart = shape.positionAtOffset2D(angleLookahead);
2367
const Position referencePosEnd = shape.positionAtOffset2D(shape.length2D() - angleLookahead);
2368
2369
myStartAngle = GeomHelper::legacyDegree(fromCenter.angleTo2D(referencePosStart), true);
2370
const double myStartAngle2 = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(referencePosStart), true);
2371
const double myStartAngle3 = getAngleAtNode(myFrom);
2372
myEndAngle = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(toCenter), true);
2373
const double myEndAngle2 = GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myTo->getPosition()), true);
2374
const double myEndAngle3 = getAngleAtNode(myTo);
2375
2376
#ifdef DEBUG_ANGLES
2377
if (DEBUGCOND) {
2378
if (suspiciousFromShape) {
2379
std::cout << "suspiciousFromShape len=" << shape.length() << " startA=" << myStartAngle << " startA2=" << myStartAngle2 << " startA3=" << myStartAngle3
2380
<< " rel=" << NBHelpers::normRelAngle(myStartAngle, myStartAngle2)
2381
<< " fromCenter=" << fromCenter
2382
<< " fromPos=" << myFrom->getPosition()
2383
<< " refStart=" << referencePosStart
2384
<< "\n";
2385
}
2386
if (suspiciousToShape) {
2387
std::cout << "suspiciousToShape len=" << shape.length() << " endA=" << myEndAngle << " endA2=" << myEndAngle2 << " endA3=" << myEndAngle3
2388
<< " rel=" << NBHelpers::normRelAngle(myEndAngle, myEndAngle2)
2389
<< " toCenter=" << toCenter
2390
<< " toPos=" << myTo->getPosition()
2391
<< " refEnd=" << referencePosEnd
2392
<< "\n";
2393
}
2394
}
2395
#endif
2396
2397
if (suspiciousFromShape && shape.length() > 1) {
2398
myStartAngle = myStartAngle2;
2399
} else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myStartAngle, myStartAngle3)) > 90
2400
// don't trust footpath angles
2401
&& (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2402
myStartAngle = myStartAngle3;
2403
if (myStartAngle < 0) {
2404
myStartAngle += 360;
2405
}
2406
}
2407
2408
if (suspiciousToShape && shape.length() > 1) {
2409
myEndAngle = myEndAngle2;
2410
} else if (suspiciousToShape && fabs(NBHelpers::normRelAngle(myEndAngle, myEndAngle3)) > 90
2411
// don't trust footpath angles
2412
&& (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
2413
myEndAngle = myEndAngle3;
2414
if (myEndAngle < 0) {
2415
myEndAngle += 360;
2416
}
2417
}
2418
2419
myTotalAngle = GeomHelper::legacyDegree(myFrom->getPosition().angleTo2D(myTo->getPosition()), true);
2420
#ifdef DEBUG_ANGLES
2421
if (DEBUGCOND) std::cout << "computeAngle edge=" << getID()
2422
<< " fromCenter=" << fromCenter << " toCenter=" << toCenter
2423
<< " refStart=" << referencePosStart << " refEnd=" << referencePosEnd << " shape=" << shape
2424
<< " hasFromShape=" << hasFromShape
2425
<< " hasToShape=" << hasToShape
2426
<< " numLanes=" << getNumLanes()
2427
<< " shapeLane=" << getNumLanes() / 2
2428
<< " startA=" << myStartAngle << " endA=" << myEndAngle << " totA=" << myTotalAngle << "\n";
2429
#endif
2430
}
2431
2432
2433
double
2434
NBEdge::getShapeStartAngle() const {
2435
const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2436
const Position referencePosStart = myGeom.positionAtOffset2D(angleLookahead);
2437
return GeomHelper::legacyDegree(myGeom.front().angleTo2D(referencePosStart), true);
2438
}
2439
2440
2441
double
2442
NBEdge::getShapeEndAngle() const {
2443
const double angleLookahead = MIN2(myGeom.length2D() / 2, ANGLE_LOOKAHEAD);
2444
const Position referencePosEnd = myGeom.positionAtOffset2D(myGeom.length2D() - angleLookahead);
2445
return GeomHelper::legacyDegree(referencePosEnd.angleTo2D(myGeom.back()), true);
2446
}
2447
2448
2449
bool
2450
NBEdge::hasPermissions() const {
2451
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2452
if ((*i).permissions != SVCAll) {
2453
return true;
2454
}
2455
}
2456
return false;
2457
}
2458
2459
2460
bool
2461
NBEdge::hasLaneSpecificPermissions() const {
2462
std::vector<Lane>::const_iterator i = myLanes.begin();
2463
SVCPermissions firstLanePermissions = i->permissions;
2464
i++;
2465
for (; i != myLanes.end(); ++i) {
2466
if (i->permissions != firstLanePermissions) {
2467
return true;
2468
}
2469
}
2470
return false;
2471
}
2472
2473
2474
bool
2475
NBEdge::hasLaneSpecificSpeed() const {
2476
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2477
if (i->speed != getSpeed()) {
2478
return true;
2479
}
2480
}
2481
return false;
2482
}
2483
2484
bool
2485
NBEdge::hasLaneSpecificFriction() const {
2486
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2487
if (i->friction != myLanes.begin()->friction) {
2488
return true;
2489
}
2490
}
2491
return false;
2492
}
2493
2494
bool
2495
NBEdge::hasLaneSpecificWidth() const {
2496
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2497
if (i->width != myLanes.begin()->width) {
2498
return true;
2499
}
2500
}
2501
return false;
2502
}
2503
2504
2505
bool
2506
NBEdge::hasLaneSpecificType() const {
2507
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2508
if (i->type != myLanes.begin()->type) {
2509
return true;
2510
}
2511
}
2512
return false;
2513
}
2514
2515
2516
bool
2517
NBEdge::hasLaneSpecificEndOffset() const {
2518
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2519
if (i->endOffset != myLanes.begin()->endOffset) {
2520
return true;
2521
}
2522
}
2523
return false;
2524
}
2525
2526
2527
bool
2528
NBEdge::hasLaneSpecificStopOffsets() const {
2529
for (const auto& lane : myLanes) {
2530
if (lane.laneStopOffset.isDefined()) {
2531
if (myEdgeStopOffset.isDefined() || (myEdgeStopOffset != lane.laneStopOffset)) {
2532
return true;
2533
}
2534
}
2535
}
2536
return false;
2537
}
2538
2539
2540
bool
2541
NBEdge::hasAccelLane() const {
2542
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2543
if (i->accelRamp) {
2544
return true;
2545
}
2546
}
2547
return false;
2548
}
2549
2550
2551
bool
2552
NBEdge::hasCustomLaneShape() const {
2553
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2554
if (i->customShape.size() > 0) {
2555
return true;
2556
}
2557
}
2558
return false;
2559
}
2560
2561
2562
bool
2563
NBEdge::hasLaneParams() const {
2564
for (std::vector<Lane>::const_iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
2565
if (i->getParametersMap().size() > 0) {
2566
return true;
2567
}
2568
}
2569
return false;
2570
}
2571
2572
bool
2573
NBEdge::prohibitsChanging() const {
2574
for (const Lane& lane : myLanes) {
2575
if (lane.changeLeft != SVCAll || lane.changeRight != SVCAll) {
2576
return true;
2577
}
2578
}
2579
return false;
2580
}
2581
2582
bool
2583
NBEdge::needsLaneSpecificOutput() const {
2584
return (hasLaneSpecificPermissions()
2585
|| hasLaneSpecificSpeed()
2586
|| hasLaneSpecificWidth()
2587
|| hasLaneSpecificType()
2588
|| hasLaneSpecificEndOffset()
2589
|| hasLaneSpecificStopOffsets()
2590
|| hasAccelLane()
2591
|| hasCustomLaneShape()
2592
|| hasLaneParams()
2593
|| prohibitsChanging()
2594
|| (!myLanes.empty() && myLanes.back().oppositeID != ""));
2595
}
2596
2597
2598
2599
bool
2600
NBEdge::computeEdge2Edges(bool noLeftMovers) {
2601
#ifdef DEBUG_CONNECTION_GUESSING
2602
if (DEBUGCOND) {
2603
std::cout << "computeEdge2Edges edge=" << getID() << " step=" << (int)myStep << " noLeftMovers=" << noLeftMovers << "\n";
2604
for (Connection& c : myConnections) {
2605
std::cout << " conn " << c.getDescription(this) << "\n";
2606
}
2607
for (Connection& c : myConnectionsToDelete) {
2608
std::cout << " connToDelete " << c.getDescription(this) << "\n";
2609
}
2610
}
2611
#endif
2612
// return if this relationship has been build in previous steps or
2613
// during the import
2614
if (myStep >= EdgeBuildingStep::EDGE2EDGES) {
2615
return true;
2616
}
2617
const bool fromRail = isRailway(getPermissions());
2618
for (NBEdge* out : myTo->getOutgoingEdges()) {
2619
if (noLeftMovers && myTo->isLeftMover(this, out)) {
2620
continue;
2621
}
2622
// avoid sharp railway turns
2623
if (fromRail && isRailway(out->getPermissions())) {
2624
const double angle = fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), out->getAngleAtNode(myTo)));
2625
if (angle > 150) {
2626
continue;
2627
} else if (angle > 90) {
2628
// possibly the junction is large enough to achieve a plausible radius:
2629
const PositionVector& fromShape = myLanes.front().shape;
2630
const PositionVector& toShape = out->getLanes().front().shape;
2631
PositionVector shape = myTo->computeSmoothShape(fromShape, toShape, 5, getTurnDestination() == out, 5, 5);
2632
const double radius = shape.length2D() / DEG2RAD(angle);
2633
const double minRadius = (getPermissions() & SVC_TRAM) != 0 ? 20 : 80;
2634
//std::cout << getID() << " to=" << out->getID() << " radius=" << radius << " minRadius=" << minRadius << "\n";
2635
if (radius < minRadius) {
2636
continue;
2637
}
2638
}
2639
}
2640
if (out == myTurnDestination) {
2641
// will be added by appendTurnaround
2642
continue;
2643
}
2644
if ((getPermissions() & out->getPermissions() & ~SVC_PEDESTRIAN) == 0) {
2645
// no common permissions
2646
continue;
2647
}
2648
myConnections.push_back(Connection(-1, out, -1));
2649
}
2650
myStep = EdgeBuildingStep::EDGE2EDGES;
2651
return true;
2652
}
2653
2654
2655
bool
2656
NBEdge::computeLanes2Edges() {
2657
#ifdef DEBUG_CONNECTION_GUESSING
2658
if (DEBUGCOND) {
2659
std::cout << "computeLanes2Edges edge=" << getID() << " step=" << (int)myStep << "\n";
2660
for (Connection& c : myConnections) {
2661
std::cout << " conn " << c.getDescription(this) << "\n";
2662
}
2663
for (Connection& c : myConnectionsToDelete) {
2664
std::cout << " connToDelete " << c.getDescription(this) << "\n";
2665
}
2666
}
2667
#endif
2668
// return if this relationship has been build in previous steps or
2669
// during the import
2670
if (myStep >= EdgeBuildingStep::LANES2EDGES) {
2671
if (myStep == EdgeBuildingStep::LANES2LANES_USER && myConnections.size() > 1) {
2672
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2673
if ((*i).toEdge == nullptr) {
2674
WRITE_WARNINGF("Inconsistent connection definitions at edge '%'.", getID());
2675
i = myConnections.erase(i);
2676
} else {
2677
i++;
2678
}
2679
}
2680
}
2681
return true;
2682
}
2683
assert(myStep == EdgeBuildingStep::EDGE2EDGES);
2684
// get list of possible outgoing edges sorted by direction clockwise
2685
// the edge in the backward direction (turnaround) is not in the list
2686
const EdgeVector* edges = getConnectedSorted();
2687
if (myConnections.size() != 0 && edges->size() == 0) {
2688
// dead end per definition!?
2689
myConnections.clear();
2690
} else {
2691
// divide the lanes on reachable edges
2692
divideOnEdges(edges);
2693
}
2694
delete edges;
2695
myStep = EdgeBuildingStep::LANES2EDGES;
2696
return true;
2697
}
2698
2699
2700
std::vector<LinkDirection>
2701
NBEdge::decodeTurnSigns(int turnSigns, int shift) {
2702
std::vector<LinkDirection> result;
2703
for (int i = 0; i < 8; i++) {
2704
// see LinkDirection in SUMOXMLDefinitions.h
2705
if ((turnSigns & (1 << (i + shift))) != 0) {
2706
result.push_back((LinkDirection)(1 << i));
2707
}
2708
}
2709
return result;
2710
}
2711
2712
void
2713
NBEdge::updateTurnPermissions(SVCPermissions& perm, LinkDirection dir, SVCPermissions spec, std::vector<LinkDirection> dirs) {
2714
if (dirs.size() > 0) {
2715
if (std::find(dirs.begin(), dirs.end(), dir) == dirs.end()) {
2716
perm &= ~spec;
2717
} else {
2718
perm |= spec;
2719
}
2720
}
2721
}
2722
2723
bool
2724
NBEdge::applyTurnSigns() {
2725
#ifdef DEBUG_TURNSIGNS
2726
std::cout << "applyTurnSigns edge=" << getID() << "\n";
2727
#endif
2728
// build a map of target edges and lanes
2729
std::vector<const NBEdge*> targets;
2730
std::map<const NBEdge*, std::vector<int> > toLaneMap;
2731
for (const Connection& c : myConnections) {
2732
if (myLanes[c.fromLane].turnSigns != 0) {
2733
if (std::find(targets.begin(), targets.end(), c.toEdge) == targets.end()) {
2734
targets.push_back(c.toEdge);
2735
}
2736
toLaneMap[c.toEdge].push_back(c.toLane);
2737
}
2738
}
2739
// might be unsorted due to bike lane connections
2740
for (auto& item : toLaneMap) {
2741
std::sort(item.second.begin(), item.second.end());
2742
}
2743
2744
// check number of distinct signed directions and count the number of signs for each direction
2745
std::map<LinkDirection, int> signCons;
2746
int allDirs = 0;
2747
for (const Lane& lane : myLanes) {
2748
allDirs |= lane.turnSigns;
2749
for (LinkDirection dir : decodeTurnSigns(lane.turnSigns)) {
2750
signCons[dir]++;
2751
}
2752
}
2753
allDirs |= allDirs >> TURN_SIGN_SHIFT_BUS;
2754
allDirs |= allDirs >> TURN_SIGN_SHIFT_TAXI;
2755
allDirs |= allDirs >> TURN_SIGN_SHIFT_BICYCLE;
2756
2757
if ((allDirs & (int)LinkDirection::NODIR) != 0) {
2758
targets.push_back(nullptr); // dead end
2759
}
2760
2761
SVCPermissions defaultPermissions = SVC_PASSENGER | SVC_DELIVERY;
2762
// build a mapping from sign directions to targets
2763
std::vector<LinkDirection> signedDirs = decodeTurnSigns(allDirs);
2764
std::map<LinkDirection, const NBEdge*> dirMap;
2765
#ifdef DEBUG_TURNSIGNS
2766
std::cout << " numDirs=" << signedDirs.size() << " numTargets=" << targets.size() << "\n";
2767
#endif
2768
if (signedDirs.size() > targets.size()) {
2769
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions but only % targets"), getID(), signedDirs.size(), targets.size());
2770
return false;
2771
} else if (signedDirs.size() < targets.size()) {
2772
// we need to drop some targets (i.e. turn-around)
2773
// use sumo-directions as a guide
2774
std::vector<LinkDirection> sumoDirs;
2775
for (const NBEdge* to : targets) {
2776
sumoDirs.push_back(myTo->getDirection(this, to));
2777
}
2778
// remove targets to the left
2779
bool checkMore = true;
2780
while (signedDirs.size() < targets.size() && checkMore) {
2781
checkMore = false;
2782
//std::cout << getID() << " sumoDirs=" << joinToString(sumoDirs, ",") << " signedDirs=" << joinToString(signedDirs, ",") << "\n";
2783
if (sumoDirs.back() != signedDirs.back()) {
2784
targets.pop_back();
2785
sumoDirs.pop_back();
2786
checkMore = true;
2787
}
2788
}
2789
// remove targets to the right
2790
checkMore = true;
2791
while (signedDirs.size() < targets.size() && checkMore) {
2792
checkMore = false;
2793
if (sumoDirs.front() != signedDirs.front()) {
2794
targets.erase(targets.begin());
2795
sumoDirs.erase(sumoDirs.begin());
2796
checkMore = true;
2797
}
2798
}
2799
// remove targets by permissions
2800
int i = 0;
2801
while (signedDirs.size() < targets.size() && i < (int)targets.size()) {
2802
if (targets[i] != nullptr && (targets[i]->getPermissions() & defaultPermissions) == 0) {
2803
targets.erase(targets.begin() + i);
2804
sumoDirs.erase(sumoDirs.begin() + i);
2805
} else {
2806
i++;
2807
}
2808
}
2809
if (signedDirs.size() != targets.size()) {
2810
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed directions and % targets (after target pruning)"), getID(), signedDirs.size(), targets.size());
2811
return false;
2812
}
2813
}
2814
// directions and connections are both sorted from right to left
2815
for (int i = 0; i < (int)signedDirs.size(); i++) {
2816
dirMap[signedDirs[i]] = targets[i];
2817
}
2818
// check whether we have enough target lanes for a each signed direction
2819
for (auto item : signCons) {
2820
const LinkDirection dir = item.first;
2821
if (dir == LinkDirection::NODIR) {
2822
continue;
2823
}
2824
const NBEdge* to = dirMap[dir];
2825
int candidates = to->getNumLanesThatAllow(defaultPermissions, false);
2826
if (candidates == 0) {
2827
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because the target edge '%' has no suitable lanes"), getID(), to->getID());
2828
return false;
2829
}
2830
std::vector<int>& knownTargets = toLaneMap[to];
2831
if ((int)knownTargets.size() < item.second) {
2832
if (candidates < item.second) {
2833
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because there are % signed connections with directions '%' but target edge '%' has only % suitable lanes"),
2834
getID(), item.second, toString(dir), to->getID(), candidates);
2835
return false;
2836
}
2837
int i;
2838
int iInc;
2839
int iEnd;
2840
if (dir > LinkDirection::STRAIGHT) {
2841
// set more targets on the left
2842
i = to->getNumLanes() - 1;
2843
iInc = -1;
2844
iEnd = -1;
2845
} else {
2846
// set more targets on the right
2847
i = 0;
2848
iInc = 1;
2849
iEnd = to->getNumLanes();
2850
}
2851
while ((int)knownTargets.size() < item.second && i != iEnd) {
2852
if ((to->getPermissions(i) & defaultPermissions) != 0) {
2853
if (std::find(knownTargets.begin(), knownTargets.end(), i) == knownTargets.end()) {
2854
knownTargets.push_back(i);
2855
}
2856
}
2857
i += iInc;
2858
}
2859
if ((int)knownTargets.size() != item.second) {
2860
WRITE_WARNINGF(TL("Cannot apply turn sign information for edge '%' because not enough target lanes could be determined for direction '%'"), getID(), toString(dir));
2861
return false;
2862
}
2863
std::sort(knownTargets.begin(), knownTargets.end());
2864
}
2865
}
2866
std::map<const NBEdge*, int> toLaneIndex;
2867
for (int i = 0; i < getNumLanes(); i++) {
2868
const int turnSigns = myLanes[i].turnSigns;
2869
// no turnSigns are given for bicycle lanes and sidewalks
2870
if (turnSigns != 0) {
2871
// clear existing connections
2872
for (auto it = myConnections.begin(); it != myConnections.end();) {
2873
if (it->fromLane == i) {
2874
it = myConnections.erase(it);
2875
} else {
2876
it++;
2877
}
2878
}
2879
// add new connections
2880
int allSigns = (turnSigns
2881
| turnSigns >> TURN_SIGN_SHIFT_BUS
2882
| turnSigns >> TURN_SIGN_SHIFT_TAXI
2883
| turnSigns >> TURN_SIGN_SHIFT_BICYCLE);
2884
std::vector<LinkDirection> all = decodeTurnSigns(turnSigns);
2885
std::vector<LinkDirection> bus = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BUS);
2886
std::vector<LinkDirection> taxi = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_TAXI);
2887
std::vector<LinkDirection> bike = decodeTurnSigns(turnSigns, TURN_SIGN_SHIFT_BICYCLE);
2888
//std::cout << " allSigns=" << allSigns << " turnSigns=" << turnSigns << " bus=" << bus.size() << "\n";
2889
SVCPermissions fromP = getPermissions(i);
2890
if ((fromP & SVC_PASSENGER) != 0) {
2891
// if the source permits passenger traffic, the target should too
2892
fromP = SVC_PASSENGER;
2893
}
2894
for (LinkDirection dir : decodeTurnSigns(allSigns)) {
2895
SVCPermissions perm = 0;
2896
updateTurnPermissions(perm, dir, SVCAll, all);
2897
updateTurnPermissions(perm, dir, SVC_BUS, bus);
2898
updateTurnPermissions(perm, dir, SVC_TAXI, taxi);
2899
updateTurnPermissions(perm, dir, SVC_BICYCLE, bike);
2900
if (perm == SVCAll) {
2901
perm = SVC_UNSPECIFIED;
2902
}
2903
//std::cout << " lane=" << i << " dir=" << toString(dir) << " perm=" << getVehicleClassNames(perm) << "\n";
2904
NBEdge* to = const_cast<NBEdge*>(dirMap[dir]);
2905
if (to != nullptr) {
2906
if (toLaneIndex.count(to) == 0) {
2907
// initialize to rightmost feasible lane
2908
int toLane = toLaneMap[to][0];
2909
while ((to->getPermissions(toLane) & fromP) == 0 && (toLane + 1 < to->getNumLanes())) {
2910
toLane++;
2911
/*
2912
if (toLane == to->getNumLanes()) {
2913
SOFT_ASSERT(false);
2914
#ifdef DEBUG_TURNSIGNS
2915
std::cout << " could not find passenger lane for target=" << to->getID() << "\n";
2916
#endif
2917
return false;
2918
}
2919
*/
2920
}
2921
#ifdef DEBUG_TURNSIGNS
2922
std::cout << " target=" << to->getID() << " initial toLane=" << toLane << "\n";
2923
#endif
2924
toLaneIndex[to] = toLane;
2925
}
2926
#ifdef DEBUG_TURNSIGNS
2927
//std::cout << " set fromLane=" << i << " to=" << to->getID() << " toLane=" << toLaneIndex[to] << "\n";
2928
#endif
2929
setConnection(i, to, toLaneIndex[to], Lane2LaneInfoType::VALIDATED, true,
2930
false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS,
2931
UNSPECIFIED_VISIBILITY_DISTANCE, UNSPECIFIED_SPEED, UNSPECIFIED_FRICTION,
2932
myDefaultConnectionLength, PositionVector::EMPTY,
2933
UNSPECIFIED_CONNECTION_UNCONTROLLED,
2934
perm);
2935
if (toLaneIndex[to] < to->getNumLanes() - 1
2936
&& (to->getPermissions(toLaneIndex[to] + 1) & fromP) != 0) {
2937
toLaneIndex[to]++;
2938
} else if (toLaneIndex[to] < to->getNumLanes() - 2
2939
&& (to->getPermissions(toLaneIndex[to] + 2) & fromP) != 0) {
2940
// skip forbidden lane
2941
toLaneIndex[to] += 2;
2942
}
2943
}
2944
}
2945
}
2946
}
2947
sortOutgoingConnectionsByAngle();
2948
sortOutgoingConnectionsByIndex();
2949
return true;
2950
}
2951
2952
2953
bool
2954
NBEdge::recheckLanes() {
2955
#ifdef DEBUG_CONNECTION_GUESSING
2956
if (DEBUGCOND) {
2957
std::cout << "recheckLanes (initial) edge=" << getID() << "\n";
2958
for (Connection& c : myConnections) {
2959
std::cout << " conn " << c.getDescription(this) << "\n";
2960
}
2961
for (Connection& c : myConnectionsToDelete) {
2962
std::cout << " connToDelete " << c.getDescription(this) << "\n";
2963
}
2964
}
2965
#endif
2966
// check delayed removals
2967
for (std::vector<Connection>::iterator it = myConnectionsToDelete.begin(); it != myConnectionsToDelete.end(); ++it) {
2968
removeFromConnections(it->toEdge, it->fromLane, it->toLane, false, false, true);
2969
}
2970
std::vector<int> connNumbersPerLane(myLanes.size(), 0);
2971
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
2972
if ((*i).toEdge == nullptr || (*i).fromLane < 0 || (*i).toLane < 0) {
2973
i = myConnections.erase(i);
2974
} else {
2975
if ((*i).fromLane >= 0) {
2976
++connNumbersPerLane[(*i).fromLane];
2977
}
2978
++i;
2979
}
2980
}
2981
if (myStep != EdgeBuildingStep::LANES2LANES_DONE && myStep != EdgeBuildingStep::LANES2LANES_USER) {
2982
#ifdef DEBUG_TURNSIGNS
2983
if (myLanes.back().turnSigns != 0) {
2984
std::cout << getID() << " hasTurnSigns\n";
2985
if (myTurnSignTarget != myTo->getID()) {
2986
std::cout << " tst=" << myTurnSignTarget << " to=" << myTo->getID() << "\n";
2987
}
2988
}
2989
#endif
2990
if (myLanes.back().turnSigns == 0 || myTurnSignTarget != myTo->getID() || !applyTurnSigns()) {
2991
// check #1:
2992
// If there is a lane with no connections and any neighbour lane has
2993
// more than one connections, try to move one of them.
2994
// This check is only done for edges which connections were assigned
2995
// using the standard algorithm.
2996
for (int i = 0; i < (int)myLanes.size(); i++) {
2997
if (connNumbersPerLane[i] == 0 && !isForbidden(getPermissions(i) & ~SVC_PEDESTRIAN)) {
2998
// dead-end lane found
2999
bool hasDeadEnd = true;
3000
// find lane with two connections or more to the right of the current lane
3001
for (int i2 = i - 1; hasDeadEnd && i2 >= 0; i2--) {
3002
if (getPermissions(i) != getPermissions(i2)) {
3003
break;
3004
}
3005
if (connNumbersPerLane[i2] > 1) {
3006
connNumbersPerLane[i2]--;
3007
for (int i3 = i2; i3 != i; i3++) {
3008
moveConnectionToLeft(i3);
3009
sortOutgoingConnectionsByAngle();
3010
sortOutgoingConnectionsByIndex();
3011
}
3012
hasDeadEnd = false;
3013
}
3014
}
3015
if (hasDeadEnd) {
3016
// find lane with two connections or more to the left of the current lane
3017
for (int i2 = i + 1; hasDeadEnd && i2 < getNumLanes(); i2++) {
3018
if (getPermissions(i) != getPermissions(i2)) {
3019
break;
3020
}
3021
if (connNumbersPerLane[i2] > 1) {
3022
connNumbersPerLane[i2]--;
3023
for (int i3 = i2; i3 != i; i3--) {
3024
moveConnectionToRight(i3);
3025
sortOutgoingConnectionsByAngle();
3026
sortOutgoingConnectionsByIndex();
3027
}
3028
hasDeadEnd = false;
3029
}
3030
}
3031
}
3032
if (hasDeadEnd && myTo->getOutgoingEdges().size() > 1) {
3033
int passengerLanes = 0;
3034
int passengerTargetLanes = 0;
3035
for (const Lane& lane : myLanes) {
3036
if ((lane.permissions & SVC_PASSENGER) != 0) {
3037
passengerLanes++;
3038
}
3039
}
3040
for (const NBEdge* out : myTo->getOutgoingEdges()) {
3041
if (!isTurningDirectionAt(out)) {
3042
for (const Lane& lane : out->getLanes()) {
3043
if ((lane.permissions & SVC_PASSENGER) != 0) {
3044
passengerTargetLanes++;
3045
}
3046
}
3047
}
3048
}
3049
if (passengerLanes > 0 && passengerLanes <= passengerTargetLanes) {
3050
// no need for dead-ends
3051
if (i > 0) {
3052
// check if a connection to the right has a usable target to the left of its target
3053
std::vector<Connection> rightCons = getConnectionsFromLane(i - 1);
3054
if (rightCons.size() > 0) {
3055
const Connection& rc = rightCons.back();
3056
NBEdge* to = rc.toEdge;
3057
int toLane = rc.toLane + 1;
3058
if (toLane < to->getNumLanes()
3059
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3060
&& !hasConnectionTo(to, toLane)) {
3061
#ifdef DEBUG_CONNECTION_CHECKING
3062
std::cout << " recheck1 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3063
#endif
3064
setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3065
hasDeadEnd = false;
3066
sortOutgoingConnectionsByAngle();
3067
sortOutgoingConnectionsByIndex();
3068
}
3069
if (hasDeadEnd) {
3070
// check if a connection to the right has a usable target to the right of its target
3071
toLane = rc.toLane - 1;
3072
if (toLane >= 0
3073
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(rc.toLane)) != 0
3074
&& (getPermissions(rc.fromLane) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3075
&& !hasConnectionTo(to, toLane)) {
3076
// shift the right lane connection target right and connect the dead lane to the old target
3077
getConnectionRef(rc.fromLane, to, rc.toLane).toLane = toLane;
3078
#ifdef DEBUG_CONNECTION_CHECKING
3079
std::cout << " recheck2 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << (toLane + 1) << "\n";
3080
#endif
3081
setConnection(i, to, toLane + 1, Lane2LaneInfoType::COMPUTED);
3082
hasDeadEnd = false;
3083
sortOutgoingConnectionsByAngle();
3084
sortOutgoingConnectionsByIndex();
3085
}
3086
}
3087
}
3088
}
3089
if (hasDeadEnd && i < getNumLanes() - 1) {
3090
// check if a connection to the left has a usable target to the right of its target
3091
std::vector<Connection> leftCons = getConnectionsFromLane(i + 1);
3092
if (leftCons.size() > 0) {
3093
NBEdge* to = leftCons.front().toEdge;
3094
int toLane = leftCons.front().toLane - 1;
3095
if (toLane >= 0
3096
&& (getPermissions(i) & ~SVC_PEDESTRIAN & to->getPermissions(toLane)) != 0
3097
&& !hasConnectionTo(to, toLane)) {
3098
#ifdef DEBUG_CONNECTION_CHECKING
3099
std::cout << " recheck3 setConnection " << getID() << "_" << i << "->" << to->getID() << "_" << toLane << "\n";
3100
#endif
3101
setConnection(i, to, toLane, Lane2LaneInfoType::COMPUTED);
3102
hasDeadEnd = false;
3103
sortOutgoingConnectionsByAngle();
3104
sortOutgoingConnectionsByIndex();
3105
}
3106
}
3107
}
3108
#ifdef ADDITIONAL_WARNINGS
3109
if (hasDeadEnd) {
3110
WRITE_WARNING("Found dead-end lane " + getLaneID(i));
3111
}
3112
#endif
3113
}
3114
}
3115
}
3116
}
3117
removeInvalidConnections();
3118
}
3119
}
3120
// check involuntary dead end at "real" junctions
3121
if (getPermissions() != SVC_PEDESTRIAN) {
3122
if (myConnections.empty() && myTo->getOutgoingEdges().size() > 1 && (getPermissions() & ~SVC_PEDESTRIAN) != 0) {
3123
WRITE_WARNINGF(TL("Edge '%' is not connected to outgoing edges at junction '%'."), getID(), myTo->getID());
3124
}
3125
const EdgeVector& incoming = myFrom->getIncomingEdges();
3126
if (incoming.size() > 1) {
3127
for (int i = 0; i < (int)myLanes.size(); i++) {
3128
if (getPermissions(i) != 0 && getPermissions(i) != SVC_PEDESTRIAN) {
3129
bool connected = false;
3130
for (std::vector<NBEdge*>::const_iterator in = incoming.begin(); in != incoming.end(); ++in) {
3131
if ((*in)->hasConnectionTo(this, i)) {
3132
connected = true;
3133
break;
3134
}
3135
}
3136
if (!connected) {
3137
WRITE_WARNINGF(TL("Lane '%' is not connected from any incoming edge at junction '%'."), getLaneID(i), myFrom->getID());
3138
}
3139
}
3140
}
3141
}
3142
}
3143
// avoid deadend due to change prohibitions
3144
if (getNumLanes() > 1 && myConnections.size() > 0) {
3145
for (int i = 0; i < (int)myLanes.size(); i++) {
3146
Lane& lane = myLanes[i];
3147
if ((connNumbersPerLane[i] == 0 || ((lane.accelRamp || (i > 0 && myLanes[i - 1].accelRamp && connNumbersPerLane[i - 1] > 0))
3148
&& getSuccessors(SVC_PASSENGER).size() > 1))
3149
&& getPermissions(i) != SVC_PEDESTRIAN && !isForbidden(getPermissions(i))) {
3150
const bool forbiddenLeft = lane.changeLeft != SVCAll && lane.changeLeft != SVC_IGNORING && lane.changeLeft != SVC_UNSPECIFIED;
3151
const bool forbiddenRight = lane.changeRight != SVCAll && lane.changeRight != SVC_IGNORING && lane.changeRight != SVC_UNSPECIFIED;
3152
if (forbiddenLeft && (i == 0 || forbiddenRight)) {
3153
lane.changeLeft = SVC_UNSPECIFIED;
3154
WRITE_WARNINGF(TL("Ignoring changeLeft prohibition for '%' to avoid dead-end"), getLaneID(i));
3155
} else if (forbiddenRight && (i == getNumLanes() - 1 || (i > 0 && myLanes[i - 1].accelRamp))) {
3156
lane.changeRight = SVC_UNSPECIFIED;
3157
WRITE_WARNINGF(TL("Ignoring changeRight prohibition for '%' to avoid dead-end"), getLaneID(i));
3158
}
3159
}
3160
}
3161
}
3162
#ifdef ADDITIONAL_WARNINGS
3163
// check for connections with bad access permissions
3164
for (const Connection& c : myConnections) {
3165
SVCPermissions fromP = getPermissions(c.fromLane);
3166
SVCPermissions toP = c.toEdge->getPermissions(c.toLane);
3167
if ((fromP & SVC_PASSENGER) != 0
3168
&& toP == SVC_BICYCLE) {
3169
bool hasAlternative = false;
3170
for (const Connection& c2 : myConnections) {
3171
if (c.fromLane == c2.fromLane && c.toEdge == c2.toEdge
3172
&& (c.toEdge->getPermissions(c2.toLane) & SVC_PASSENGER) != 0) {
3173
hasAlternative = true;
3174
}
3175
}
3176
if (!hasAlternative) {
3177
WRITE_WARNING("Road lane ends on bikeLane for connection " + c.getDescription(this));
3178
}
3179
}
3180
}
3181
3182
#endif
3183
#ifdef DEBUG_CONNECTION_GUESSING
3184
if (DEBUGCOND) {
3185
std::cout << "recheckLanes (final) edge=" << getID() << "\n";
3186
for (Connection& c : myConnections) {
3187
std::cout << " conn " << c.getDescription(this) << "\n";
3188
}
3189
}
3190
#endif
3191
if (myStep != EdgeBuildingStep::LANES2LANES_USER) {
3192
myStep = EdgeBuildingStep::LANES2LANES_DONE;
3193
}
3194
return true;
3195
}
3196
3197
3198
void NBEdge::recheckOpposite(const NBEdgeCont& ec, bool fixOppositeLengths) {
3199
if (getNumLanes() == 0) {
3200
return;
3201
}
3202
const int leftmostLane = getNumLanes() - 1;
3203
// check oppositeID stored in other lanes
3204
for (int i = 0; i < leftmostLane; i++) {
3205
const std::string& oppositeID = getLanes()[i].oppositeID;
3206
NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3207
if (oppositeID != "" && oppositeID != "-") {
3208
if (getLanes().back().oppositeID == "" && oppEdge != nullptr) {
3209
getLaneStruct(leftmostLane).oppositeID = oppositeID;
3210
WRITE_WARNINGF(TL("Moving opposite lane '%' from invalid lane '%' to lane index %."), oppositeID, getLaneID(i), leftmostLane);
3211
} else {
3212
WRITE_WARNINGF(TL("Removing opposite lane '%' for invalid lane '%'."), oppositeID, getLaneID(i));
3213
}
3214
getLaneStruct(i).oppositeID = "";
3215
}
3216
}
3217
const std::string& oppositeID = getLanes().back().oppositeID;
3218
if (oppositeID != "" && oppositeID != "-") {
3219
NBEdge* oppEdge = ec.retrieve(oppositeID.substr(0, oppositeID.rfind("_")));
3220
if (oppEdge == nullptr) {
3221
WRITE_WARNINGF(TL("Removing unknown opposite lane '%' for edge '%'."), oppositeID, getID());
3222
getLaneStruct(leftmostLane).oppositeID = "";
3223
} else {
3224
if (oppEdge->getFromNode() != getToNode() || oppEdge->getToNode() != getFromNode()) {
3225
WRITE_WARNINGF(TL("Opposite lane '%' does not reverse-connect the same nodes as edge '%'!"), oppositeID, getID());
3226
getLaneStruct(getNumLanes() - 1).oppositeID = "";
3227
} else {
3228
if (oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != oppositeID) {
3229
const std::string oppEdgeLeftmost = oppEdge->getLaneID(oppEdge->getNumLanes() - 1);
3230
WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppositeID, getID(), oppEdgeLeftmost);
3231
getLaneStruct(leftmostLane).oppositeID = oppEdgeLeftmost;
3232
}
3233
NBEdge::Lane& oppLane = oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1);
3234
const std::string leftmostID = getLaneID(leftmostLane);
3235
if (oppLane.oppositeID == "") {
3236
WRITE_WARNINGF(TL("Adapting missing opposite lane '%' for edge '%'."), leftmostID, oppEdge->getID());
3237
oppLane.oppositeID = leftmostID;
3238
} else if (oppLane.oppositeID != leftmostID && oppLane.oppositeID != "-") {
3239
const std::string oppOpp = oppLane.oppositeID.substr(0, oppLane.oppositeID.rfind("_"));
3240
NBEdge* oppOppEdge = ec.retrieve(oppOpp);
3241
if (oppOppEdge == nullptr) {
3242
WRITE_WARNINGF(TL("Adapting invalid opposite lane '%' for edge '%' to '%'."), oppLane.oppositeID, oppEdge->getID(), leftmostID);
3243
oppLane.oppositeID = leftmostID;
3244
} else {
3245
if (oppEdge->getFromNode() != oppOppEdge->getToNode() || oppEdge->getToNode() != oppOppEdge->getFromNode()) {
3246
WRITE_ERRORF(TL("Opposite edge '%' does not reverse-connect the same nodes as edge '%'!"), oppEdge->getID(), oppOppEdge->getID());
3247
} else {
3248
WRITE_WARNINGF(TL("Adapting inconsistent opposite lanes for edges '%', '%' and '%'."), getID(), oppEdge->getID(), oppOpp);
3249
}
3250
oppLane.oppositeID = leftmostID;
3251
NBEdge::Lane& oppOppLane = oppOppEdge->getLaneStruct(oppOppEdge->getNumLanes() - 1);
3252
if (oppOppLane.oppositeID == oppEdge->getLaneID(oppEdge->getNumLanes() - 1)) {
3253
oppOppLane.oppositeID = "";
3254
}
3255
}
3256
}
3257
if (fabs(oppEdge->getLoadedLength() - getLoadedLength()) > NUMERICAL_EPS) {
3258
if (fixOppositeLengths) {
3259
const double avgLength = 0.5 * (getFinalLength() + oppEdge->getFinalLength());
3260
WRITE_WARNINGF(TL("Averaging edge lengths for lane '%' (length %) and edge '%' (length %)."),
3261
oppositeID, oppEdge->getLoadedLength(), getID(), getLoadedLength());
3262
setLoadedLength(avgLength);
3263
oppEdge->setLoadedLength(avgLength);
3264
} else {
3265
WRITE_ERROR("Opposite lane '" + oppositeID + "' (length " + toString(oppEdge->getLoadedLength()) +
3266
") differs in length from edge '" + getID() + "' (length " +
3267
toString(getLoadedLength()) + "). Set --opposites.guess.fix-lengths to fix this.");
3268
getLaneStruct(getNumLanes() - 1).oppositeID = "";
3269
}
3270
}
3271
}
3272
}
3273
}
3274
// check for matching bidi lane shapes (at least for the simple case of 1-lane edges)
3275
const NBEdge* bidi = getBidiEdge();
3276
if (bidi != nullptr && getNumLanes() == 1 && bidi->getNumLanes() == 1 && getID() < bidi->getID()) {
3277
getLaneStruct(0).shape = bidi->getLaneStruct(0).shape.reverse();
3278
}
3279
// check for valid offset and speed
3280
const double startOffset = isBidiRail() ? getTurnDestination(true)->getEndOffset() : 0;
3281
int i = 0;
3282
for (const NBEdge::Lane& l : getLanes()) {
3283
if (startOffset + l.endOffset > getLength()) {
3284
WRITE_WARNINGF(TL("Invalid endOffset % at lane '%' with length % (startOffset %)."),
3285
toString(l.endOffset), getLaneID(i), toString(l.shape.length()), toString(startOffset));
3286
} else if (l.speed < 0.) {
3287
WRITE_WARNINGF(TL("Negative allowed speed (%) on lane '%', use --speed.minimum to prevent this."), toString(l.speed), getLaneID(i));
3288
} else if (l.speed == 0.) {
3289
WRITE_WARNINGF(TL("Lane '%' has a maximum allowed speed of 0."), getLaneID(i));
3290
}
3291
i++;
3292
}
3293
}
3294
3295
void NBEdge::removeInvalidConnections() {
3296
// check restrictions
3297
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3298
Connection& c = *i;
3299
const SVCPermissions common = getPermissions(c.fromLane) & c.toEdge->getPermissions(c.toLane);
3300
if (common == SVC_PEDESTRIAN || getPermissions(c.fromLane) == SVC_PEDESTRIAN) {
3301
// these are computed in NBNode::buildWalkingAreas
3302
#ifdef DEBUG_CONNECTION_CHECKING
3303
std::cout << " remove pedCon " << c.getDescription(this) << "\n";
3304
#endif
3305
i = myConnections.erase(i);
3306
} else if (common == 0) {
3307
// no common permissions.
3308
// try to find a suitable target lane to the right
3309
const int origToLane = c.toLane;
3310
c.toLane = -1; // ignore this connection when calling hasConnectionTo
3311
int toLane = origToLane;
3312
while (toLane > 0
3313
&& (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3314
&& !hasConnectionTo(c.toEdge, toLane)
3315
) {
3316
toLane--;
3317
}
3318
if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3319
&& !hasConnectionTo(c.toEdge, toLane)) {
3320
c.toLane = toLane;
3321
++i;
3322
} else {
3323
// try to find a suitable target lane to the left
3324
toLane = origToLane;
3325
while (toLane < (int)c.toEdge->getNumLanes() - 1
3326
&& (getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) == 0
3327
&& !hasConnectionTo(c.toEdge, toLane)
3328
) {
3329
toLane++;
3330
}
3331
if ((getPermissions(c.fromLane) & c.toEdge->getPermissions(toLane)) != 0
3332
&& !hasConnectionTo(c.toEdge, toLane)) {
3333
c.toLane = toLane;
3334
++i;
3335
} else {
3336
// no alternative target found
3337
#ifdef DEBUG_CONNECTION_CHECKING
3338
std::cout << " remove " << c.getDescription(this) << " with no alternative target\n";
3339
#endif
3340
i = myConnections.erase(i);
3341
}
3342
}
3343
} else if (isRailway(getPermissions(c.fromLane)) && isRailway(c.toEdge->getPermissions(c.toLane))
3344
&& isTurningDirectionAt(c.toEdge)) {
3345
// do not allow sharp rail turns
3346
#ifdef DEBUG_CONNECTION_CHECKING
3347
std::cout << " remove " << c.getDescription(this) << " (rail turnaround)\n";
3348
#endif
3349
i = myConnections.erase(i);
3350
} else {
3351
++i;
3352
}
3353
}
3354
}
3355
3356
void
3357
NBEdge::divideOnEdges(const EdgeVector* outgoing) {
3358
if (outgoing->size() == 0) {
3359
// we have to do this, because the turnaround may have been added before
3360
myConnections.clear();
3361
return;
3362
}
3363
3364
#ifdef DEBUG_CONNECTION_GUESSING
3365
if (DEBUGCOND) {
3366
std::cout << " divideOnEdges " << getID() << " outgoing=" << toString(*outgoing) << "\n";
3367
}
3368
#endif
3369
3370
// build connections for miv lanes
3371
std::vector<int> availableLanes;
3372
for (int i = 0; i < (int)myLanes.size(); ++i) {
3373
if ((getPermissions(i) & SVC_PASSENGER) != 0) {
3374
availableLanes.push_back(i);
3375
}
3376
}
3377
if (availableLanes.size() > 0) {
3378
divideSelectedLanesOnEdges(outgoing, availableLanes);
3379
}
3380
// build connections for miscellaneous further modes (more than bike,peds,bus and without passenger)
3381
availableLanes.clear();
3382
for (int i = 0; i < (int)myLanes.size(); ++i) {
3383
const SVCPermissions perms = getPermissions(i);
3384
if ((perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) == 0 || (perms & SVC_PASSENGER) != 0 || isForbidden(perms)) {
3385
continue;
3386
}
3387
availableLanes.push_back(i);
3388
}
3389
if (availableLanes.size() > 0) {
3390
divideSelectedLanesOnEdges(outgoing, availableLanes);
3391
}
3392
// build connections for busses from lanes that were excluded in the previous step
3393
availableLanes.clear();
3394
for (int i = 0; i < (int)myLanes.size(); ++i) {
3395
const SVCPermissions perms = getPermissions(i);
3396
if ((perms & SVC_BUS) == 0 || (perms & ~(SVC_PEDESTRIAN | SVC_BICYCLE | SVC_BUS)) != 0 || (perms & SVC_PASSENGER) != 0) {
3397
continue;
3398
}
3399
availableLanes.push_back(i);
3400
}
3401
if (availableLanes.size() > 0) {
3402
divideSelectedLanesOnEdges(outgoing, availableLanes);
3403
}
3404
// build connections for bicycles (possibly combined with pedestrians)
3405
availableLanes.clear();
3406
for (int i = 0; i < (int)myLanes.size(); ++i) {
3407
const SVCPermissions perms = getPermissions(i);
3408
if (perms != SVC_BICYCLE && perms != (SVC_BICYCLE | SVC_PEDESTRIAN)) {
3409
continue;
3410
}
3411
availableLanes.push_back(i);
3412
}
3413
if (availableLanes.size() > 0) {
3414
divideSelectedLanesOnEdges(outgoing, availableLanes);
3415
}
3416
// clean up unassigned fromLanes
3417
bool explicitTurnaround = false;
3418
SVCPermissions turnaroundPermissions = SVC_UNSPECIFIED;
3419
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end();) {
3420
if ((*i).fromLane == -1) {
3421
if ((*i).toEdge == myTurnDestination && myTurnDestination != nullptr) {
3422
explicitTurnaround = true;
3423
turnaroundPermissions = (*i).permissions;
3424
}
3425
if ((*i).permissions != SVC_UNSPECIFIED) {
3426
for (Connection& c : myConnections) {
3427
if (c.toLane == -1 && c.toEdge == (*i).toEdge) {
3428
// carry over loaded edge2edge permissions
3429
c.permissions = (*i).permissions;
3430
}
3431
}
3432
}
3433
i = myConnections.erase(i);
3434
} else {
3435
++i;
3436
}
3437
}
3438
if (explicitTurnaround) {
3439
myConnections.push_back(Connection((int)myLanes.size() - 1, myTurnDestination, myTurnDestination->getNumLanes() - 1));
3440
myConnections.back().permissions = turnaroundPermissions;
3441
}
3442
sortOutgoingConnectionsByIndex();
3443
}
3444
3445
3446
void
3447
NBEdge::divideSelectedLanesOnEdges(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3448
const std::vector<int>& priorities = prepareEdgePriorities(outgoing, availableLanes);
3449
if (priorities.empty()) {
3450
return;
3451
}
3452
#ifdef DEBUG_CONNECTION_GUESSING
3453
if (DEBUGCOND) {
3454
std::cout << "divideSelectedLanesOnEdges " << getID() << " out=" << toString(*outgoing) << " prios=" << toString(priorities) << " avail=" << toString(availableLanes) << "\n";
3455
}
3456
#endif
3457
// compute the resulting number of lanes that should be used to reach the following edge
3458
const int numOutgoing = (int)outgoing->size();
3459
std::vector<int> resultingLanesFactor;
3460
resultingLanesFactor.reserve(numOutgoing);
3461
int minResulting = std::numeric_limits<int>::max();
3462
for (int i = 0; i < numOutgoing; i++) {
3463
// res / minResulting will be the number of lanes which are meant to reach the current outgoing edge
3464
const int res = priorities[i] * (int)availableLanes.size();
3465
resultingLanesFactor.push_back(res);
3466
if (minResulting > res && res > 0) {
3467
// prevent minResulting from becoming 0
3468
minResulting = res;
3469
}
3470
}
3471
// compute the number of virtual edges
3472
// a virtual edge is used as a replacement for a real edge from now on
3473
// it shall allow to divide the existing lanes on this structure without
3474
// regarding the structure of outgoing edges
3475
int numVirtual = 0;
3476
// compute the transition from virtual to real edges
3477
EdgeVector transition;
3478
transition.reserve(numOutgoing);
3479
for (int i = 0; i < numOutgoing; i++) {
3480
// tmpNum will be the number of connections from this edge to the next edge
3481
assert(i < (int)resultingLanesFactor.size());
3482
const int tmpNum = (resultingLanesFactor[i] + minResulting - 1) / minResulting; // integer division rounding up
3483
numVirtual += tmpNum;
3484
for (int j = 0; j < tmpNum; j++) {
3485
transition.push_back((*outgoing)[i]);
3486
}
3487
}
3488
#ifdef DEBUG_CONNECTION_GUESSING
3489
if (DEBUGCOND) {
3490
std::cout << " minResulting=" << minResulting << " numVirtual=" << numVirtual << " availLanes=" << toString(availableLanes) << " resLanes=" << toString(resultingLanesFactor) << " transition=" << toString(transition) << "\n";
3491
}
3492
#endif
3493
3494
// assign lanes to edges
3495
// (conversion from virtual to real edges is done)
3496
ToEdgeConnectionsAdder adder(transition);
3497
Bresenham::compute(&adder, static_cast<int>(availableLanes.size()), numVirtual);
3498
const std::map<NBEdge*, std::vector<int> >& l2eConns = adder.getBuiltConnections();
3499
for (NBEdge* const target : *outgoing) {
3500
assert(l2eConns.find(target) != l2eConns.end());
3501
for (const int j : l2eConns.find(target)->second) {
3502
const int fromIndex = availableLanes[j];
3503
if ((getPermissions(fromIndex) & target->getPermissions()) == 0) {
3504
// exclude connection if fromLane and toEdge have no common permissions
3505
continue;
3506
}
3507
if ((getPermissions(fromIndex) & target->getPermissions()) == SVC_PEDESTRIAN) {
3508
// exclude connection if the only commonly permitted class are pedestrians
3509
// these connections are later built in NBNode::buildWalkingAreas
3510
continue;
3511
}
3512
// avoid building more connections than the edge has viable lanes (earlier
3513
// ones have precedence). This is necessary when running divideSelectedLanesOnEdges more than once.
3514
// @todo To decide which target lanes are still available we need to do a
3515
// preliminary lane-to-lane assignment in regard to permissions (rather than to ordering)
3516
const int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3517
int targetLanes = target->getNumLanes();
3518
if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3519
--targetLanes;
3520
}
3521
if (numConsToTarget >= targetLanes) {
3522
continue;
3523
}
3524
if (myLanes[fromIndex].connectionsDone) {
3525
// we already have complete information about connections from
3526
// this lane. do not add anything else
3527
#ifdef DEBUG_CONNECTION_GUESSING
3528
if (DEBUGCOND) {
3529
std::cout << " connectionsDone from " << getID() << "_" << fromIndex << ": ";
3530
for (const Connection& c : getConnectionsFromLane(fromIndex)) {
3531
std::cout << c.getDescription(this) << ", ";
3532
}
3533
std::cout << "\n";
3534
}
3535
#endif
3536
continue;
3537
}
3538
myConnections.push_back(Connection(fromIndex, target, -1));
3539
#ifdef DEBUG_CONNECTION_GUESSING
3540
if (DEBUGCOND) {
3541
std::cout << " request connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3542
}
3543
#endif
3544
}
3545
}
3546
3547
addStraightConnections(outgoing, availableLanes, priorities);
3548
}
3549
3550
3551
void
3552
NBEdge::addStraightConnections(const EdgeVector* outgoing, const std::vector<int>& availableLanes, const std::vector<int>& priorities) {
3553
// ensure sufficient straight connections for the (highest-priority) straight target
3554
const int numOutgoing = (int) outgoing->size();
3555
NBEdge* target = nullptr;
3556
NBEdge* rightOfTarget = nullptr;
3557
NBEdge* leftOfTarget = nullptr;
3558
int maxPrio = 0;
3559
for (int i = 0; i < numOutgoing; i++) {
3560
if (maxPrio < priorities[i]) {
3561
const LinkDirection dir = myTo->getDirection(this, (*outgoing)[i]);
3562
if (dir == LinkDirection::STRAIGHT) {
3563
maxPrio = priorities[i];
3564
target = (*outgoing)[i];
3565
rightOfTarget = i == 0 ? outgoing->back() : (*outgoing)[i - 1];
3566
leftOfTarget = i + 1 == numOutgoing ? outgoing->front() : (*outgoing)[i + 1];
3567
}
3568
}
3569
}
3570
if (target == nullptr) {
3571
return;
3572
}
3573
int numConsToTarget = (int)count_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(target, true));
3574
int targetLanes = (int)target->getNumLanes();
3575
if (target->getPermissions(0) == SVC_PEDESTRIAN) {
3576
--targetLanes;
3577
}
3578
const int numDesiredConsToTarget = MIN2(targetLanes, (int)availableLanes.size());
3579
#ifdef DEBUG_CONNECTION_GUESSING
3580
if (DEBUGCOND) {
3581
std::cout << " checking extra lanes for target=" << target->getID() << " cons=" << numConsToTarget << " desired=" << numDesiredConsToTarget << "\n";
3582
}
3583
#endif
3584
std::vector<int>::const_iterator it_avail = availableLanes.begin();
3585
while (numConsToTarget < numDesiredConsToTarget && it_avail != availableLanes.end()) {
3586
const int fromIndex = *it_avail;
3587
if (
3588
// not yet connected
3589
(count_if(myConnections.begin(), myConnections.end(), connections_finder(fromIndex, target, -1)) == 0)
3590
// matching permissions
3591
&& ((getPermissions(fromIndex) & target->getPermissions()) != 0)
3592
// more than pedestrians
3593
&& ((getPermissions(fromIndex) & target->getPermissions()) != SVC_PEDESTRIAN)
3594
// lane not yet fully defined
3595
&& !myLanes[fromIndex].connectionsDone
3596
) {
3597
#ifdef DEBUG_CONNECTION_GUESSING
3598
if (DEBUGCOND) {
3599
std::cout << " candidate from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3600
}
3601
#endif
3602
// prevent same-edge conflicts
3603
if (
3604
// no outgoing connections to the right from further left
3605
((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3606
// no outgoing connections to the left from further right
3607
&& (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)) {
3608
#ifdef DEBUG_CONNECTION_GUESSING
3609
if (DEBUGCOND) {
3610
std::cout << " request additional connection from " << getID() << "_" << fromIndex << " to " << target->getID() << "\n";
3611
}
3612
#endif
3613
myConnections.push_back(Connection(fromIndex, target, -1));
3614
numConsToTarget++;
3615
} else {
3616
#ifdef DEBUG_CONNECTION_GUESSING
3617
if (DEBUGCOND) std::cout
3618
<< " fail check1="
3619
<< ((it_avail + 1) == availableLanes.end() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, rightOfTarget, false)) == 0)
3620
<< " check2=" << (it_avail == availableLanes.begin() || count_if(myConnections.begin(), myConnections.end(), connections_conflict_finder(fromIndex, leftOfTarget, true)) == 0)
3621
<< " rightOfTarget=" << rightOfTarget->getID()
3622
<< " leftOfTarget=" << leftOfTarget->getID()
3623
<< "\n";
3624
#endif
3625
3626
}
3627
}
3628
++it_avail;
3629
}
3630
}
3631
3632
3633
const std::vector<int>
3634
NBEdge::prepareEdgePriorities(const EdgeVector* outgoing, const std::vector<int>& availableLanes) {
3635
std::vector<int> priorities;
3636
MainDirections mainDirections(*outgoing, this, myTo, availableLanes);
3637
const int dist = mainDirections.getStraightest();
3638
if (dist == -1) {
3639
return priorities;
3640
}
3641
// copy the priorities first
3642
priorities.reserve(outgoing->size());
3643
for (const NBEdge* const out : *outgoing) {
3644
int prio = NBNode::isTrafficLight(myTo->getType()) ? 0 : out->getJunctionPriority(myTo);
3645
assert((prio + 1) * 2 > 0);
3646
prio = (prio + 1) * 2;
3647
priorities.push_back(prio);
3648
}
3649
// when the right turning direction has not a higher priority, divide
3650
// the importance by 2 due to the possibility to leave the junction
3651
// faster from this lane
3652
#ifdef DEBUG_CONNECTION_GUESSING
3653
if (DEBUGCOND) std::cout << " prepareEdgePriorities " << getID()
3654
<< " outgoing=" << toString(*outgoing)
3655
<< " priorities1=" << toString(priorities)
3656
<< " dist=" << dist
3657
<< "\n";
3658
#endif
3659
if (dist != 0 && !mainDirections.includes(MainDirections::Direction::RIGHTMOST)) {
3660
assert(priorities.size() > 0);
3661
priorities[0] /= 2;
3662
#ifdef DEBUG_CONNECTION_GUESSING
3663
if (DEBUGCOND) {
3664
std::cout << " priorities2=" << toString(priorities) << "\n";
3665
}
3666
#endif
3667
}
3668
// HEURISTIC:
3669
// when no higher priority exists, let the forward direction be
3670
// the main direction
3671
if (mainDirections.empty()) {
3672
assert(dist < (int)priorities.size());
3673
priorities[dist] *= 2;
3674
#ifdef DEBUG_CONNECTION_GUESSING
3675
if (DEBUGCOND) {
3676
std::cout << " priorities3=" << toString(priorities) << "\n";
3677
}
3678
#endif
3679
}
3680
if (NBNode::isTrafficLight(myTo->getType())) {
3681
priorities[dist] += 1;
3682
} else {
3683
// try to ensure separation of left turns
3684
if (mainDirections.includes(MainDirections::Direction::RIGHTMOST) && mainDirections.includes(MainDirections::Direction::LEFTMOST)) {
3685
priorities[0] /= 4;
3686
priorities[(int)priorities.size() - 1] /= 2;
3687
#ifdef DEBUG_CONNECTION_GUESSING
3688
if (DEBUGCOND) {
3689
std::cout << " priorities6=" << toString(priorities) << "\n";
3690
}
3691
#endif
3692
} else if (mainDirections.includes(MainDirections::Direction::RIGHTMOST)
3693
&& outgoing->size() > 2
3694
&& availableLanes.size() == 2
3695
&& (*outgoing)[dist]->getPriority() == (*outgoing)[0]->getPriority()) {
3696
priorities[0] /= 4;
3697
priorities.back() /= 2;
3698
#ifdef DEBUG_CONNECTION_GUESSING
3699
if (DEBUGCOND) {
3700
std::cout << " priorities7=" << toString(priorities) << "\n";
3701
}
3702
#endif
3703
}
3704
}
3705
if (mainDirections.includes(MainDirections::Direction::FORWARD)) {
3706
if (myLanes.size() > 2) {
3707
priorities[dist] *= 2;
3708
#ifdef DEBUG_CONNECTION_GUESSING
3709
if (DEBUGCOND) {
3710
std::cout << " priorities4=" << toString(priorities) << "\n";
3711
}
3712
#endif
3713
} else {
3714
priorities[dist] *= 3;
3715
#ifdef DEBUG_CONNECTION_GUESSING
3716
if (DEBUGCOND) {
3717
std::cout << " priorities5=" << toString(priorities) << "\n";
3718
}
3719
#endif
3720
}
3721
}
3722
return priorities;
3723
}
3724
3725
3726
void
3727
NBEdge::appendTurnaround(bool noTLSControlled, bool noFringe, bool onlyDeadends, bool onlyTurnlane, bool noGeometryLike, bool checkPermissions) {
3728
// do nothing if no turnaround is known
3729
if (myTurnDestination == nullptr || myTo->getType() == SumoXMLNodeType::RAIL_CROSSING) {
3730
return;
3731
}
3732
// do nothing if the destination node is controlled by a tls and no turnarounds
3733
// shall be appended for such junctions
3734
if (noTLSControlled && myTo->isTLControlled()) {
3735
return;
3736
}
3737
if (noFringe && myTo->getFringeType() == FringeType::OUTER) {
3738
return;
3739
}
3740
bool isDeadEnd = true;
3741
for (const Connection& c : myConnections) {
3742
if ((c.toEdge->getPermissions(c.toLane)
3743
& getPermissions(c.fromLane)
3744
& SVC_PASSENGER) != 0
3745
|| (c.toEdge->getPermissions() & getPermissions()) == getPermissions()) {
3746
isDeadEnd = false;
3747
break;
3748
}
3749
}
3750
if (onlyDeadends && !isDeadEnd) {
3751
return;
3752
}
3753
const int fromLane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3754
if (onlyTurnlane) {
3755
for (const Connection& c : getConnectionsFromLane(fromLane)) {
3756
LinkDirection dir = myTo->getDirection(this, c.toEdge);
3757
if (dir != LinkDirection::LEFT && dir != LinkDirection::PARTLEFT) {
3758
return;
3759
}
3760
}
3761
}
3762
const int toLane = myTurnDestination->getFirstAllowedLaneIndex(NBNode::BACKWARD);
3763
if (checkPermissions) {
3764
if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == 0) {
3765
// exclude connection if fromLane and toEdge have no common permissions
3766
return;
3767
}
3768
if ((getPermissions(fromLane) & myTurnDestination->getPermissions(toLane)) == SVC_PEDESTRIAN) {
3769
// exclude connection if the only commonly permitted class are pedestrians
3770
// these connections are later built in NBNode::buildWalkingAreas
3771
return;
3772
}
3773
}
3774
// avoid railway turn-arounds
3775
if (isRailway(getPermissions() & myTurnDestination->getPermissions())
3776
&& fabs(NBHelpers::normRelAngle(getAngleAtNode(myTo), myTurnDestination->getAngleAtNode(myTo))) > 90) {
3777
// except at dead-ends on bidi-edges where they model a reversal in train direction
3778
// @todo #4382: once the network fringe is tagged, it also should not receive turn-arounds)
3779
if (isBidiRail() && isRailDeadEnd()) {
3780
// add a slow connection because direction-reversal implies stopping
3781
setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED, false, false, KEEPCLEAR_UNSPECIFIED, UNSPECIFIED_CONTPOS, UNSPECIFIED_VISIBILITY_DISTANCE, SUMO_const_haltingSpeed);
3782
return;
3783
} else {
3784
return;
3785
}
3786
};
3787
if (noGeometryLike && !isDeadEnd) {
3788
// ignore paths and service entrances if this edge is for passenger traffic
3789
if (myTo->geometryLike() || ((getPermissions() & SVC_PASSENGER) != 0
3790
&& !onlyTurnlane
3791
&& myTo->geometryLike(
3792
NBEdge::filterByPermissions(myTo->getIncomingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY)),
3793
NBEdge::filterByPermissions(myTo->getOutgoingEdges(), ~(SVC_BICYCLE | SVC_PEDESTRIAN | SVC_DELIVERY))))) {
3794
// make sure the turnDestination has other incoming edges
3795
EdgeVector turnIncoming = myTurnDestination->getIncomingEdges();
3796
if (turnIncoming.size() > 1) {
3797
// this edge is always part of incoming
3798
return;
3799
}
3800
}
3801
}
3802
setConnection(fromLane, myTurnDestination, toLane, Lane2LaneInfoType::VALIDATED);
3803
}
3804
3805
3806
bool
3807
NBEdge::isTurningDirectionAt(const NBEdge* const edge) const {
3808
// maybe it was already set as the turning direction
3809
if (edge == myTurnDestination) {
3810
return true;
3811
} else if (myTurnDestination != nullptr) {
3812
// otherwise - it's not if a turning direction exists
3813
return false;
3814
}
3815
return edge == myPossibleTurnDestination;
3816
}
3817
3818
3819
NBNode*
3820
NBEdge::tryGetNodeAtPosition(double pos, double tolerance) const {
3821
// return the from-node when the position is at the begin of the edge
3822
if (pos < tolerance) {
3823
return myFrom;
3824
}
3825
// return the to-node when the position is at the end of the edge
3826
if (pos > myLength - tolerance) {
3827
return myTo;
3828
}
3829
return nullptr;
3830
}
3831
3832
3833
void
3834
NBEdge::moveOutgoingConnectionsFrom(NBEdge* e, int laneOff) {
3835
int lanes = e->getNumLanes();
3836
for (int i = 0; i < lanes; i++) {
3837
for (const NBEdge::Connection& el : e->getConnectionsFromLane(i)) {
3838
assert(el.tlID == "");
3839
addLane2LaneConnection(i + laneOff, el.toEdge, el.toLane, Lane2LaneInfoType::COMPUTED);
3840
}
3841
}
3842
}
3843
3844
3845
bool
3846
NBEdge::lanesWereAssigned() const {
3847
return myStep == EdgeBuildingStep::LANES2LANES_DONE || myStep == EdgeBuildingStep::LANES2LANES_USER;
3848
}
3849
3850
3851
double
3852
NBEdge::getMaxLaneOffset() {
3853
return SUMO_const_laneWidth * (double)myLanes.size();
3854
}
3855
3856
3857
bool
3858
NBEdge::mayBeTLSControlled(int fromLane, NBEdge* toEdge, int toLane) const {
3859
for (const Connection& c : myConnections) {
3860
if (c.fromLane == fromLane && c.toEdge == toEdge && c.toLane == toLane && c.uncontrolled) {
3861
return false;
3862
}
3863
}
3864
return true;
3865
}
3866
3867
3868
bool
3869
NBEdge::setControllingTLInformation(const NBConnection& c, const std::string& tlID) {
3870
const int fromLane = c.getFromLane();
3871
NBEdge* toEdge = c.getTo();
3872
const int toLane = c.getToLane();
3873
const int tlIndex = c.getTLIndex();
3874
const int tlIndex2 = c.getTLIndex2();
3875
// check whether the connection was not set as not to be controled previously
3876
if (!mayBeTLSControlled(fromLane, toEdge, toLane)) {
3877
return false;
3878
}
3879
3880
assert(fromLane < 0 || fromLane < (int) myLanes.size());
3881
// try to use information about the connections if given
3882
if (fromLane >= 0 && toLane >= 0) {
3883
// find the specified connection
3884
std::vector<Connection>::iterator i =
3885
find_if(myConnections.begin(), myConnections.end(), connections_finder(fromLane, toEdge, toLane));
3886
// ok, we have to test this as on the removal of self-loop edges some connections
3887
// will be reassigned
3888
if (i != myConnections.end()) {
3889
// get the connection
3890
Connection& connection = *i;
3891
// set the information about the tl
3892
connection.tlID = tlID;
3893
connection.tlLinkIndex = tlIndex;
3894
connection.tlLinkIndex2 = tlIndex2;
3895
return true;
3896
}
3897
}
3898
// if the original connection was not found, set the information for all
3899
// connections
3900
int no = 0;
3901
bool hadError = false;
3902
for (std::vector<Connection>::iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
3903
if ((*i).toEdge != toEdge) {
3904
continue;
3905
}
3906
if (fromLane >= 0 && fromLane != (*i).fromLane) {
3907
continue;
3908
}
3909
if (toLane >= 0 && toLane != (*i).toLane) {
3910
continue;
3911
}
3912
if ((*i).tlID == "") {
3913
(*i).tlID = tlID;
3914
(*i).tlLinkIndex = tlIndex;
3915
(*i).tlLinkIndex2 = tlIndex2;
3916
no++;
3917
} else {
3918
if ((*i).tlID != tlID && (*i).tlLinkIndex == tlIndex) {
3919
WRITE_WARNINGF(TL("The lane '%' on edge '%' already had a traffic light signal."), i->fromLane, getID());
3920
hadError = true;
3921
}
3922
}
3923
}
3924
if (hadError && no == 0) {
3925
WRITE_WARNINGF(TL("Could not set any signal of the tlLogic '%' (unknown group)."), tlID);
3926
}
3927
return true;
3928
}
3929
3930
3931
void
3932
NBEdge::clearControllingTLInformation() {
3933
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); it++) {
3934
it->tlID = "";
3935
}
3936
}
3937
3938
3939
PositionVector
3940
NBEdge::getCWBoundaryLine(const NBNode& n) const {
3941
PositionVector ret;
3942
int lane;
3943
if (myFrom == (&n)) {
3944
// outgoing
3945
lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3946
ret = myLanes[lane].shape;
3947
} else {
3948
// incoming
3949
lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3950
ret = myLanes[lane].shape.reverse();
3951
}
3952
ret.move2side(getLaneWidth(lane) / 2.);
3953
return ret;
3954
}
3955
3956
3957
PositionVector
3958
NBEdge::getCCWBoundaryLine(const NBNode& n) const {
3959
PositionVector ret;
3960
int lane;
3961
if (myFrom == (&n)) {
3962
// outgoing
3963
lane = getFirstAllowedLaneIndex(NBNode::BACKWARD);
3964
ret = myLanes[lane].shape;
3965
} else {
3966
// incoming
3967
lane = getFirstAllowedLaneIndex(NBNode::FORWARD);
3968
ret = myLanes[lane].shape.reverse();
3969
}
3970
ret.move2side(-getLaneWidth(lane) / 2.);
3971
return ret;
3972
}
3973
3974
3975
bool
3976
NBEdge::expandableBy(NBEdge* possContinuation, std::string& reason) const {
3977
// ok, the number of lanes must match
3978
if (myLanes.size() != possContinuation->myLanes.size()) {
3979
reason = "laneNumber";
3980
return false;
3981
}
3982
// do not create self loops
3983
if (myFrom == possContinuation->myTo) {
3984
reason = "loop";
3985
return false;
3986
}
3987
// conserve bidi-rails
3988
if (isBidiRail() != possContinuation->isBidiRail()) {
3989
reason = "bidi-rail";
3990
return false;
3991
}
3992
// also, check whether the connections - if any exit do allow to join
3993
// both edges
3994
// This edge must have a one-to-one connection to the following lanes
3995
switch (myStep) {
3996
case EdgeBuildingStep::INIT_REJECT_CONNECTIONS:
3997
break;
3998
case EdgeBuildingStep::INIT:
3999
break;
4000
case EdgeBuildingStep::EDGE2EDGES: {
4001
// the following edge must be connected
4002
const EdgeVector& conn = getConnectedEdges();
4003
if (find(conn.begin(), conn.end(), possContinuation) == conn.end()) {
4004
reason = "disconnected";
4005
return false;
4006
}
4007
}
4008
break;
4009
case EdgeBuildingStep::LANES2EDGES:
4010
case EdgeBuildingStep::LANES2LANES_RECHECK:
4011
case EdgeBuildingStep::LANES2LANES_DONE:
4012
case EdgeBuildingStep::LANES2LANES_USER: {
4013
// the possible continuation must be connected
4014
if (find_if(myConnections.begin(), myConnections.end(), connections_toedge_finder(possContinuation)) == myConnections.end()) {
4015
reason = "disconnected";
4016
return false;
4017
}
4018
// all lanes must go to the possible continuation
4019
std::vector<int> conns = getConnectionLanes(possContinuation);
4020
const int offset = MAX2(0, getFirstNonPedestrianLaneIndex(NBNode::FORWARD, true));
4021
if (conns.size() < myLanes.size() - offset) {
4022
reason = "some lanes disconnected";
4023
return false;
4024
}
4025
}
4026
break;
4027
default:
4028
break;
4029
}
4030
const double minLength = OptionsCont::getOptions().getFloat("geometry.remove.min-length");
4031
if (minLength > 0 && (possContinuation->getLoadedLength() < minLength || getLoadedLength() < minLength)) {
4032
return true;
4033
}
4034
const double maxJunctionSize = OptionsCont::getOptions().getFloat("geometry.remove.max-junction-size");
4035
if (maxJunctionSize >= 0) {
4036
const double junctionSize = myGeom.back().distanceTo2D(possContinuation->myGeom.front());
4037
if (junctionSize > maxJunctionSize + POSITION_EPS) {
4038
reason = "junction size (" + toString(junctionSize) + ") > max-junction-size (" + toString(maxJunctionSize) + ")";
4039
return false;
4040
}
4041
}
4042
// the priority, too (?)
4043
if (getPriority() != possContinuation->getPriority()) {
4044
reason = "priority";
4045
return false;
4046
}
4047
// the speed allowed
4048
if (mySpeed != possContinuation->mySpeed) {
4049
reason = "speed";
4050
return false;
4051
}
4052
// the routingType
4053
if (myRoutingType != possContinuation->myRoutingType) {
4054
reason = "routingType";
4055
return false;
4056
}
4057
// spreadtype should match or it will look ugly
4058
if (myLaneSpreadFunction != possContinuation->myLaneSpreadFunction) {
4059
reason = "spreadType";
4060
return false;
4061
}
4062
// matching lanes must have identical properties
4063
for (int i = 0; i < (int)myLanes.size(); i++) {
4064
if (myLanes[i].speed != possContinuation->myLanes[i].speed) {
4065
reason = "lane " + toString(i) + " speed";
4066
return false;
4067
} else if (myLanes[i].permissions != possContinuation->myLanes[i].permissions) {
4068
reason = "lane " + toString(i) + " permissions";
4069
return false;
4070
} else if (myLanes[i].changeLeft != possContinuation->myLanes[i].changeLeft || myLanes[i].changeRight != possContinuation->myLanes[i].changeRight) {
4071
reason = "lane " + toString(i) + " change restrictions";
4072
return false;
4073
} else if (myLanes[i].width != possContinuation->myLanes[i].width &&
4074
fabs(myLanes[i].width - possContinuation->myLanes[i].width) > OptionsCont::getOptions().getFloat("geometry.remove.width-tolerance")) {
4075
reason = "lane " + toString(i) + " width";
4076
return false;
4077
}
4078
}
4079
// if given identically osm names
4080
if (!OptionsCont::getOptions().isDefault("output.street-names") && myStreetName != possContinuation->getStreetName()
4081
&& ((myStreetName != "" && possContinuation->getStreetName() != "")
4082
// only permit merging a short unnamed road with a longer named road
4083
|| (myStreetName != "" && myLength <= possContinuation->getLength())
4084
|| (myStreetName == "" && myLength >= possContinuation->getLength()))) {
4085
return false;
4086
}
4087
4088
return true;
4089
}
4090
4091
4092
void
4093
NBEdge::append(NBEdge* e) {
4094
// append geometry
4095
myGeom.append(e->myGeom);
4096
for (int i = 0; i < (int)myLanes.size(); i++) {
4097
myLanes[i].customShape.append(e->myLanes[i].customShape);
4098
if (myLanes[i].hasParameter(SUMO_PARAM_ORIGID) || e->myLanes[i].hasParameter(SUMO_PARAM_ORIGID)
4099
|| OptionsCont::getOptions().getBool("output.original-names")) {
4100
const std::string origID = myLanes[i].getParameter(SUMO_PARAM_ORIGID, getID());
4101
const std::string origID2 = e->myLanes[i].getParameter(SUMO_PARAM_ORIGID, e->getID());
4102
if (origID != origID2) {
4103
myLanes[i].setParameter(SUMO_PARAM_ORIGID, origID + " " + origID2);
4104
}
4105
}
4106
myLanes[i].connectionsDone = e->myLanes[i].connectionsDone;
4107
myLanes[i].turnSigns = e->myLanes[i].turnSigns;
4108
}
4109
if (e->getLength() > myLength) {
4110
// possibly some lane attributes differ (when using option geometry.remove.min-length)
4111
// make sure to use the attributes from the longer edge
4112
for (int i = 0; i < (int)myLanes.size(); i++) {
4113
myLanes[i].width = e->myLanes[i].width;
4114
}
4115
// defined name prevails over undefined name of shorter road
4116
if (myStreetName == "") {
4117
myStreetName = e->myStreetName;
4118
}
4119
}
4120
// recompute length
4121
myLength += e->myLength;
4122
if (myLoadedLength > 0 || e->myLoadedLength > 0) {
4123
myLoadedLength = getFinalLength() + e->getFinalLength();
4124
}
4125
// copy the connections and the building step if given
4126
myStep = e->myStep;
4127
myConnections = e->myConnections;
4128
myTurnDestination = e->myTurnDestination;
4129
myPossibleTurnDestination = e->myPossibleTurnDestination;
4130
myConnectionsToDelete = e->myConnectionsToDelete;
4131
updateRemovedNodes(e->getParameter(SUMO_PARAM_REMOVED_NODES));
4132
// set the node
4133
myTo = e->myTo;
4134
myTurnSignTarget = e->myTurnSignTarget;
4135
myToBorder = e->myToBorder;
4136
mergeParameters(e->getParametersMap());
4137
if (e->mySignalPosition != Position::INVALID) {
4138
mySignalPosition = e->mySignalPosition;
4139
}
4140
computeAngle(); // myEndAngle may be different now
4141
}
4142
4143
4144
void
4145
NBEdge::updateRemovedNodes(const std::string& removed) {
4146
std::string result = getParameter(SUMO_PARAM_REMOVED_NODES);
4147
if (!result.empty() && !removed.empty()) {
4148
result += " ";
4149
}
4150
result += removed;
4151
if (!result.empty()) {
4152
setParameter(SUMO_PARAM_REMOVED_NODES, result);
4153
}
4154
}
4155
4156
4157
bool
4158
NBEdge::hasSignalisedConnectionTo(const NBEdge* const e) const {
4159
for (std::vector<Connection>::const_iterator i = myConnections.begin(); i != myConnections.end(); ++i) {
4160
if ((*i).toEdge == e && (*i).tlID != "") {
4161
return true;
4162
}
4163
}
4164
return false;
4165
}
4166
4167
4168
NBEdge*
4169
NBEdge::getTurnDestination(bool possibleDestination) const {
4170
if (myTurnDestination == nullptr && possibleDestination) {
4171
return myPossibleTurnDestination;
4172
}
4173
return myTurnDestination;
4174
}
4175
4176
4177
std::string
4178
NBEdge::getLaneID(int lane) const {
4179
return myID + "_" + toString(lane);
4180
}
4181
4182
4183
bool
4184
NBEdge::isNearEnough2BeJoined2(NBEdge* e, double threshold) const {
4185
std::vector<double> distances = myGeom.distances(e->getGeometry());
4186
assert(distances.size() > 0);
4187
return VectorHelper<double>::maxValue(distances) < threshold;
4188
}
4189
4190
4191
void
4192
NBEdge::addLane(int index, bool recomputeShape, bool recomputeConnections, bool shiftIndices) {
4193
assert(index <= (int)myLanes.size());
4194
myLanes.insert(myLanes.begin() + index, Lane(this, ""));
4195
// copy attributes
4196
if (myLanes.size() > 1) {
4197
int templateIndex = index > 0 ? index - 1 : index + 1;
4198
myLanes[index].speed = myLanes[templateIndex].speed;
4199
myLanes[index].friction = myLanes[templateIndex].friction;
4200
myLanes[index].permissions = myLanes[templateIndex].permissions;
4201
myLanes[index].preferred = myLanes[templateIndex].preferred;
4202
myLanes[index].endOffset = myLanes[templateIndex].endOffset;
4203
myLanes[index].width = myLanes[templateIndex].width;
4204
myLanes[index].updateParameters(myLanes[templateIndex].getParametersMap());
4205
}
4206
const EdgeVector& incs = myFrom->getIncomingEdges();
4207
if (recomputeShape) {
4208
computeLaneShapes();
4209
}
4210
if (recomputeConnections) {
4211
for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4212
(*i)->invalidateConnections(true);
4213
}
4214
invalidateConnections(true);
4215
} else if (shiftIndices) {
4216
// shift outgoing connections above the added lane to the left
4217
for (Connection& c : myConnections) {
4218
if (c.fromLane >= index) {
4219
c.fromLane += 1;
4220
}
4221
}
4222
// shift incoming connections above the added lane to the left
4223
for (NBEdge* inc : myFrom->getIncomingEdges()) {
4224
for (Connection& c : inc->myConnections) {
4225
if (c.toEdge == this && c.toLane >= index) {
4226
c.toLane += 1;
4227
}
4228
}
4229
}
4230
myFrom->shiftTLConnectionLaneIndex(this, +1, index - 1);
4231
myTo->shiftTLConnectionLaneIndex(this, +1, index - 1);
4232
}
4233
}
4234
4235
void
4236
NBEdge::incLaneNo(int by) {
4237
int newLaneNo = (int)myLanes.size() + by;
4238
while ((int)myLanes.size() < newLaneNo) {
4239
// recompute shapes on last addition
4240
const bool recompute = ((int)myLanes.size() == newLaneNo - 1) && myStep < EdgeBuildingStep::LANES2LANES_USER;
4241
addLane((int)myLanes.size(), recompute, recompute, false);
4242
}
4243
}
4244
4245
4246
void
4247
NBEdge::deleteLane(int index, bool recompute, bool shiftIndices) {
4248
assert(index < (int)myLanes.size());
4249
myLanes.erase(myLanes.begin() + index);
4250
if (recompute) {
4251
computeLaneShapes();
4252
const EdgeVector& incs = myFrom->getIncomingEdges();
4253
for (EdgeVector::const_iterator i = incs.begin(); i != incs.end(); ++i) {
4254
(*i)->invalidateConnections(true);
4255
}
4256
invalidateConnections(true);
4257
} else if (shiftIndices) {
4258
removeFromConnections(nullptr, index, -1, false, true);
4259
for (NBEdge* inc : myFrom->getIncomingEdges()) {
4260
inc->removeFromConnections(this, -1, index, false, true);
4261
}
4262
}
4263
}
4264
4265
4266
void
4267
NBEdge::decLaneNo(int by) {
4268
int newLaneNo = (int) myLanes.size() - by;
4269
assert(newLaneNo > 0);
4270
while ((int)myLanes.size() > newLaneNo) {
4271
// recompute shapes on last removal
4272
const bool recompute = (int)myLanes.size() == newLaneNo + 1 && myStep < EdgeBuildingStep::LANES2LANES_USER;
4273
deleteLane((int)myLanes.size() - 1, recompute, false);
4274
}
4275
}
4276
4277
4278
void
4279
NBEdge::markAsInLane2LaneState() {
4280
assert(myTo->getOutgoingEdges().size() == 0);
4281
myStep = EdgeBuildingStep::LANES2LANES_DONE;
4282
}
4283
4284
4285
void
4286
NBEdge::allowVehicleClass(int lane, SUMOVehicleClass vclass) {
4287
if (lane < 0) { // all lanes are meant...
4288
for (int i = 0; i < (int)myLanes.size(); i++) {
4289
allowVehicleClass(i, vclass);
4290
}
4291
} else {
4292
assert(lane < (int)myLanes.size());
4293
myLanes[lane].permissions |= vclass;
4294
}
4295
}
4296
4297
4298
void
4299
NBEdge::disallowVehicleClass(int lane, SUMOVehicleClass vclass) {
4300
if (lane < 0) { // all lanes are meant...
4301
for (int i = 0; i < (int)myLanes.size(); i++) {
4302
disallowVehicleClass((int) i, vclass);
4303
}
4304
} else {
4305
assert(lane < (int)myLanes.size());
4306
myLanes[lane].permissions &= ~vclass;
4307
}
4308
}
4309
4310
4311
void
4312
NBEdge::preferVehicleClass(int lane, SVCPermissions vclasses) {
4313
if (lane < 0) { // all lanes are meant...
4314
for (int i = 0; i < (int)myLanes.size(); i++) {
4315
preferVehicleClass(i, vclasses);
4316
}
4317
} else {
4318
assert(lane < (int)myLanes.size());
4319
myLanes[lane].permissions |= vclasses;
4320
myLanes[lane].preferred |= vclasses;
4321
}
4322
}
4323
4324
4325
void
4326
NBEdge::setLaneWidth(int lane, double width) {
4327
if (lane < 0) {
4328
// all lanes are meant...
4329
myLaneWidth = width;
4330
for (int i = 0; i < (int)myLanes.size(); i++) {
4331
// ... do it for each lane
4332
setLaneWidth(i, width);
4333
}
4334
return;
4335
}
4336
assert(lane < (int)myLanes.size());
4337
myLanes[lane].width = width;
4338
}
4339
4340
void
4341
NBEdge::setLaneType(int lane, const std::string& type) {
4342
if (lane < 0) {
4343
for (int i = 0; i < (int)myLanes.size(); i++) {
4344
// ... do it for each lane
4345
setLaneType(i, type);
4346
}
4347
return;
4348
}
4349
assert(lane < (int)myLanes.size());
4350
myLanes[lane].type = type;
4351
}
4352
4353
4354
double
4355
NBEdge::getLaneWidth(int lane) const {
4356
return myLanes[lane].width != UNSPECIFIED_WIDTH
4357
? myLanes[lane].width
4358
: getLaneWidth() != UNSPECIFIED_WIDTH ? getLaneWidth() : SUMO_const_laneWidth;
4359
}
4360
4361
double
4362
NBEdge::getInternalLaneWidth(
4363
const NBNode& node,
4364
const NBEdge::Connection& connection,
4365
const NBEdge::Lane& successor,
4366
bool isVia) const {
4367
4368
if (!isVia && node.isConstantWidthTransition() && getNumLanes() > connection.toEdge->getNumLanes()) {
4369
return getLaneWidth(connection.fromLane);
4370
}
4371
4372
return (isBikepath(getPermissions(connection.fromLane)) && (
4373
getLaneWidth(connection.fromLane) < successor.width || successor.width == UNSPECIFIED_WIDTH)) ?
4374
myLanes[connection.fromLane].width : successor.width; // getLaneWidth(connection.fromLane) never returns -1 (UNSPECIFIED_WIDTH)
4375
}
4376
4377
double
4378
NBEdge::getTotalWidth() const {
4379
double result = 0;
4380
for (int i = 0; i < (int)myLanes.size(); i++) {
4381
result += getLaneWidth(i);
4382
}
4383
return result;
4384
}
4385
4386
double
4387
NBEdge::getEndOffset(int lane) const {
4388
return myLanes[lane].endOffset != UNSPECIFIED_OFFSET ? myLanes[lane].endOffset : getEndOffset();
4389
}
4390
4391
4392
const StopOffset&
4393
NBEdge::getEdgeStopOffset() const {
4394
return myEdgeStopOffset;
4395
}
4396
4397
4398
const StopOffset&
4399
NBEdge::getLaneStopOffset(int lane) const {
4400
if (lane == -1) {
4401
return myEdgeStopOffset;
4402
} else {
4403
return myLanes[lane].laneStopOffset;
4404
}
4405
}
4406
4407
4408
void
4409
NBEdge::setEndOffset(int lane, double offset) {
4410
if (lane < 0) {
4411
// all lanes are meant...
4412
myEndOffset = offset;
4413
for (int i = 0; i < (int)myLanes.size(); i++) {
4414
// ... do it for each lane
4415
setEndOffset(i, offset);
4416
}
4417
return;
4418
}
4419
assert(lane < (int)myLanes.size());
4420
myLanes[lane].endOffset = offset;
4421
}
4422
4423
4424
bool
4425
NBEdge::setEdgeStopOffset(int lane, const StopOffset& offset, bool overwrite) {
4426
if (lane < 0) {
4427
if (!overwrite && myEdgeStopOffset.isDefined()) {
4428
return false;
4429
}
4430
// all lanes are meant...
4431
if (offset.getOffset() < 0) {
4432
// Edge length unknown at parsing time, thus check here.
4433
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for edge '%' (negative offset)."), getID());
4434
return false;
4435
} else {
4436
myEdgeStopOffset = offset;
4437
}
4438
} else if (lane < (int)myLanes.size()) {
4439
if (!myLanes[lane].laneStopOffset.isDefined() || overwrite) {
4440
if (offset.getOffset() < 0) {
4441
// Edge length unknown at parsing time, thus check here.
4442
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (negative offset)."), getLaneID(lane));
4443
} else {
4444
myLanes[lane].laneStopOffset = offset;
4445
}
4446
}
4447
} else {
4448
WRITE_WARNINGF(TL("Ignoring invalid stopOffset for lane '%' (invalid lane index)."), toString(lane));
4449
}
4450
return true;
4451
}
4452
4453
4454
void
4455
NBEdge::setSpeed(int lane, double speed) {
4456
if (lane < 0) {
4457
// all lanes are meant...
4458
mySpeed = speed;
4459
for (int i = 0; i < (int)myLanes.size(); i++) {
4460
// ... do it for each lane
4461
setSpeed(i, speed);
4462
}
4463
return;
4464
}
4465
assert(lane < (int)myLanes.size());
4466
myLanes[lane].speed = speed;
4467
}
4468
4469
4470
void
4471
NBEdge::setFriction(int lane, double friction) {
4472
if (lane < 0) {
4473
// all lanes are meant...
4474
myFriction = friction;
4475
for (int i = 0; i < (int)myLanes.size(); i++) {
4476
// ... do it for each lane
4477
setFriction(i, friction);
4478
}
4479
return;
4480
}
4481
assert(lane < (int)myLanes.size());
4482
myLanes[lane].friction = friction;
4483
}
4484
4485
4486
void
4487
NBEdge::setAcceleration(int lane, bool accelRamp) {
4488
assert(lane >= 0);
4489
assert(lane < (int)myLanes.size());
4490
myLanes[lane].accelRamp = accelRamp;
4491
}
4492
4493
4494
void
4495
NBEdge::setLaneShape(int lane, const PositionVector& shape) {
4496
assert(lane >= 0);
4497
assert(lane < (int)myLanes.size());
4498
myLanes[lane].customShape = shape;
4499
}
4500
4501
4502
void
4503
NBEdge::setPermissions(SVCPermissions permissions, int lane) {
4504
if (lane < 0) {
4505
for (int i = 0; i < (int)myLanes.size(); i++) {
4506
// ... do it for each lane
4507
setPermissions(permissions, i);
4508
}
4509
} else {
4510
assert(lane < (int)myLanes.size());
4511
myLanes[lane].permissions = permissions;
4512
}
4513
}
4514
4515
4516
void
4517
NBEdge::setPreferredVehicleClass(SVCPermissions permissions, int lane) {
4518
if (lane < 0) {
4519
for (int i = 0; i < (int)myLanes.size(); i++) {
4520
// ... do it for each lane
4521
setPreferredVehicleClass(permissions, i);
4522
}
4523
} else {
4524
assert(lane < (int)myLanes.size());
4525
myLanes[lane].preferred = permissions;
4526
}
4527
}
4528
4529
4530
void
4531
NBEdge::setPermittedChanging(int lane, SVCPermissions changeLeft, SVCPermissions changeRight) {
4532
assert(lane >= 0);
4533
assert(lane < (int)myLanes.size());
4534
myLanes[lane].changeLeft = changeLeft;
4535
myLanes[lane].changeRight = changeRight;
4536
}
4537
4538
4539
SVCPermissions
4540
NBEdge::getPermissions(int lane) const {
4541
if (lane < 0) {
4542
SVCPermissions result = 0;
4543
for (int i = 0; i < (int)myLanes.size(); i++) {
4544
result |= getPermissions(i);
4545
}
4546
return result;
4547
} else {
4548
assert(lane < (int)myLanes.size());
4549
return myLanes[lane].permissions;
4550
}
4551
}
4552
4553
4554
void
4555
NBEdge::setLoadedLength(double val) {
4556
myLoadedLength = val;
4557
}
4558
4559
void
4560
NBEdge::setAverageLengthWithOpposite(double val) {
4561
myLength = val;
4562
}
4563
4564
4565
void
4566
NBEdge::dismissVehicleClassInformation() {
4567
for (std::vector<Lane>::iterator i = myLanes.begin(); i != myLanes.end(); ++i) {
4568
(*i).permissions = SVCAll;
4569
(*i).preferred = 0;
4570
}
4571
}
4572
4573
4574
bool
4575
NBEdge::connections_sorter(const Connection& c1, const Connection& c2) {
4576
if (c1.fromLane != c2.fromLane) {
4577
return c1.fromLane < c2.fromLane;
4578
}
4579
if (c1.toEdge != c2.toEdge) {
4580
return false; // do not change ordering among toEdges as this is determined by angle in an earlier step
4581
}
4582
return c1.toLane < c2.toLane;
4583
}
4584
4585
4586
double
4587
NBEdge::getSignalOffset() const {
4588
if (mySignalPosition == Position::INVALID) {
4589
return UNSPECIFIED_SIGNAL_OFFSET;
4590
} else {
4591
Position laneEnd = myLaneSpreadFunction == LaneSpreadFunction::RIGHT ?
4592
myLanes.back().shape.back() : myLanes[getNumLanes() / 2].shape.back();
4593
//std::cout << getID() << " signalPos=" << mySignalPosition << " laneEnd=" << laneEnd << " toShape=" << myTo->getShape() << " toBorder=" << myToBorder << "\n";
4594
return mySignalPosition.distanceTo2D(laneEnd);
4595
}
4596
}
4597
4598
4599
int
4600
NBEdge::getFirstNonPedestrianLaneIndex(int direction, bool exclusive) const {
4601
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4602
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4603
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4604
for (int i = start; i != end; i += direction) {
4605
// SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4606
// in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4607
if ((exclusive && myLanes[i].permissions != SVC_PEDESTRIAN && myLanes[i].permissions != 0)
4608
|| ((myLanes[i].permissions & SVC_PEDESTRIAN) == 0 && myLanes[i].permissions != 0)) {
4609
return i;
4610
}
4611
}
4612
return -1;
4613
}
4614
4615
int
4616
NBEdge::getFirstNonPedestrianNonBicycleLaneIndex(int direction, bool exclusive) const {
4617
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4618
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4619
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4620
for (int i = start; i != end; i += direction) {
4621
// SVCAll, does not count as a sidewalk, green verges (permissions = 0) do not count as road
4622
// in the exclusive case, lanes that allow pedestrians along with any other class also count as road
4623
SVCPermissions p = myLanes[i].permissions;
4624
if ((exclusive && p != SVC_PEDESTRIAN && p != SVC_BICYCLE && p != (SVC_PEDESTRIAN | SVC_BICYCLE) && p != 0)
4625
|| (p == SVCAll || ((p & (SVC_PEDESTRIAN | SVC_BICYCLE)) == 0 && p != 0))) {
4626
return i;
4627
}
4628
}
4629
return -1;
4630
}
4631
4632
int
4633
NBEdge::getSpecialLane(SVCPermissions permissions) const {
4634
for (int i = 0; i < (int)myLanes.size(); i++) {
4635
if (myLanes[i].permissions == permissions) {
4636
return i;
4637
}
4638
}
4639
return -1;
4640
}
4641
4642
int
4643
NBEdge::getFirstAllowedLaneIndex(int direction) const {
4644
assert(direction == NBNode::FORWARD || direction == NBNode::BACKWARD);
4645
const int start = (direction == NBNode::FORWARD ? 0 : (int)myLanes.size() - 1);
4646
const int end = (direction == NBNode::FORWARD ? (int)myLanes.size() : - 1);
4647
for (int i = start; i != end; i += direction) {
4648
if (myLanes[i].permissions != 0) {
4649
return i;
4650
}
4651
}
4652
return end - direction;
4653
}
4654
4655
4656
std::set<SVCPermissions>
4657
NBEdge::getPermissionVariants(int iStart, int iEnd) const {
4658
std::set<SVCPermissions> result;
4659
if (iStart < 0 || iStart >= getNumLanes() || iEnd > getNumLanes()) {
4660
throw ProcessError("invalid indices iStart " + toString(iStart) + " iEnd " + toString(iEnd) + " for edge with " + toString(getNumLanes()) + " lanes.");
4661
}
4662
for (int i = iStart; i < iEnd; ++i) {
4663
result.insert(getPermissions(i));
4664
}
4665
return result;
4666
}
4667
4668
int
4669
NBEdge::getNumLanesThatAllow(SVCPermissions permissions, bool allPermissions) const {
4670
int result = 0;
4671
for (const Lane& lane : myLanes) {
4672
if ((allPermissions && (lane.permissions & permissions) == permissions)
4673
|| (!allPermissions && (lane.permissions & permissions) != 0)) {
4674
result++;
4675
}
4676
}
4677
return result;
4678
}
4679
4680
bool
4681
NBEdge::allowsChangingLeft(int lane, SUMOVehicleClass vclass) const {
4682
assert(lane >= 0 && lane < getNumLanes());
4683
return myLanes[lane].changeLeft == SVC_UNSPECIFIED ? true : (myLanes[lane].changeLeft & vclass) == vclass;
4684
}
4685
4686
bool
4687
NBEdge::allowsChangingRight(int lane, SUMOVehicleClass vclass) const {
4688
assert(lane >= 0 && lane < getNumLanes());
4689
return myLanes[lane].changeRight == SVC_UNSPECIFIED ? true : (myLanes[lane].changeRight & vclass) == vclass;
4690
}
4691
4692
double
4693
NBEdge::getCrossingAngle(NBNode* node) {
4694
double angle = getAngleAtNode(node) + (getFromNode() == node ? 180.0 : 0.0);
4695
if (angle < 0) {
4696
angle += 360.0;
4697
}
4698
if (angle >= 360) {
4699
angle -= 360.0;
4700
}
4701
if (gDebugFlag1) {
4702
std::cout << getID() << " angle=" << getAngleAtNode(node) << " convAngle=" << angle << "\n";
4703
}
4704
return angle;
4705
}
4706
4707
4708
NBEdge::Lane
4709
NBEdge::getFirstNonPedestrianLane(int direction) const {
4710
int index = getFirstNonPedestrianLaneIndex(direction);
4711
if (index < 0) {
4712
throw ProcessError(TLF("Edge % allows pedestrians on all lanes", getID()));
4713
}
4714
return myLanes[index];
4715
}
4716
4717
std::string
4718
NBEdge::getSidewalkID() {
4719
// see IntermodalEdge::getSidewalk()
4720
for (int i = 0; i < (int)myLanes.size(); i++) {
4721
if (myLanes[i].permissions == SVC_PEDESTRIAN) {
4722
return getLaneID(i);
4723
}
4724
}
4725
for (int i = 0; i < (int)myLanes.size(); i++) {
4726
if ((myLanes[i].permissions & SVC_PEDESTRIAN) != 0) {
4727
return getLaneID(i);
4728
}
4729
}
4730
return getLaneID(0);
4731
}
4732
4733
void
4734
NBEdge::addSidewalk(double width) {
4735
addRestrictedLane(width, SVC_PEDESTRIAN);
4736
}
4737
4738
4739
void
4740
NBEdge::restoreSidewalk(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4741
restoreRestrictedLane(SVC_PEDESTRIAN, oldLanes, oldGeometry, oldConnections);
4742
}
4743
4744
4745
void
4746
NBEdge::addBikeLane(double width) {
4747
addRestrictedLane(width, SVC_BICYCLE);
4748
}
4749
4750
4751
void
4752
NBEdge::restoreBikelane(std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4753
restoreRestrictedLane(SVC_BICYCLE, oldLanes, oldGeometry, oldConnections);
4754
}
4755
4756
bool
4757
NBEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {
4758
for (const Lane& lane : myLanes) {
4759
if (lane.permissions == vclass) {
4760
return true;
4761
}
4762
}
4763
return false;
4764
}
4765
4766
4767
void
4768
NBEdge::addRestrictedLane(double width, SUMOVehicleClass vclass) {
4769
if (hasRestrictedLane(vclass)) {
4770
WRITE_WARNINGF(TL("Edge '%' already has a dedicated lane for %s. Not adding another one."), getID(), toString(vclass));
4771
return;
4772
}
4773
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER) {
4774
myGeom.move2side(width / 2);
4775
}
4776
// disallow the designated vclass on all "old" lanes
4777
disallowVehicleClass(-1, vclass);
4778
// don't create a restricted vehicle lane to the right of a sidewalk
4779
const int newIndex = (vclass != SVC_PEDESTRIAN && myLanes[0].permissions == SVC_PEDESTRIAN) ? 1 : 0;
4780
if (newIndex == 0) {
4781
// disallow pedestrians on all "higher" lanes to ensure that sidewalk remains the rightmost lane
4782
disallowVehicleClass(-1, SVC_PEDESTRIAN);
4783
}
4784
// add new lane
4785
myLanes.insert(myLanes.begin() + newIndex, Lane(this, myLanes[0].getParameter(SUMO_PARAM_ORIGID)));
4786
myLanes[newIndex].permissions = vclass;
4787
myLanes[newIndex].width = fabs(width);
4788
// shift outgoing connections to the left
4789
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4790
Connection& c = *it;
4791
if (c.fromLane >= newIndex) {
4792
c.fromLane += 1;
4793
}
4794
}
4795
// shift incoming connections to the left
4796
const EdgeVector& incoming = myFrom->getIncomingEdges();
4797
for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4798
(*it)->shiftToLanesToEdge(this, 1);
4799
}
4800
myFrom->shiftTLConnectionLaneIndex(this, 1);
4801
myTo->shiftTLConnectionLaneIndex(this, 1);
4802
computeLaneShapes();
4803
}
4804
4805
4806
void
4807
NBEdge::restoreRestrictedLane(SUMOVehicleClass vclass, std::vector<NBEdge::Lane> oldLanes, PositionVector oldGeometry, std::vector<NBEdge::Connection> oldConnections) {
4808
// check that previously lane was transformed
4809
if (myLanes[0].permissions != vclass) {
4810
WRITE_WARNINGF(TL("Edge '%' doesn't have a dedicated lane for %s. Cannot be restored."), getID(), toString(vclass));
4811
return;
4812
}
4813
// restore old values
4814
myGeom = oldGeometry;
4815
myLanes = oldLanes;
4816
myConnections = oldConnections;
4817
// shift incoming connections to the right
4818
const EdgeVector& incoming = myFrom->getIncomingEdges();
4819
for (EdgeVector::const_iterator it = incoming.begin(); it != incoming.end(); ++it) {
4820
(*it)->shiftToLanesToEdge(this, 0);
4821
}
4822
// Shift TL conections
4823
myFrom->shiftTLConnectionLaneIndex(this, 0);
4824
myTo->shiftTLConnectionLaneIndex(this, 0);
4825
computeLaneShapes();
4826
}
4827
4828
4829
void
4830
NBEdge::shiftToLanesToEdge(NBEdge* to, int laneOff) {
4831
/// XXX could we repurpose the function replaceInConnections ?
4832
for (std::vector<Connection>::iterator it = myConnections.begin(); it != myConnections.end(); ++it) {
4833
if ((*it).toEdge == to && (*it).toLane >= 0) {
4834
(*it).toLane += laneOff;
4835
}
4836
}
4837
}
4838
4839
4840
bool
4841
NBEdge::shiftPositionAtNode(NBNode* node, NBEdge* other) {
4842
if (myLaneSpreadFunction == LaneSpreadFunction::CENTER
4843
&& !isRailway(getPermissions())
4844
&& !isRailway(other->getPermissions())
4845
&& getBidiEdge() == nullptr) {
4846
const int i = (node == myTo ? -1 : 0);
4847
const int i2 = (node == myTo ? 0 : -1);
4848
const double dist = myGeom[i].distanceTo2D(node->getPosition());
4849
const double neededOffset = getTotalWidth() / 2;
4850
const double dist2 = MIN2(myGeom.distance2D(other->getGeometry()[i2]),
4851
other->getGeometry().distance2D(myGeom[i]));
4852
const double neededOffset2 = neededOffset + (other->getTotalWidth()) / 2;
4853
if (dist < neededOffset && dist2 < neededOffset2) {
4854
PositionVector tmp = myGeom;
4855
// @note this doesn't work well for vissim networks
4856
//tmp.move2side(MIN2(neededOffset - dist, neededOffset2 - dist2));
4857
try {
4858
tmp.move2side(neededOffset - dist);
4859
tmp[i].round(gPrecision);
4860
myGeom[i] = tmp[i];
4861
computeAngle();
4862
return true;
4863
//std::cout << getID() << " shiftPositionAtNode needed=" << neededOffset << " dist=" << dist << " needed2=" << neededOffset2 << " dist2=" << dist2 << " by=" << (neededOffset - dist) << " other=" << other->getID() << "\n";
4864
} catch (InvalidArgument&) {
4865
WRITE_WARNINGF(TL("Could not avoid overlapping shape at node '%' for edge '%'."), node->getID(), getID());
4866
}
4867
}
4868
}
4869
return false;
4870
}
4871
4872
4873
Position
4874
NBEdge::geometryPositionAtOffset(double offset) const {
4875
if (myLoadedLength > 0) {
4876
return myGeom.positionAtOffset(offset * myLength / myLoadedLength);
4877
} else {
4878
return myGeom.positionAtOffset(offset);
4879
}
4880
}
4881
4882
4883
double
4884
NBEdge::getFinalLength() const {
4885
double result = getLoadedLength();
4886
if (OptionsCont::getOptions().getBool("no-internal-links") && !hasLoadedLength()) {
4887
// use length to junction center even if a modified geometry was given
4888
PositionVector geom = cutAtIntersection(myGeom);
4889
geom.push_back_noDoublePos(getToNode()->getCenter());
4890
geom.push_front_noDoublePos(getFromNode()->getCenter());
4891
result = geom.length();
4892
}
4893
double avgEndOffset = 0;
4894
for (const Lane& lane : myLanes) {
4895
avgEndOffset += lane.endOffset;
4896
}
4897
if (isBidiRail()) {
4898
avgEndOffset += myPossibleTurnDestination->getEndOffset();
4899
}
4900
avgEndOffset /= (double)myLanes.size();
4901
return MAX2(result - avgEndOffset, POSITION_EPS);
4902
}
4903
4904
4905
void
4906
NBEdge::setOrigID(const std::string origID, const bool append, const int laneIdx) {
4907
if (laneIdx == -1) {
4908
for (int i = 0; i < (int)myLanes.size(); i++) {
4909
setOrigID(origID, append, i);
4910
}
4911
} else {
4912
if (origID != "") {
4913
if (append) {
4914
std::vector<std::string> oldIDs = StringTokenizer(myLanes[laneIdx].getParameter(SUMO_PARAM_ORIGID)).getVector();
4915
if (std::find(oldIDs.begin(), oldIDs.end(), origID) == oldIDs.end()) {
4916
oldIDs.push_back(origID);
4917
}
4918
myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, toString(oldIDs));
4919
} else {
4920
myLanes[laneIdx].setParameter(SUMO_PARAM_ORIGID, origID);
4921
}
4922
} else {
4923
// do not record empty origID parameter
4924
myLanes[laneIdx].unsetParameter(SUMO_PARAM_ORIGID);
4925
}
4926
}
4927
}
4928
4929
4930
const EdgeVector&
4931
NBEdge::getSuccessors(SUMOVehicleClass vClass) const {
4932
// @todo cache successors instead of recomputing them every time
4933
mySuccessors.clear();
4934
//std::cout << "getSuccessors edge=" << getID() << " svc=" << toString(vClass) << " cons=" << myConnections.size() << "\n";
4935
for (const Connection& con : myConnections) {
4936
if (con.fromLane >= 0 && con.toLane >= 0 && con.toEdge != nullptr &&
4937
(vClass == SVC_IGNORING || (getPermissions(con.fromLane)
4938
& con.toEdge->getPermissions(con.toLane) & vClass) != 0)
4939
&& std::find(mySuccessors.begin(), mySuccessors.end(), con.toEdge) == mySuccessors.end()) {
4940
mySuccessors.push_back(con.toEdge);
4941
//std::cout << " succ=" << con.toEdge->getID() << "\n";
4942
}
4943
}
4944
return mySuccessors;
4945
}
4946
4947
4948
const ConstRouterEdgePairVector&
4949
NBEdge::getViaSuccessors(SUMOVehicleClass vClass, bool /*ignoreTransientPermissions*/) const {
4950
// @todo cache successors instead of recomputing them every time
4951
myViaSuccessors.clear();
4952
for (const Connection& con : myConnections) {
4953
std::pair<const NBEdge*, const Connection*> pair(con.toEdge, nullptr);
4954
// special case for Persons in Netedit
4955
if (vClass == SVC_PEDESTRIAN) {
4956
myViaSuccessors.push_back(pair); // Pedestrians have complete freedom of movement in all sucessors
4957
} else if ((con.fromLane >= 0) && (con.toLane >= 0) &&
4958
(con.toEdge != nullptr) &&
4959
((getPermissions(con.fromLane) & con.toEdge->getPermissions(con.toLane) & vClass) == vClass)) {
4960
// ignore duplicates
4961
if (con.getLength() > 0) {
4962
pair.second = &con;
4963
}
4964
myViaSuccessors.push_back(pair);
4965
}
4966
}
4967
return myViaSuccessors;
4968
}
4969
4970
4971
void
4972
NBEdge::debugPrintConnections(bool outgoing, bool incoming) const {
4973
if (outgoing) {
4974
for (const Connection& c : myConnections) {
4975
std::cout << " " << getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4976
}
4977
}
4978
if (incoming) {
4979
for (NBEdge* inc : myFrom->getIncomingEdges()) {
4980
for (Connection& c : inc->myConnections) {
4981
if (c.toEdge == this) {
4982
std::cout << " " << inc->getID() << "_" << c.fromLane << "->" << c.toEdge->getID() << "_" << c.toLane << "\n";
4983
}
4984
}
4985
}
4986
}
4987
}
4988
4989
4990
int
4991
NBEdge::getLaneIndexFromLaneID(const std::string laneID) {
4992
return StringUtils::toInt(laneID.substr(laneID.rfind("_") + 1));
4993
}
4994
4995
bool
4996
NBEdge::joinLanes(SVCPermissions perms) {
4997
bool haveJoined = false;
4998
int i = 0;
4999
while (i < getNumLanes() - 1) {
5000
if ((getPermissions(i) == perms) && (getPermissions(i + 1) == perms)) {
5001
const double newWidth = getLaneWidth(i) + getLaneWidth(i + 1);
5002
const std::string newType = myLanes[i].type + "|" + myLanes[i + 1].type;
5003
deleteLane(i, false, true);
5004
setLaneWidth(i, newWidth);
5005
setLaneType(i, newType);
5006
haveJoined = true;
5007
} else {
5008
i++;
5009
}
5010
}
5011
return haveJoined;
5012
}
5013
5014
5015
EdgeVector
5016
NBEdge::filterByPermissions(const EdgeVector& edges, SVCPermissions permissions) {
5017
EdgeVector result;
5018
for (NBEdge* edge : edges) {
5019
if ((edge->getPermissions() & permissions) != 0) {
5020
result.push_back(edge);
5021
}
5022
}
5023
return result;
5024
}
5025
5026
NBEdge*
5027
NBEdge::getStraightContinuation(SVCPermissions permissions) const {
5028
EdgeVector cands = filterByPermissions(myTo->getOutgoingEdges(), permissions);
5029
if (cands.size() == 0) {
5030
return nullptr;
5031
}
5032
sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this));
5033
NBEdge* best = cands.front();
5034
if (isTurningDirectionAt(best)) {
5035
return nullptr;
5036
} else {
5037
return best;
5038
}
5039
}
5040
5041
NBEdge*
5042
NBEdge::getStraightPredecessor(SVCPermissions permissions) const {
5043
EdgeVector cands = filterByPermissions(myFrom->getIncomingEdges(), permissions);
5044
if (cands.size() == 0) {
5045
return nullptr;
5046
}
5047
sort(cands.begin(), cands.end(), NBContHelper::edge_similar_direction_sorter(this, false));
5048
NBEdge* best = cands.front();
5049
if (best->isTurningDirectionAt(this)) {
5050
return nullptr;
5051
} else {
5052
return best;
5053
}
5054
}
5055
5056
5057
NBEdge*
5058
NBEdge::guessOpposite(bool reguess) {
5059
NBEdge* opposite = nullptr;
5060
if (getNumLanes() > 0) {
5061
NBEdge::Lane& lastLane = myLanes.back();
5062
const double lastWidth = getLaneWidth(getNumLanes() - 1);
5063
if (lastLane.oppositeID == "" || reguess) {
5064
for (NBEdge* cand : getToNode()->getOutgoingEdges()) {
5065
if (cand->getToNode() == getFromNode() && !cand->getLanes().empty()) {
5066
const NBEdge::Lane& candLastLane = cand->getLanes().back();
5067
if (candLastLane.oppositeID == "" || candLastLane.oppositeID == getLaneID(getNumLanes() - 1)) {
5068
const double lastWidthCand = cand->getLaneWidth(cand->getNumLanes() - 1);
5069
// in sharp corners, the difference may be higher
5070
// factor (sqrt(2) for 90 degree corners
5071
const double threshold = 1.42 * 0.5 * (lastWidth + lastWidthCand) + 0.5;
5072
const double distance = VectorHelper<double>::maxValue(lastLane.shape.distances(cand->getLanes().back().shape));
5073
//std::cout << " distance=" << distance << " threshold=" << threshold << " distances=" << toString(lastLane.shape.distances(cand->getLanes().back().shape)) << "\n";
5074
if (distance < threshold) {
5075
opposite = cand;
5076
}
5077
}
5078
}
5079
}
5080
if (opposite != nullptr) {
5081
lastLane.oppositeID = opposite->getLaneID(opposite->getNumLanes() - 1);
5082
}
5083
}
5084
}
5085
return opposite;
5086
}
5087
5088
double
5089
NBEdge::getDistancAt(double pos) const {
5090
// negative values of myDistances indicate descending kilometrage
5091
return fabs(myDistance + pos);
5092
}
5093
5094
/****************************************************************************/
5095
5096