Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/netedit/elements/network/GNEConnection.cpp
185790 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 GNEConnection.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Jun 2016
17
///
18
// A class for visualizing connections between lanes
19
/****************************************************************************/
20
21
#include <netbuild/NBLoadedSUMOTLDef.h>
22
#include <netedit/changes/GNEChange_Attribute.h>
23
#include <netedit/changes/GNEChange_TLS.h>
24
#include <netedit/elements/moving/GNEMoveElementConnection.h>
25
#include <netedit/GNENet.h>
26
#include <netedit/GNETagProperties.h>
27
#include <netedit/GNEUndoList.h>
28
#include <utils/common/MsgHandler.h>
29
#include <utils/gui/div/GLHelper.h>
30
#include <utils/gui/div/GUIDesigns.h>
31
#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>
32
#include <utils/gui/windows/GUIAppEnum.h>
33
#include <utils/options/OptionsCont.h>
34
35
#include "GNEConnection.h"
36
#include "GNEInternalLane.h"
37
38
// ===========================================================================
39
// method definitions
40
// ===========================================================================
41
42
GNEConnection::GNEConnection(GNELane* from, GNELane* to) :
43
GNENetworkElement(from->getNet(), "from" + from->getID() + "to" + to->getID(), SUMO_TAG_CONNECTION),
44
myMoveElementConnection(new GNEMoveElementConnection(this)),
45
myLinkState(LINKSTATE_TL_OFF_NOSIGNAL),
46
mySpecialColor(nullptr),
47
myShapeDeprecated(true) {
48
// set parents
49
setParents<GNELane*>({from, to});
50
setParents<GNEEdge*>({from->getParentEdge(), to->getParentEdge()});
51
}
52
53
54
GNEConnection::~GNEConnection() {
55
}
56
57
58
GNEMoveElement*
59
GNEConnection::getMoveElement() const {
60
return myMoveElementConnection;
61
}
62
63
64
Parameterised*
65
GNEConnection::getParameters() {
66
return &getNBEdgeConnection();
67
}
68
69
70
const Parameterised*
71
GNEConnection::getParameters() const {
72
return &getNBEdgeConnection();
73
74
}
75
76
77
const PositionVector&
78
GNEConnection::getConnectionShape() const {
79
if (myConnectionGeometry.getShape().size() > 0) {
80
return myConnectionGeometry.getShape();
81
} else {
82
return getNBEdgeConnection().customShape;
83
}
84
}
85
86
87
void
88
GNEConnection::updateGeometry() {
89
// check if adjust shape
90
if (myShapeDeprecated && existNBEdgeConnection()) {
91
// Get shape of from and to lanes
92
const NBEdge::Connection& nbCon = getNBEdgeConnection();
93
// obtain lane shapes
94
PositionVector laneShapeFrom = getParentLanes().front()->getLaneShape();
95
PositionVector laneShapeTo = getParentLanes().back()->getLaneShape();
96
// Calculate shape of connection depending of the size of Junction shape
97
if (nbCon.customShape.size() > 0) {
98
myConnectionGeometry.updateGeometry(nbCon.customShape);
99
} else if (nbCon.shape.size() > 1) {
100
PositionVector connectionShape;
101
if ((nbCon.shape.length() < 3) && !nbCon.haveVia) {
102
// apply offset to lane shape if we're in lane spread function center
103
if (getParentLanes().front()->getParentEdge()->getNBEdge()->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
104
laneShapeFrom.move2side(0.3);
105
}
106
if (getParentLanes().back()->getParentEdge()->getNBEdge()->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {
107
laneShapeTo.move2side(0.3);
108
}
109
// check if this connetion is a turn around
110
bool turnAround = false;
111
const auto fromOppositeEdges = getParentLanes().front()->getParentEdge()->getOppositeEdges();
112
for (const auto& edge : fromOppositeEdges) {
113
if (edge == getParentLanes().back()->getParentEdge()) {
114
turnAround = true;
115
break;
116
}
117
}
118
// add from lane shape one step before
119
if (laneShapeFrom.length() > 1) {
120
// set length depending of turn arounds
121
if (turnAround) {
122
connectionShape.push_back(laneShapeFrom.positionAtOffset(laneShapeFrom.length() - 0.5));
123
} else {
124
connectionShape.push_back(laneShapeFrom.positionAtOffset(laneShapeFrom.length() - 1));
125
}
126
}
127
// add from lane shape
128
connectionShape.push_back(laneShapeFrom.back());
129
// add to lane shape
130
connectionShape.push_back(laneShapeTo.front());
131
// add to lane shape one step after
132
if (laneShapeTo.length() > 1) {
133
// set length depending of turn arounds
134
if (turnAround) {
135
connectionShape.push_back(laneShapeTo.positionAtOffset(0.5));
136
} else {
137
connectionShape.push_back(laneShapeTo.positionAtOffset(1));
138
}
139
}
140
} else {
141
connectionShape = nbCon.shape;
142
// only append via shape if it exists
143
if (nbCon.haveVia) {
144
connectionShape.append(nbCon.viaShape);
145
}
146
}
147
myConnectionGeometry.updateGeometry(connectionShape);
148
} else if (getParentLanes().front()->getLane2laneConnections().exist(getParentLanes().back())) {
149
myConnectionGeometry = getParentLanes().front()->getLane2laneConnections().getLane2laneGeometry(getParentLanes().back());
150
} else {
151
myConnectionGeometry.clearGeometry();
152
}
153
// check if internal junction marker must be calculated
154
if (nbCon.haveVia && (nbCon.shape.size() > 0)) {
155
// create marker for internal junction waiting position (contPos)
156
const double orthoLength = 0.5;
157
PositionVector internalJunctionMarker = nbCon.shape.getOrthogonal(nbCon.shape.back(), 10, true, 0.1);
158
if (internalJunctionMarker.length() < orthoLength) {
159
internalJunctionMarker.extrapolate(orthoLength - internalJunctionMarker.length());
160
}
161
myInternalJunctionMarkerGeometry.updateGeometry(internalJunctionMarker);
162
} else {
163
myInternalJunctionMarkerGeometry.clearGeometry();
164
}
165
// mark connection as non-deprecated
166
myShapeDeprecated = false;
167
}
168
}
169
170
171
Position
172
GNEConnection::getPositionInView() const {
173
// currently unused
174
return Position(0, 0);
175
}
176
177
178
bool
179
GNEConnection::checkDrawFromContour() const {
180
return false;
181
}
182
183
184
bool
185
GNEConnection::checkDrawToContour() const {
186
return false;
187
}
188
189
190
bool
191
GNEConnection::checkDrawRelatedContour() const {
192
// check opened popup
193
if (myNet->getViewNet()->getPopup()) {
194
return myNet->getViewNet()->getPopup()->getGLObject() == this;
195
}
196
return false;
197
}
198
199
200
bool
201
GNEConnection::checkDrawOverContour() const {
202
return false;
203
}
204
205
206
bool
207
GNEConnection::checkDrawDeleteContour() const {
208
// get edit modes
209
const auto& editModes = myNet->getViewNet()->getEditModes();
210
// check if we're in delete mode
211
if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {
212
return myNet->getViewNet()->checkOverLockedElement(this, mySelected);
213
} else {
214
return false;
215
}
216
}
217
218
219
bool
220
GNEConnection::checkDrawDeleteContourSmall() const {
221
return false;
222
}
223
224
225
bool
226
GNEConnection::checkDrawSelectContour() const {
227
// get edit modes
228
const auto& editModes = myNet->getViewNet()->getEditModes();
229
// check if we're in select mode
230
if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {
231
return myNet->getViewNet()->checkOverLockedElement(this, mySelected);
232
} else {
233
return false;
234
}
235
}
236
237
238
bool
239
GNEConnection::checkDrawMoveContour() const {
240
// get edit modes
241
const auto& editModes = myNet->getViewNet()->getEditModes();
242
// check if we're in move mode
243
if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&
244
(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {
245
// check if we're editing this network element
246
const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();
247
if (editedNetworkElement) {
248
return editedNetworkElement == this;
249
} else {
250
// only move the first element
251
return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;
252
}
253
} else {
254
return false;
255
}
256
}
257
258
259
GNEEdge*
260
GNEConnection::getEdgeFrom() const {
261
return getParentEdges().front();
262
}
263
264
265
GNEEdge*
266
GNEConnection::getEdgeTo() const {
267
return getParentEdges().back();
268
}
269
270
271
GNELane*
272
GNEConnection::getLaneFrom() const {
273
return getParentLanes().front();
274
}
275
276
277
GNELane*
278
GNEConnection::getLaneTo() const {
279
return getParentLanes().back();
280
}
281
282
283
int
284
GNEConnection::getFromLaneIndex() const {
285
return getParentLanes().front()->getIndex();
286
}
287
288
289
int
290
GNEConnection::getToLaneIndex() const {
291
return getParentLanes().back()->getIndex();
292
}
293
294
295
NBEdge::Connection&
296
GNEConnection::getNBEdgeConnection() const {
297
return getParentEdges().front()->getNBEdge()->getConnectionRef(getFromLaneIndex(), getParentEdges().back()->getNBEdge(), getToLaneIndex());
298
}
299
300
301
NBConnection
302
GNEConnection::getNBConnection() const {
303
const NBEdge::Connection& c = getNBEdgeConnection();
304
return NBConnection(getParentEdges().front()->getNBEdge(), getFromLaneIndex(),
305
getParentEdges().back()->getNBEdge(), getToLaneIndex(),
306
(int)c.tlLinkIndex, (int)c.tlLinkIndex2);
307
}
308
309
310
void
311
GNEConnection::updateConnectionID() {
312
setNetworkElementID(getParentLanes().front()->getID() + " -> " + getParentLanes().back()->getID());
313
}
314
315
316
LinkState
317
GNEConnection::getLinkState() const {
318
return myLinkState;
319
}
320
321
322
void
323
GNEConnection::markConnectionGeometryDeprecated() {
324
myShapeDeprecated = true;
325
}
326
327
328
void
329
GNEConnection::updateLinkState() {
330
const NBEdge::Connection& nbCon = getNBEdgeConnection();
331
myLinkState = getParentEdges().front()->getNBEdge()->getToNode()->getLinkState(getParentEdges().front()->getNBEdge(),
332
nbCon.toEdge,
333
nbCon.fromLane,
334
nbCon.toLane,
335
nbCon.mayDefinitelyPass,
336
nbCon.tlID);
337
}
338
339
340
void
341
GNEConnection::smootShape() {
342
auto shape = getConnectionShape();
343
shape = shape.bezier(5);
344
setAttribute(SUMO_ATTR_CUSTOMSHAPE, toString(shape), myNet->getUndoList());
345
}
346
347
348
GUIGLObjectPopupMenu*
349
GNEConnection::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {
350
if (myShapeEdited) {
351
return getShapeEditedPopUpMenu(app, parent, getNBEdgeConnection().customShape);
352
} else {
353
// create popup
354
GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);
355
// build common options
356
buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);
357
// check if we're in supermode network
358
if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {
359
// create menu commands
360
FXMenuCommand* mcCustomShape = GUIDesigns::buildFXMenuCommand(ret, TL("Set custom connection shape"), nullptr, &parent, MID_GNE_CONNECTION_EDIT_SHAPE);
361
GUIDesigns::buildFXMenuCommand(ret, TL("Smooth connection shape"), nullptr, &parent, MID_GNE_CONNECTION_SMOOTH_SHAPE);
362
// check if menu commands has to be disabled
363
NetworkEditMode editMode = myNet->getViewNet()->getEditModes().networkEditMode;
364
// check if we're in the correct edit mode
365
if ((editMode == NetworkEditMode::NETWORK_CONNECT) || (editMode == NetworkEditMode::NETWORK_TLS) || (editMode == NetworkEditMode::NETWORK_CREATE_EDGE)) {
366
mcCustomShape->disable();
367
}
368
}
369
return ret;
370
}
371
}
372
373
374
double
375
GNEConnection::getExaggeration(const GUIVisualizationSettings& s) const {
376
return s.addSize.getExaggeration(s, this);
377
}
378
379
380
Boundary
381
GNEConnection::getCenteringBoundary() const {
382
return myNetworkElementContour.getContourBoundary();
383
}
384
385
386
void
387
GNEConnection::updateCenteringBoundary(const bool /*updateGrid*/) {
388
// nothing to update
389
}
390
391
392
void
393
GNEConnection::drawGL(const GUIVisualizationSettings& s) const {
394
// Check if connection must be drawed
395
if (checkDrawConnection()) {
396
// get connection exaggeration
397
const double connectionExaggeration = isAttributeCarrierSelected() ? s.selectorFrameScale : 1;
398
// get detail level
399
const auto d = s.getDetailLevel(connectionExaggeration);
400
// check if draw shape superposed (used in train lanes)
401
PositionVector shapeSuperposed = myConnectionGeometry.getShape();
402
if (getParentLanes().front()->getDrawingConstants()->drawSuperposed()) {
403
shapeSuperposed.move2side(0.5);
404
}
405
GUIGeometry superposedGeometry(shapeSuperposed);
406
// draw geometry only if we'rent in drawForObjectUnderCursor mode
407
if (!s.drawForViewObjectsHandler) {
408
// draw connection
409
drawConnection(s, d, superposedGeometry, connectionExaggeration);
410
// draw lock icon
411
GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), 0.1);
412
// draw dotted contour depending if we're editing the custom shape
413
const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();
414
if (editedNetworkElement && (editedNetworkElement == this)) {
415
// draw dotted contour geometry points
416
myNetworkElementContour.drawDottedContourGeometryPoints(s, d, this, shapeSuperposed, s.neteditSizeSettings.connectionGeometryPointRadius,
417
connectionExaggeration, s.dottedContourSettings.segmentWidthSmall);
418
} else {
419
// draw dotted contour
420
myNetworkElementContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);
421
}
422
}
423
// calculate contour
424
calculateConnectionContour(s, d, shapeSuperposed, connectionExaggeration);
425
}
426
}
427
428
429
void
430
GNEConnection::deleteGLObject() {
431
myNet->deleteNetworkElement(this, myNet->getUndoList());
432
}
433
434
435
void
436
GNEConnection::updateGLObject() {
437
updateGeometry();
438
}
439
440
441
void
442
GNEConnection::setSpecialColor(const RGBColor* color) {
443
mySpecialColor = color;
444
}
445
446
447
std::string
448
GNEConnection::getAttribute(SumoXMLAttr key) const {
449
// first get attributes in which nbConnection reference can be invalid
450
switch (key) {
451
case SUMO_ATTR_ID:
452
return getMicrosimID();
453
case SUMO_ATTR_FROM:
454
return getParentLanes().front()->getParentEdge()->getID();
455
case SUMO_ATTR_TO:
456
return getParentLanes().back()->getParentEdge()->getID();
457
case SUMO_ATTR_FROM_LANE:
458
return getParentLanes().front()->getAttribute(SUMO_ATTR_INDEX);
459
case GNE_ATTR_FROM_LANEID:
460
return getParentLanes().front()->getID();
461
case SUMO_ATTR_TO_LANE:
462
return getParentLanes().back()->getAttribute(SUMO_ATTR_INDEX);
463
case GNE_ATTR_TO_LANEID:
464
return getParentLanes().back()->getID();
465
case GNE_ATTR_SELECTED:
466
case GNE_ATTR_FRONTELEMENT:
467
return getCommonAttribute(key);
468
case GNE_ATTR_PARENT:
469
return getParentEdges().front()->getToJunction()->getID();
470
default:
471
break;
472
}
473
// now continue with attributes that needs a nbConnection reference
474
const NBEdge::Connection& nbCon = getNBEdgeConnection();
475
switch (key) {
476
case SUMO_ATTR_PASS:
477
return toString(nbCon.mayDefinitelyPass);
478
case SUMO_ATTR_INDIRECT:
479
return toString(nbCon.indirectLeft);
480
case SUMO_ATTR_TYPE:
481
return toString(nbCon.edgeType);
482
case SUMO_ATTR_KEEP_CLEAR:
483
return toString(nbCon.keepClear);
484
case SUMO_ATTR_CONTPOS:
485
return toString(nbCon.contPos);
486
case SUMO_ATTR_UNCONTROLLED:
487
return toString(nbCon.uncontrolled);
488
case SUMO_ATTR_VISIBILITY_DISTANCE:
489
return toString(nbCon.visibility);
490
case SUMO_ATTR_TLLINKINDEX:
491
return toString(nbCon.tlLinkIndex);
492
case SUMO_ATTR_TLLINKINDEX2:
493
return toString(nbCon.tlLinkIndex2);
494
case SUMO_ATTR_ALLOW:
495
if (nbCon.permissions == SVC_UNSPECIFIED) {
496
return getVehicleClassNames(nbCon.toEdge->getLanes()[nbCon.toLane].permissions);
497
} else {
498
return getVehicleClassNames(nbCon.permissions);
499
}
500
case SUMO_ATTR_DISALLOW:
501
if (nbCon.permissions == SVC_UNSPECIFIED) {
502
return getVehicleClassNames(invertPermissions(nbCon.toEdge->getLanes()[nbCon.toLane].permissions));
503
} else {
504
return getVehicleClassNames(invertPermissions(nbCon.permissions));
505
}
506
case SUMO_ATTR_CHANGE_LEFT:
507
if (nbCon.changeLeft == SVC_UNSPECIFIED) {
508
return "all";
509
} else {
510
return getVehicleClassNames(nbCon.changeLeft);
511
}
512
case SUMO_ATTR_CHANGE_RIGHT:
513
if (nbCon.changeRight == SVC_UNSPECIFIED) {
514
return "all";
515
} else {
516
return getVehicleClassNames(nbCon.changeRight);
517
}
518
case SUMO_ATTR_SPEED:
519
if (nbCon.speed == NBEdge::UNSPECIFIED_SPEED) {
520
return "default";
521
} else {
522
return toString(nbCon.speed);
523
}
524
case SUMO_ATTR_LENGTH:
525
return toString(nbCon.customLength);
526
case SUMO_ATTR_DIR:
527
return toString(getParentEdges().front()->getNBEdge()->getToNode()->getDirection(
528
getParentEdges().front()->getNBEdge(), nbCon.toEdge, OptionsCont::getOptions().getBool("lefthand")));
529
case SUMO_ATTR_STATE:
530
return toString(getParentEdges().front()->getNBEdge()->getToNode()->getLinkState(
531
getParentEdges().front()->getNBEdge(), nbCon.toEdge, nbCon.fromLane, nbCon.toLane, nbCon.mayDefinitelyPass, nbCon.tlID));
532
case SUMO_ATTR_SHAPE:
533
case SUMO_ATTR_CUSTOMSHAPE:
534
return toString(nbCon.customShape);
535
default:
536
return getCommonAttribute(key);
537
}
538
}
539
540
541
double
542
GNEConnection::getAttributeDouble(SumoXMLAttr key) const {
543
return getCommonAttributeDouble(key);
544
}
545
546
547
Position
548
GNEConnection::getAttributePosition(SumoXMLAttr key) const {
549
return getCommonAttributePosition(key);
550
}
551
552
553
PositionVector
554
GNEConnection::getAttributePositionVector(SumoXMLAttr key) const {
555
switch (key) {
556
case SUMO_ATTR_SHAPE:
557
case SUMO_ATTR_CUSTOMSHAPE:
558
return getNBEdgeConnection().customShape;
559
default:
560
return getCommonAttributePositionVector(key);
561
}
562
}
563
564
565
void
566
GNEConnection::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {
567
const NBEdge::Connection& c = getNBEdgeConnection();
568
switch (key) {
569
case SUMO_ATTR_FROM:
570
case SUMO_ATTR_TO:
571
case SUMO_ATTR_FROM_LANE:
572
case SUMO_ATTR_TO_LANE:
573
case SUMO_ATTR_PASS:
574
case SUMO_ATTR_KEEP_CLEAR:
575
case SUMO_ATTR_CONTPOS:
576
case SUMO_ATTR_VISIBILITY_DISTANCE:
577
case SUMO_ATTR_ALLOW:
578
case SUMO_ATTR_DISALLOW:
579
case SUMO_ATTR_CHANGE_LEFT:
580
case SUMO_ATTR_CHANGE_RIGHT:
581
case SUMO_ATTR_SPEED:
582
case SUMO_ATTR_LENGTH:
583
case SUMO_ATTR_SHAPE:
584
case SUMO_ATTR_CUSTOMSHAPE:
585
case SUMO_ATTR_TYPE:
586
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
587
break;
588
case SUMO_ATTR_TLLINKINDEX:
589
if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {
590
changeTLIndex(key, parse<int>(value), c.tlLinkIndex2, undoList);
591
}
592
break;
593
case SUMO_ATTR_TLLINKINDEX2:
594
if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {
595
changeTLIndex(key, c.tlLinkIndex, parse<int>(value), undoList);
596
}
597
break;
598
case SUMO_ATTR_UNCONTROLLED:
599
undoList->begin(this, "change attribute controlled for connection");
600
{
601
const bool wasUncontrolled = c.uncontrolled;
602
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
603
if (wasUncontrolled && !c.uncontrolled) {
604
GNEEdge* srcEdge = getParentEdges().front();
605
NBConnection newNBCon(srcEdge->getNBEdge(), c.fromLane, c.toEdge, c.toLane);
606
srcEdge->getToJunction()->invalidateTLS(undoList, NBConnection::InvalidConnection, newNBCon);
607
}
608
}
609
undoList->end();
610
break;
611
case SUMO_ATTR_INDIRECT:
612
undoList->begin(this, "change attribute indirect for connection");
613
if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {
614
GNEChange_Attribute::changeAttribute(this, key, value, undoList);
615
int linkIndex2 = -1;
616
if (parse<bool>(value)) {
617
// find straight connection with the same toEdge
618
std::set<NBTrafficLightDefinition*> defs = getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS();
619
NBEdge* from = getParentEdges().front()->getNBEdge();
620
for (NBTrafficLightDefinition* tlDef : defs) {
621
for (const NBConnection& c2 : tlDef->getControlledLinks()) {
622
if (c2.getTo() == c.toEdge && c2.getFrom() != from) {
623
if (from->getToNode()->getDirection(c2.getFrom(), c2.getTo()) == LinkDirection::STRAIGHT) {
624
linkIndex2 = c2.getTLIndex();
625
break;
626
}
627
}
628
}
629
}
630
}
631
changeTLIndex(key, c.tlLinkIndex, linkIndex2, undoList);
632
}
633
undoList->end();
634
break;
635
case SUMO_ATTR_DIR:
636
throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
637
case SUMO_ATTR_STATE:
638
throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
639
default:
640
setCommonAttribute(key, value, undoList);
641
break;
642
}
643
}
644
645
646
void
647
GNEConnection::changeTLIndex(SumoXMLAttr key, int tlIndex, int tlIndex2, GNEUndoList* undoList) {
648
// trigger GNEChange_TLS
649
undoList->begin(this, "change tls linkIndex for connection");
650
// make a copy
651
std::set<NBTrafficLightDefinition*> defs = getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS();
652
for (const auto& tlDef : defs) {
653
NBLoadedSUMOTLDef* sumoDef = dynamic_cast<NBLoadedSUMOTLDef*>(tlDef);
654
NBTrafficLightLogic* tllogic = sumoDef ? sumoDef->getLogic() : tlDef->compute(OptionsCont::getOptions());
655
if (tllogic != nullptr) {
656
NBLoadedSUMOTLDef* newDef = new NBLoadedSUMOTLDef(*tlDef, *tllogic);
657
newDef->addConnection(getParentEdges().front()->getNBEdge(), getParentEdges().back()->getNBEdge(),
658
getLaneFrom()->getIndex(), getLaneTo()->getIndex(), tlIndex, tlIndex2, false);
659
// make a copy
660
std::vector<NBNode*> nodes = tlDef->getNodes();
661
for (const auto& node : nodes) {
662
GNEJunction* junction = getNet()->getAttributeCarriers()->retrieveJunction(node->getID());
663
undoList->add(new GNEChange_TLS(junction, tlDef, false), true);
664
undoList->add(new GNEChange_TLS(junction, newDef, true), true);
665
}
666
} else {
667
WRITE_ERRORF(TL("Could not set attribute '%' (tls is broken)"), toString(key));
668
}
669
}
670
undoList->end();
671
}
672
673
674
bool
675
GNEConnection::existNBEdgeConnection() const {
676
return getParentEdges().front()->getNBEdge()->getConnectionsFromLane(getFromLaneIndex(), getParentEdges().back()->getNBEdge(), getToLaneIndex()).size() > 0;
677
}
678
679
680
bool
681
GNEConnection::checkDrawConnection() const {
682
// declare a flag to check if shape has to be draw (by deafult false)
683
bool drawConnection = false;
684
// only draw connections if shape isn't deprecated
685
if (myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand() && myNet->getViewNet()->getNetworkViewOptions().showConnections()) {
686
drawConnection = !myShapeDeprecated;
687
} else if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {
688
if (myNet->getViewNet()->getNetworkViewOptions().showConnections() || isAttributeCarrierSelected()) {
689
drawConnection = !myShapeDeprecated;
690
} else {
691
drawConnection = false;
692
}
693
} else {
694
drawConnection = false;
695
}
696
// check if we're editing this connection
697
const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();
698
if (editedNetworkElement && (editedNetworkElement->getTagProperty()->getTag() == SUMO_TAG_CONNECTION)) {
699
if (editedNetworkElement->getAttribute(GNE_ATTR_PARENT) == getAttribute(GNE_ATTR_PARENT)) {
700
drawConnection = true;
701
}
702
}
703
return drawConnection;
704
}
705
706
707
RGBColor
708
GNEConnection::getConnectionColor(const GUIVisualizationSettings& s) const {
709
// check conditions
710
if (myShapeEdited) {
711
// return shape edit color
712
return s.colorSettings.editShapeColor;
713
} else if (drawUsingSelectColor()) {
714
// override with special colors (unless the color scheme is based on selection)
715
return s.colorSettings.selectedConnectionColor;
716
} else if (mySpecialColor != nullptr) {
717
// return special color
718
return *mySpecialColor;
719
} else {
720
// Set color depending of the link state
721
return GNEInternalLane::colorForLinksState(getLinkState());
722
}
723
}
724
725
726
void
727
GNEConnection::drawConnection(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
728
const GUIGeometry& superposedGeometry, const double exaggeration) const {
729
// get color
730
RGBColor connectionColor = getConnectionColor(s);
731
// Push layer matrix
732
GLHelper::pushMatrix();
733
// move top if is selected
734
if (mySelected) {
735
glTranslated(0, 0, 0.2);
736
}
737
// translate to front
738
if (myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() == this) {
739
drawInLayer(GLO_CONNECTION, 200);
740
} else {
741
drawInLayer(GLO_CONNECTION, 200);
742
}
743
// Set color
744
GLHelper::setColor(connectionColor);
745
// continue depending of detail level
746
if (d <= GUIVisualizationSettings::Detail::JunctionElementDetails) {
747
// draw geometry
748
GLHelper::drawBoxLines(superposedGeometry.getShape(), superposedGeometry.getShapeRotations(), superposedGeometry.getShapeLengths(),
749
s.connectionSettings.connectionWidth * exaggeration);
750
// draw arrows over connection
751
drawConnectionArrows(s, superposedGeometry, connectionColor);
752
// check if internal junction marker has to be drawn
753
if (myInternalJunctionMarkerGeometry.getShape().size() > 0) {
754
GLHelper::setColor(RGBColor::GREY);
755
GUIGeometry::drawGeometry(d, myInternalJunctionMarkerGeometry, s.connectionSettings.connectionWidth * exaggeration * 0.5);
756
}
757
// draw edge values
758
drawEdgeValues(s, superposedGeometry.getShape());
759
// draw shape points only in Network supemode
760
if (myShapeEdited && s.drawMovingGeometryPoint(1, s.neteditSizeSettings.connectionGeometryPointRadius)) {
761
// draw geometry points
762
GUIGeometry::drawGeometryPoints(d, superposedGeometry.getShape(), connectionColor.changedBrightness(-32),
763
s.neteditSizeSettings.connectionGeometryPointRadius, exaggeration,
764
myNet->getViewNet()->getNetworkViewOptions().editingElevation());
765
}
766
} else {
767
GLHelper::drawLine(superposedGeometry.getShape());
768
}
769
// Pop layer matrix
770
GLHelper::popMatrix();
771
}
772
773
774
void
775
GNEConnection::drawConnectionArrows(const GUIVisualizationSettings& s, const GUIGeometry& superposedGeometry,
776
const RGBColor& color) const {
777
if (s.showLaneDirection) {
778
// Push matrix
779
GLHelper::pushMatrix();
780
// move front
781
glTranslated(0, 0, 0.1);
782
// change color
783
GLHelper::setColor(color.changedBrightness(51));
784
// draw triangles
785
for (int i = 1; i < (int)superposedGeometry.getShape().size(); i++) {
786
const auto& posA = superposedGeometry.getShape()[i - 1];
787
const auto& posB = superposedGeometry.getShape()[i];
788
GLHelper::drawTriangleAtEnd(posA, posB, (double) .2, (double) .1);
789
}
790
// Pop matrix
791
GLHelper::popMatrix();
792
}
793
}
794
795
796
void
797
GNEConnection::drawEdgeValues(const GUIVisualizationSettings& s, const PositionVector& shape) const {
798
// check if edge value has to be shown
799
if (s.edgeValue.show(this)) {
800
const NBEdge::Connection& nbCon = getNBEdgeConnection();
801
const std::string value = nbCon.getParameter(s.edgeParam, "");
802
if (value != "") {
803
int shapeIndex = (int)shape.size() / 2;
804
const Position p = (myConnectionGeometry.getShape().size() == 2) ? (shape.front() * 0.67 + shape.back() * 0.33) : shape[shapeIndex];
805
GLHelper::drawTextSettings(s.edgeValue, value, p, s.scale, 0);
806
}
807
}
808
}
809
810
811
void
812
GNEConnection::calculateConnectionContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,
813
const PositionVector& shape, const double exaggeration) const {
814
// first check if junction parent was inserted with full boundary
815
if (!gViewObjectsHandler.checkBoundaryParentObject(this, getType(), getParentLanes().front()->getParentEdge()->getToJunction())) {
816
// calculate geometry points contour if we're editing shape
817
if (myShapeEdited) {
818
myNetworkElementContour.calculateContourAllGeometryPoints(s, d, this, shape, getType(), s.neteditSizeSettings.connectionGeometryPointRadius,
819
exaggeration, true);
820
} else {
821
// in move mode, add to selected object if this is the edited element
822
const auto& editModes = myNet->getViewNet()->getEditModes();
823
const bool addToSelectedObjects = (editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) ?
824
(myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() == this) : true;
825
// calculate connection shape contour
826
myNetworkElementContour.calculateContourExtrudedShape(s, d, this, shape, getType(), s.connectionSettings.connectionWidth, exaggeration,
827
true, true, 0, nullptr, getParentLanes().front()->getParentEdge()->getToJunction(), addToSelectedObjects);
828
}
829
}
830
}
831
832
833
bool
834
GNEConnection::isValid(SumoXMLAttr key, const std::string& value) {
835
// Currently ignored before implementation to avoid warnings
836
switch (key) {
837
case SUMO_ATTR_FROM:
838
case SUMO_ATTR_TO:
839
case SUMO_ATTR_FROM_LANE:
840
case SUMO_ATTR_TO_LANE:
841
return false;
842
case SUMO_ATTR_PASS:
843
return canParse<bool>(value);
844
case SUMO_ATTR_INDIRECT:
845
return canParse<bool>(value);
846
case SUMO_ATTR_TYPE:
847
return true;
848
case SUMO_ATTR_KEEP_CLEAR:
849
return canParse<bool>(value);
850
case SUMO_ATTR_CONTPOS:
851
return canParse<double>(value) && (parse<double>(value) >= -1);
852
case SUMO_ATTR_UNCONTROLLED:
853
return canParse<bool>(value);
854
case SUMO_ATTR_VISIBILITY_DISTANCE:
855
return canParse<double>(value) && (parse<double>(value) >= -1);
856
case SUMO_ATTR_TLLINKINDEX:
857
case SUMO_ATTR_TLLINKINDEX2:
858
if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) &&
859
!getNBEdgeConnection().uncontrolled &&
860
(getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().size() > 0) &&
861
canParse<int>(value) &&
862
(parse<int>(value) >= 0 || parse<int>(value) == -1)) {
863
// obtain Traffic light definition
864
NBTrafficLightDefinition* def = *getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().begin();
865
return def->getMaxValidIndex() >= parse<int>(value);
866
} else {
867
return false;
868
}
869
case SUMO_ATTR_ALLOW:
870
case SUMO_ATTR_DISALLOW:
871
case SUMO_ATTR_CHANGE_LEFT:
872
case SUMO_ATTR_CHANGE_RIGHT:
873
return canParseVehicleClasses(value);
874
case SUMO_ATTR_SPEED:
875
if (value.empty() || value == "default") {
876
return true;
877
} else {
878
return canParse<double>(value) && ((parse<double>(value) >= 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_SPEED));
879
}
880
case SUMO_ATTR_LENGTH:
881
return canParse<double>(value) && (parse<double>(value) >= -1);
882
case SUMO_ATTR_SHAPE:
883
case SUMO_ATTR_CUSTOMSHAPE:
884
// empty custom shapes are allowed
885
return canParse<PositionVector>(value);
886
case SUMO_ATTR_STATE:
887
return false;
888
case SUMO_ATTR_DIR:
889
return false;
890
default:
891
return isCommonAttributeValid(key, value);
892
}
893
}
894
895
896
bool
897
GNEConnection::isAttributeEnabled(SumoXMLAttr key) const {
898
switch (key) {
899
case SUMO_ATTR_FROM:
900
case SUMO_ATTR_TO:
901
case SUMO_ATTR_FROM_LANE:
902
case SUMO_ATTR_TO_LANE:
903
case SUMO_ATTR_DIR:
904
case SUMO_ATTR_STATE:
905
// this attributes cannot be edited
906
return false;
907
case SUMO_ATTR_TLLINKINDEX:
908
case SUMO_ATTR_TLLINKINDEX2:
909
// get Traffic Light definitions
910
if (getParentEdges().front()->getNBEdge()->getToNode()->isTLControlled()) {
911
NBTrafficLightDefinition* tlDef = *getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().begin();
912
NBLoadedSUMOTLDef* sumoDef = dynamic_cast<NBLoadedSUMOTLDef*>(tlDef);
913
NBTrafficLightLogic* tllogic = sumoDef != nullptr ? sumoDef->getLogic() : tlDef->compute(OptionsCont::getOptions());
914
if (tllogic != nullptr) {
915
return true;
916
} else {
917
return false;
918
}
919
}
920
return false;
921
default:
922
return true;
923
}
924
}
925
926
927
bool
928
GNEConnection::isAttributeComputed(SumoXMLAttr key) const {
929
switch (key) {
930
case SUMO_ATTR_SPEED:
931
return (getNBEdgeConnection().speed == NBEdge::UNSPECIFIED_SPEED);
932
default:
933
return false;
934
}
935
}
936
937
// ===========================================================================
938
// private
939
// ===========================================================================
940
941
void
942
GNEConnection::setAttribute(SumoXMLAttr key, const std::string& value) {
943
if (!existNBEdgeConnection()) {
944
WRITE_WARNINGF(TL("Cannot restore attribute '%=%' for computed connection from lane '%'"), toString(key), value, getParentLanes().front()->getID());
945
return;
946
}
947
NBEdge::Connection& nbCon = getNBEdgeConnection();
948
switch (key) {
949
case SUMO_ATTR_PASS:
950
nbCon.mayDefinitelyPass = parse<bool>(value);
951
break;
952
case SUMO_ATTR_INDIRECT:
953
nbCon.indirectLeft = parse<bool>(value);
954
break;
955
case SUMO_ATTR_KEEP_CLEAR:
956
if (value == toString(KEEPCLEAR_UNSPECIFIED)) {
957
nbCon.keepClear = KEEPCLEAR_UNSPECIFIED;
958
} else {
959
nbCon.keepClear = parse<bool>(value) ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE;
960
}
961
break;
962
case SUMO_ATTR_UNCONTROLLED:
963
nbCon.uncontrolled = parse<bool>(value);
964
break;
965
case SUMO_ATTR_CONTPOS:
966
nbCon.contPos = parse<double>(value);
967
break;
968
case SUMO_ATTR_VISIBILITY_DISTANCE:
969
nbCon.visibility = parse<double>(value);
970
break;
971
case SUMO_ATTR_SPEED:
972
if (value.empty() || (value == "default")) {
973
nbCon.speed = NBEdge::UNSPECIFIED_SPEED;
974
} else {
975
nbCon.speed = parse<double>(value);
976
}
977
break;
978
case SUMO_ATTR_LENGTH:
979
nbCon.customLength = parse<double>(value);
980
break;
981
case SUMO_ATTR_ALLOW:
982
nbCon.permissions = parseVehicleClasses(value);
983
break;
984
case SUMO_ATTR_DISALLOW:
985
nbCon.permissions = invertPermissions(parseVehicleClasses(value));
986
break;
987
case SUMO_ATTR_CHANGE_LEFT:
988
nbCon.changeLeft = value == "" ? SVC_UNSPECIFIED : parseVehicleClasses(value);
989
break;
990
case SUMO_ATTR_CHANGE_RIGHT:
991
nbCon.changeRight = value == "" ? SVC_UNSPECIFIED : parseVehicleClasses(value);
992
break;
993
case SUMO_ATTR_STATE:
994
throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
995
case SUMO_ATTR_DIR:
996
throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");
997
case SUMO_ATTR_SHAPE:
998
case SUMO_ATTR_CUSTOMSHAPE:
999
nbCon.customShape = parse<PositionVector>(value);
1000
break;
1001
case SUMO_ATTR_TYPE:
1002
nbCon.edgeType = value;
1003
break;
1004
default:
1005
setCommonAttribute(key, value);
1006
break;
1007
}
1008
// Update Geometry after setting a new attribute (but avoided for certain attributes)
1009
if ((key != SUMO_ATTR_ID) && (key != GNE_ATTR_PARAMETERS) && (key != GNE_ATTR_SELECTED)) {
1010
markConnectionGeometryDeprecated();
1011
updateGeometry();
1012
}
1013
// invalidate demand path calculator
1014
myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();
1015
}
1016
1017
/****************************************************************************/
1018
1019