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