Path: blob/main/src/netedit/elements/network/GNEConnection.cpp
185790 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file GNEConnection.cpp14/// @author Pablo Alvarez Lopez15/// @date Jun 201616///17// A class for visualizing connections between lanes18/****************************************************************************/1920#include <netbuild/NBLoadedSUMOTLDef.h>21#include <netedit/changes/GNEChange_Attribute.h>22#include <netedit/changes/GNEChange_TLS.h>23#include <netedit/elements/moving/GNEMoveElementConnection.h>24#include <netedit/GNENet.h>25#include <netedit/GNETagProperties.h>26#include <netedit/GNEUndoList.h>27#include <utils/common/MsgHandler.h>28#include <utils/gui/div/GLHelper.h>29#include <utils/gui/div/GUIDesigns.h>30#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>31#include <utils/gui/windows/GUIAppEnum.h>32#include <utils/options/OptionsCont.h>3334#include "GNEConnection.h"35#include "GNEInternalLane.h"3637// ===========================================================================38// method definitions39// ===========================================================================4041GNEConnection::GNEConnection(GNELane* from, GNELane* to) :42GNENetworkElement(from->getNet(), "from" + from->getID() + "to" + to->getID(), SUMO_TAG_CONNECTION),43myMoveElementConnection(new GNEMoveElementConnection(this)),44myLinkState(LINKSTATE_TL_OFF_NOSIGNAL),45mySpecialColor(nullptr),46myShapeDeprecated(true) {47// set parents48setParents<GNELane*>({from, to});49setParents<GNEEdge*>({from->getParentEdge(), to->getParentEdge()});50}515253GNEConnection::~GNEConnection() {54}555657GNEMoveElement*58GNEConnection::getMoveElement() const {59return myMoveElementConnection;60}616263Parameterised*64GNEConnection::getParameters() {65return &getNBEdgeConnection();66}676869const Parameterised*70GNEConnection::getParameters() const {71return &getNBEdgeConnection();7273}747576const PositionVector&77GNEConnection::getConnectionShape() const {78if (myConnectionGeometry.getShape().size() > 0) {79return myConnectionGeometry.getShape();80} else {81return getNBEdgeConnection().customShape;82}83}848586void87GNEConnection::updateGeometry() {88// check if adjust shape89if (myShapeDeprecated && existNBEdgeConnection()) {90// Get shape of from and to lanes91const NBEdge::Connection& nbCon = getNBEdgeConnection();92// obtain lane shapes93PositionVector laneShapeFrom = getParentLanes().front()->getLaneShape();94PositionVector laneShapeTo = getParentLanes().back()->getLaneShape();95// Calculate shape of connection depending of the size of Junction shape96if (nbCon.customShape.size() > 0) {97myConnectionGeometry.updateGeometry(nbCon.customShape);98} else if (nbCon.shape.size() > 1) {99PositionVector connectionShape;100if ((nbCon.shape.length() < 3) && !nbCon.haveVia) {101// apply offset to lane shape if we're in lane spread function center102if (getParentLanes().front()->getParentEdge()->getNBEdge()->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {103laneShapeFrom.move2side(0.3);104}105if (getParentLanes().back()->getParentEdge()->getNBEdge()->getLaneSpreadFunction() == LaneSpreadFunction::CENTER) {106laneShapeTo.move2side(0.3);107}108// check if this connetion is a turn around109bool turnAround = false;110const auto fromOppositeEdges = getParentLanes().front()->getParentEdge()->getOppositeEdges();111for (const auto& edge : fromOppositeEdges) {112if (edge == getParentLanes().back()->getParentEdge()) {113turnAround = true;114break;115}116}117// add from lane shape one step before118if (laneShapeFrom.length() > 1) {119// set length depending of turn arounds120if (turnAround) {121connectionShape.push_back(laneShapeFrom.positionAtOffset(laneShapeFrom.length() - 0.5));122} else {123connectionShape.push_back(laneShapeFrom.positionAtOffset(laneShapeFrom.length() - 1));124}125}126// add from lane shape127connectionShape.push_back(laneShapeFrom.back());128// add to lane shape129connectionShape.push_back(laneShapeTo.front());130// add to lane shape one step after131if (laneShapeTo.length() > 1) {132// set length depending of turn arounds133if (turnAround) {134connectionShape.push_back(laneShapeTo.positionAtOffset(0.5));135} else {136connectionShape.push_back(laneShapeTo.positionAtOffset(1));137}138}139} else {140connectionShape = nbCon.shape;141// only append via shape if it exists142if (nbCon.haveVia) {143connectionShape.append(nbCon.viaShape);144}145}146myConnectionGeometry.updateGeometry(connectionShape);147} else if (getParentLanes().front()->getLane2laneConnections().exist(getParentLanes().back())) {148myConnectionGeometry = getParentLanes().front()->getLane2laneConnections().getLane2laneGeometry(getParentLanes().back());149} else {150myConnectionGeometry.clearGeometry();151}152// check if internal junction marker must be calculated153if (nbCon.haveVia && (nbCon.shape.size() > 0)) {154// create marker for internal junction waiting position (contPos)155const double orthoLength = 0.5;156PositionVector internalJunctionMarker = nbCon.shape.getOrthogonal(nbCon.shape.back(), 10, true, 0.1);157if (internalJunctionMarker.length() < orthoLength) {158internalJunctionMarker.extrapolate(orthoLength - internalJunctionMarker.length());159}160myInternalJunctionMarkerGeometry.updateGeometry(internalJunctionMarker);161} else {162myInternalJunctionMarkerGeometry.clearGeometry();163}164// mark connection as non-deprecated165myShapeDeprecated = false;166}167}168169170Position171GNEConnection::getPositionInView() const {172// currently unused173return Position(0, 0);174}175176177bool178GNEConnection::checkDrawFromContour() const {179return false;180}181182183bool184GNEConnection::checkDrawToContour() const {185return false;186}187188189bool190GNEConnection::checkDrawRelatedContour() const {191// check opened popup192if (myNet->getViewNet()->getPopup()) {193return myNet->getViewNet()->getPopup()->getGLObject() == this;194}195return false;196}197198199bool200GNEConnection::checkDrawOverContour() const {201return false;202}203204205bool206GNEConnection::checkDrawDeleteContour() const {207// get edit modes208const auto& editModes = myNet->getViewNet()->getEditModes();209// check if we're in delete mode210if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {211return myNet->getViewNet()->checkOverLockedElement(this, mySelected);212} else {213return false;214}215}216217218bool219GNEConnection::checkDrawDeleteContourSmall() const {220return false;221}222223224bool225GNEConnection::checkDrawSelectContour() const {226// get edit modes227const auto& editModes = myNet->getViewNet()->getEditModes();228// check if we're in select mode229if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {230return myNet->getViewNet()->checkOverLockedElement(this, mySelected);231} else {232return false;233}234}235236237bool238GNEConnection::checkDrawMoveContour() const {239// get edit modes240const auto& editModes = myNet->getViewNet()->getEditModes();241// check if we're in move mode242if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&243(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {244// check if we're editing this network element245const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();246if (editedNetworkElement) {247return editedNetworkElement == this;248} else {249// only move the first element250return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;251}252} else {253return false;254}255}256257258GNEEdge*259GNEConnection::getEdgeFrom() const {260return getParentEdges().front();261}262263264GNEEdge*265GNEConnection::getEdgeTo() const {266return getParentEdges().back();267}268269270GNELane*271GNEConnection::getLaneFrom() const {272return getParentLanes().front();273}274275276GNELane*277GNEConnection::getLaneTo() const {278return getParentLanes().back();279}280281282int283GNEConnection::getFromLaneIndex() const {284return getParentLanes().front()->getIndex();285}286287288int289GNEConnection::getToLaneIndex() const {290return getParentLanes().back()->getIndex();291}292293294NBEdge::Connection&295GNEConnection::getNBEdgeConnection() const {296return getParentEdges().front()->getNBEdge()->getConnectionRef(getFromLaneIndex(), getParentEdges().back()->getNBEdge(), getToLaneIndex());297}298299300NBConnection301GNEConnection::getNBConnection() const {302const NBEdge::Connection& c = getNBEdgeConnection();303return NBConnection(getParentEdges().front()->getNBEdge(), getFromLaneIndex(),304getParentEdges().back()->getNBEdge(), getToLaneIndex(),305(int)c.tlLinkIndex, (int)c.tlLinkIndex2);306}307308309void310GNEConnection::updateConnectionID() {311setNetworkElementID(getParentLanes().front()->getID() + " -> " + getParentLanes().back()->getID());312}313314315LinkState316GNEConnection::getLinkState() const {317return myLinkState;318}319320321void322GNEConnection::markConnectionGeometryDeprecated() {323myShapeDeprecated = true;324}325326327void328GNEConnection::updateLinkState() {329const NBEdge::Connection& nbCon = getNBEdgeConnection();330myLinkState = getParentEdges().front()->getNBEdge()->getToNode()->getLinkState(getParentEdges().front()->getNBEdge(),331nbCon.toEdge,332nbCon.fromLane,333nbCon.toLane,334nbCon.mayDefinitelyPass,335nbCon.tlID);336}337338339void340GNEConnection::smootShape() {341auto shape = getConnectionShape();342shape = shape.bezier(5);343setAttribute(SUMO_ATTR_CUSTOMSHAPE, toString(shape), myNet->getUndoList());344}345346347GUIGLObjectPopupMenu*348GNEConnection::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {349if (myShapeEdited) {350return getShapeEditedPopUpMenu(app, parent, getNBEdgeConnection().customShape);351} else {352// create popup353GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);354// build common options355buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);356// check if we're in supermode network357if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {358// create menu commands359FXMenuCommand* mcCustomShape = GUIDesigns::buildFXMenuCommand(ret, TL("Set custom connection shape"), nullptr, &parent, MID_GNE_CONNECTION_EDIT_SHAPE);360GUIDesigns::buildFXMenuCommand(ret, TL("Smooth connection shape"), nullptr, &parent, MID_GNE_CONNECTION_SMOOTH_SHAPE);361// check if menu commands has to be disabled362NetworkEditMode editMode = myNet->getViewNet()->getEditModes().networkEditMode;363// check if we're in the correct edit mode364if ((editMode == NetworkEditMode::NETWORK_CONNECT) || (editMode == NetworkEditMode::NETWORK_TLS) || (editMode == NetworkEditMode::NETWORK_CREATE_EDGE)) {365mcCustomShape->disable();366}367}368return ret;369}370}371372373double374GNEConnection::getExaggeration(const GUIVisualizationSettings& s) const {375return s.addSize.getExaggeration(s, this);376}377378379Boundary380GNEConnection::getCenteringBoundary() const {381return myNetworkElementContour.getContourBoundary();382}383384385void386GNEConnection::updateCenteringBoundary(const bool /*updateGrid*/) {387// nothing to update388}389390391void392GNEConnection::drawGL(const GUIVisualizationSettings& s) const {393// Check if connection must be drawed394if (checkDrawConnection()) {395// get connection exaggeration396const double connectionExaggeration = isAttributeCarrierSelected() ? s.selectorFrameScale : 1;397// get detail level398const auto d = s.getDetailLevel(connectionExaggeration);399// check if draw shape superposed (used in train lanes)400PositionVector shapeSuperposed = myConnectionGeometry.getShape();401if (getParentLanes().front()->getDrawingConstants()->drawSuperposed()) {402shapeSuperposed.move2side(0.5);403}404GUIGeometry superposedGeometry(shapeSuperposed);405// draw geometry only if we'rent in drawForObjectUnderCursor mode406if (!s.drawForViewObjectsHandler) {407// draw connection408drawConnection(s, d, superposedGeometry, connectionExaggeration);409// draw lock icon410GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), 0.1);411// draw dotted contour depending if we're editing the custom shape412const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();413if (editedNetworkElement && (editedNetworkElement == this)) {414// draw dotted contour geometry points415myNetworkElementContour.drawDottedContourGeometryPoints(s, d, this, shapeSuperposed, s.neteditSizeSettings.connectionGeometryPointRadius,416connectionExaggeration, s.dottedContourSettings.segmentWidthSmall);417} else {418// draw dotted contour419myNetworkElementContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);420}421}422// calculate contour423calculateConnectionContour(s, d, shapeSuperposed, connectionExaggeration);424}425}426427428void429GNEConnection::deleteGLObject() {430myNet->deleteNetworkElement(this, myNet->getUndoList());431}432433434void435GNEConnection::updateGLObject() {436updateGeometry();437}438439440void441GNEConnection::setSpecialColor(const RGBColor* color) {442mySpecialColor = color;443}444445446std::string447GNEConnection::getAttribute(SumoXMLAttr key) const {448// first get attributes in which nbConnection reference can be invalid449switch (key) {450case SUMO_ATTR_ID:451return getMicrosimID();452case SUMO_ATTR_FROM:453return getParentLanes().front()->getParentEdge()->getID();454case SUMO_ATTR_TO:455return getParentLanes().back()->getParentEdge()->getID();456case SUMO_ATTR_FROM_LANE:457return getParentLanes().front()->getAttribute(SUMO_ATTR_INDEX);458case GNE_ATTR_FROM_LANEID:459return getParentLanes().front()->getID();460case SUMO_ATTR_TO_LANE:461return getParentLanes().back()->getAttribute(SUMO_ATTR_INDEX);462case GNE_ATTR_TO_LANEID:463return getParentLanes().back()->getID();464case GNE_ATTR_SELECTED:465case GNE_ATTR_FRONTELEMENT:466return getCommonAttribute(key);467case GNE_ATTR_PARENT:468return getParentEdges().front()->getToJunction()->getID();469default:470break;471}472// now continue with attributes that needs a nbConnection reference473const NBEdge::Connection& nbCon = getNBEdgeConnection();474switch (key) {475case SUMO_ATTR_PASS:476return toString(nbCon.mayDefinitelyPass);477case SUMO_ATTR_INDIRECT:478return toString(nbCon.indirectLeft);479case SUMO_ATTR_TYPE:480return toString(nbCon.edgeType);481case SUMO_ATTR_KEEP_CLEAR:482return toString(nbCon.keepClear);483case SUMO_ATTR_CONTPOS:484return toString(nbCon.contPos);485case SUMO_ATTR_UNCONTROLLED:486return toString(nbCon.uncontrolled);487case SUMO_ATTR_VISIBILITY_DISTANCE:488return toString(nbCon.visibility);489case SUMO_ATTR_TLLINKINDEX:490return toString(nbCon.tlLinkIndex);491case SUMO_ATTR_TLLINKINDEX2:492return toString(nbCon.tlLinkIndex2);493case SUMO_ATTR_ALLOW:494if (nbCon.permissions == SVC_UNSPECIFIED) {495return getVehicleClassNames(nbCon.toEdge->getLanes()[nbCon.toLane].permissions);496} else {497return getVehicleClassNames(nbCon.permissions);498}499case SUMO_ATTR_DISALLOW:500if (nbCon.permissions == SVC_UNSPECIFIED) {501return getVehicleClassNames(invertPermissions(nbCon.toEdge->getLanes()[nbCon.toLane].permissions));502} else {503return getVehicleClassNames(invertPermissions(nbCon.permissions));504}505case SUMO_ATTR_CHANGE_LEFT:506if (nbCon.changeLeft == SVC_UNSPECIFIED) {507return "all";508} else {509return getVehicleClassNames(nbCon.changeLeft);510}511case SUMO_ATTR_CHANGE_RIGHT:512if (nbCon.changeRight == SVC_UNSPECIFIED) {513return "all";514} else {515return getVehicleClassNames(nbCon.changeRight);516}517case SUMO_ATTR_SPEED:518if (nbCon.speed == NBEdge::UNSPECIFIED_SPEED) {519return "default";520} else {521return toString(nbCon.speed);522}523case SUMO_ATTR_LENGTH:524return toString(nbCon.customLength);525case SUMO_ATTR_DIR:526return toString(getParentEdges().front()->getNBEdge()->getToNode()->getDirection(527getParentEdges().front()->getNBEdge(), nbCon.toEdge, OptionsCont::getOptions().getBool("lefthand")));528case SUMO_ATTR_STATE:529return toString(getParentEdges().front()->getNBEdge()->getToNode()->getLinkState(530getParentEdges().front()->getNBEdge(), nbCon.toEdge, nbCon.fromLane, nbCon.toLane, nbCon.mayDefinitelyPass, nbCon.tlID));531case SUMO_ATTR_SHAPE:532case SUMO_ATTR_CUSTOMSHAPE:533return toString(nbCon.customShape);534default:535return getCommonAttribute(key);536}537}538539540double541GNEConnection::getAttributeDouble(SumoXMLAttr key) const {542return getCommonAttributeDouble(key);543}544545546Position547GNEConnection::getAttributePosition(SumoXMLAttr key) const {548return getCommonAttributePosition(key);549}550551552PositionVector553GNEConnection::getAttributePositionVector(SumoXMLAttr key) const {554switch (key) {555case SUMO_ATTR_SHAPE:556case SUMO_ATTR_CUSTOMSHAPE:557return getNBEdgeConnection().customShape;558default:559return getCommonAttributePositionVector(key);560}561}562563564void565GNEConnection::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {566const NBEdge::Connection& c = getNBEdgeConnection();567switch (key) {568case SUMO_ATTR_FROM:569case SUMO_ATTR_TO:570case SUMO_ATTR_FROM_LANE:571case SUMO_ATTR_TO_LANE:572case SUMO_ATTR_PASS:573case SUMO_ATTR_KEEP_CLEAR:574case SUMO_ATTR_CONTPOS:575case SUMO_ATTR_VISIBILITY_DISTANCE:576case SUMO_ATTR_ALLOW:577case SUMO_ATTR_DISALLOW:578case SUMO_ATTR_CHANGE_LEFT:579case SUMO_ATTR_CHANGE_RIGHT:580case SUMO_ATTR_SPEED:581case SUMO_ATTR_LENGTH:582case SUMO_ATTR_SHAPE:583case SUMO_ATTR_CUSTOMSHAPE:584case SUMO_ATTR_TYPE:585GNEChange_Attribute::changeAttribute(this, key, value, undoList);586break;587case SUMO_ATTR_TLLINKINDEX:588if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {589changeTLIndex(key, parse<int>(value), c.tlLinkIndex2, undoList);590}591break;592case SUMO_ATTR_TLLINKINDEX2:593if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {594changeTLIndex(key, c.tlLinkIndex, parse<int>(value), undoList);595}596break;597case SUMO_ATTR_UNCONTROLLED:598undoList->begin(this, "change attribute controlled for connection");599{600const bool wasUncontrolled = c.uncontrolled;601GNEChange_Attribute::changeAttribute(this, key, value, undoList);602if (wasUncontrolled && !c.uncontrolled) {603GNEEdge* srcEdge = getParentEdges().front();604NBConnection newNBCon(srcEdge->getNBEdge(), c.fromLane, c.toEdge, c.toLane);605srcEdge->getToJunction()->invalidateTLS(undoList, NBConnection::InvalidConnection, newNBCon);606}607}608undoList->end();609break;610case SUMO_ATTR_INDIRECT:611undoList->begin(this, "change attribute indirect for connection");612if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) && (value != getAttribute(key))) {613GNEChange_Attribute::changeAttribute(this, key, value, undoList);614int linkIndex2 = -1;615if (parse<bool>(value)) {616// find straight connection with the same toEdge617std::set<NBTrafficLightDefinition*> defs = getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS();618NBEdge* from = getParentEdges().front()->getNBEdge();619for (NBTrafficLightDefinition* tlDef : defs) {620for (const NBConnection& c2 : tlDef->getControlledLinks()) {621if (c2.getTo() == c.toEdge && c2.getFrom() != from) {622if (from->getToNode()->getDirection(c2.getFrom(), c2.getTo()) == LinkDirection::STRAIGHT) {623linkIndex2 = c2.getTLIndex();624break;625}626}627}628}629}630changeTLIndex(key, c.tlLinkIndex, linkIndex2, undoList);631}632undoList->end();633break;634case SUMO_ATTR_DIR:635throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");636case SUMO_ATTR_STATE:637throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");638default:639setCommonAttribute(key, value, undoList);640break;641}642}643644645void646GNEConnection::changeTLIndex(SumoXMLAttr key, int tlIndex, int tlIndex2, GNEUndoList* undoList) {647// trigger GNEChange_TLS648undoList->begin(this, "change tls linkIndex for connection");649// make a copy650std::set<NBTrafficLightDefinition*> defs = getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS();651for (const auto& tlDef : defs) {652NBLoadedSUMOTLDef* sumoDef = dynamic_cast<NBLoadedSUMOTLDef*>(tlDef);653NBTrafficLightLogic* tllogic = sumoDef ? sumoDef->getLogic() : tlDef->compute(OptionsCont::getOptions());654if (tllogic != nullptr) {655NBLoadedSUMOTLDef* newDef = new NBLoadedSUMOTLDef(*tlDef, *tllogic);656newDef->addConnection(getParentEdges().front()->getNBEdge(), getParentEdges().back()->getNBEdge(),657getLaneFrom()->getIndex(), getLaneTo()->getIndex(), tlIndex, tlIndex2, false);658// make a copy659std::vector<NBNode*> nodes = tlDef->getNodes();660for (const auto& node : nodes) {661GNEJunction* junction = getNet()->getAttributeCarriers()->retrieveJunction(node->getID());662undoList->add(new GNEChange_TLS(junction, tlDef, false), true);663undoList->add(new GNEChange_TLS(junction, newDef, true), true);664}665} else {666WRITE_ERRORF(TL("Could not set attribute '%' (tls is broken)"), toString(key));667}668}669undoList->end();670}671672673bool674GNEConnection::existNBEdgeConnection() const {675return getParentEdges().front()->getNBEdge()->getConnectionsFromLane(getFromLaneIndex(), getParentEdges().back()->getNBEdge(), getToLaneIndex()).size() > 0;676}677678679bool680GNEConnection::checkDrawConnection() const {681// declare a flag to check if shape has to be draw (by deafult false)682bool drawConnection = false;683// only draw connections if shape isn't deprecated684if (myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand() && myNet->getViewNet()->getNetworkViewOptions().showConnections()) {685drawConnection = !myShapeDeprecated;686} else if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {687if (myNet->getViewNet()->getNetworkViewOptions().showConnections() || isAttributeCarrierSelected()) {688drawConnection = !myShapeDeprecated;689} else {690drawConnection = false;691}692} else {693drawConnection = false;694}695// check if we're editing this connection696const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();697if (editedNetworkElement && (editedNetworkElement->getTagProperty()->getTag() == SUMO_TAG_CONNECTION)) {698if (editedNetworkElement->getAttribute(GNE_ATTR_PARENT) == getAttribute(GNE_ATTR_PARENT)) {699drawConnection = true;700}701}702return drawConnection;703}704705706RGBColor707GNEConnection::getConnectionColor(const GUIVisualizationSettings& s) const {708// check conditions709if (myShapeEdited) {710// return shape edit color711return s.colorSettings.editShapeColor;712} else if (drawUsingSelectColor()) {713// override with special colors (unless the color scheme is based on selection)714return s.colorSettings.selectedConnectionColor;715} else if (mySpecialColor != nullptr) {716// return special color717return *mySpecialColor;718} else {719// Set color depending of the link state720return GNEInternalLane::colorForLinksState(getLinkState());721}722}723724725void726GNEConnection::drawConnection(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,727const GUIGeometry& superposedGeometry, const double exaggeration) const {728// get color729RGBColor connectionColor = getConnectionColor(s);730// Push layer matrix731GLHelper::pushMatrix();732// move top if is selected733if (mySelected) {734glTranslated(0, 0, 0.2);735}736// translate to front737if (myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() == this) {738drawInLayer(GLO_CONNECTION, 200);739} else {740drawInLayer(GLO_CONNECTION, 200);741}742// Set color743GLHelper::setColor(connectionColor);744// continue depending of detail level745if (d <= GUIVisualizationSettings::Detail::JunctionElementDetails) {746// draw geometry747GLHelper::drawBoxLines(superposedGeometry.getShape(), superposedGeometry.getShapeRotations(), superposedGeometry.getShapeLengths(),748s.connectionSettings.connectionWidth * exaggeration);749// draw arrows over connection750drawConnectionArrows(s, superposedGeometry, connectionColor);751// check if internal junction marker has to be drawn752if (myInternalJunctionMarkerGeometry.getShape().size() > 0) {753GLHelper::setColor(RGBColor::GREY);754GUIGeometry::drawGeometry(d, myInternalJunctionMarkerGeometry, s.connectionSettings.connectionWidth * exaggeration * 0.5);755}756// draw edge values757drawEdgeValues(s, superposedGeometry.getShape());758// draw shape points only in Network supemode759if (myShapeEdited && s.drawMovingGeometryPoint(1, s.neteditSizeSettings.connectionGeometryPointRadius)) {760// draw geometry points761GUIGeometry::drawGeometryPoints(d, superposedGeometry.getShape(), connectionColor.changedBrightness(-32),762s.neteditSizeSettings.connectionGeometryPointRadius, exaggeration,763myNet->getViewNet()->getNetworkViewOptions().editingElevation());764}765} else {766GLHelper::drawLine(superposedGeometry.getShape());767}768// Pop layer matrix769GLHelper::popMatrix();770}771772773void774GNEConnection::drawConnectionArrows(const GUIVisualizationSettings& s, const GUIGeometry& superposedGeometry,775const RGBColor& color) const {776if (s.showLaneDirection) {777// Push matrix778GLHelper::pushMatrix();779// move front780glTranslated(0, 0, 0.1);781// change color782GLHelper::setColor(color.changedBrightness(51));783// draw triangles784for (int i = 1; i < (int)superposedGeometry.getShape().size(); i++) {785const auto& posA = superposedGeometry.getShape()[i - 1];786const auto& posB = superposedGeometry.getShape()[i];787GLHelper::drawTriangleAtEnd(posA, posB, (double) .2, (double) .1);788}789// Pop matrix790GLHelper::popMatrix();791}792}793794795void796GNEConnection::drawEdgeValues(const GUIVisualizationSettings& s, const PositionVector& shape) const {797// check if edge value has to be shown798if (s.edgeValue.show(this)) {799const NBEdge::Connection& nbCon = getNBEdgeConnection();800const std::string value = nbCon.getParameter(s.edgeParam, "");801if (value != "") {802int shapeIndex = (int)shape.size() / 2;803const Position p = (myConnectionGeometry.getShape().size() == 2) ? (shape.front() * 0.67 + shape.back() * 0.33) : shape[shapeIndex];804GLHelper::drawTextSettings(s.edgeValue, value, p, s.scale, 0);805}806}807}808809810void811GNEConnection::calculateConnectionContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,812const PositionVector& shape, const double exaggeration) const {813// first check if junction parent was inserted with full boundary814if (!gViewObjectsHandler.checkBoundaryParentObject(this, getType(), getParentLanes().front()->getParentEdge()->getToJunction())) {815// calculate geometry points contour if we're editing shape816if (myShapeEdited) {817myNetworkElementContour.calculateContourAllGeometryPoints(s, d, this, shape, getType(), s.neteditSizeSettings.connectionGeometryPointRadius,818exaggeration, true);819} else {820// in move mode, add to selected object if this is the edited element821const auto& editModes = myNet->getViewNet()->getEditModes();822const bool addToSelectedObjects = (editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) ?823(myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() == this) : true;824// calculate connection shape contour825myNetworkElementContour.calculateContourExtrudedShape(s, d, this, shape, getType(), s.connectionSettings.connectionWidth, exaggeration,826true, true, 0, nullptr, getParentLanes().front()->getParentEdge()->getToJunction(), addToSelectedObjects);827}828}829}830831832bool833GNEConnection::isValid(SumoXMLAttr key, const std::string& value) {834// Currently ignored before implementation to avoid warnings835switch (key) {836case SUMO_ATTR_FROM:837case SUMO_ATTR_TO:838case SUMO_ATTR_FROM_LANE:839case SUMO_ATTR_TO_LANE:840return false;841case SUMO_ATTR_PASS:842return canParse<bool>(value);843case SUMO_ATTR_INDIRECT:844return canParse<bool>(value);845case SUMO_ATTR_TYPE:846return true;847case SUMO_ATTR_KEEP_CLEAR:848return canParse<bool>(value);849case SUMO_ATTR_CONTPOS:850return canParse<double>(value) && (parse<double>(value) >= -1);851case SUMO_ATTR_UNCONTROLLED:852return canParse<bool>(value);853case SUMO_ATTR_VISIBILITY_DISTANCE:854return canParse<double>(value) && (parse<double>(value) >= -1);855case SUMO_ATTR_TLLINKINDEX:856case SUMO_ATTR_TLLINKINDEX2:857if (isAttributeEnabled(SUMO_ATTR_TLLINKINDEX) &&858!getNBEdgeConnection().uncontrolled &&859(getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().size() > 0) &&860canParse<int>(value) &&861(parse<int>(value) >= 0 || parse<int>(value) == -1)) {862// obtain Traffic light definition863NBTrafficLightDefinition* def = *getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().begin();864return def->getMaxValidIndex() >= parse<int>(value);865} else {866return false;867}868case SUMO_ATTR_ALLOW:869case SUMO_ATTR_DISALLOW:870case SUMO_ATTR_CHANGE_LEFT:871case SUMO_ATTR_CHANGE_RIGHT:872return canParseVehicleClasses(value);873case SUMO_ATTR_SPEED:874if (value.empty() || value == "default") {875return true;876} else {877return canParse<double>(value) && ((parse<double>(value) >= 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_SPEED));878}879case SUMO_ATTR_LENGTH:880return canParse<double>(value) && (parse<double>(value) >= -1);881case SUMO_ATTR_SHAPE:882case SUMO_ATTR_CUSTOMSHAPE:883// empty custom shapes are allowed884return canParse<PositionVector>(value);885case SUMO_ATTR_STATE:886return false;887case SUMO_ATTR_DIR:888return false;889default:890return isCommonAttributeValid(key, value);891}892}893894895bool896GNEConnection::isAttributeEnabled(SumoXMLAttr key) const {897switch (key) {898case SUMO_ATTR_FROM:899case SUMO_ATTR_TO:900case SUMO_ATTR_FROM_LANE:901case SUMO_ATTR_TO_LANE:902case SUMO_ATTR_DIR:903case SUMO_ATTR_STATE:904// this attributes cannot be edited905return false;906case SUMO_ATTR_TLLINKINDEX:907case SUMO_ATTR_TLLINKINDEX2:908// get Traffic Light definitions909if (getParentEdges().front()->getNBEdge()->getToNode()->isTLControlled()) {910NBTrafficLightDefinition* tlDef = *getParentEdges().front()->getNBEdge()->getToNode()->getControllingTLS().begin();911NBLoadedSUMOTLDef* sumoDef = dynamic_cast<NBLoadedSUMOTLDef*>(tlDef);912NBTrafficLightLogic* tllogic = sumoDef != nullptr ? sumoDef->getLogic() : tlDef->compute(OptionsCont::getOptions());913if (tllogic != nullptr) {914return true;915} else {916return false;917}918}919return false;920default:921return true;922}923}924925926bool927GNEConnection::isAttributeComputed(SumoXMLAttr key) const {928switch (key) {929case SUMO_ATTR_SPEED:930return (getNBEdgeConnection().speed == NBEdge::UNSPECIFIED_SPEED);931default:932return false;933}934}935936// ===========================================================================937// private938// ===========================================================================939940void941GNEConnection::setAttribute(SumoXMLAttr key, const std::string& value) {942if (!existNBEdgeConnection()) {943WRITE_WARNINGF(TL("Cannot restore attribute '%=%' for computed connection from lane '%'"), toString(key), value, getParentLanes().front()->getID());944return;945}946NBEdge::Connection& nbCon = getNBEdgeConnection();947switch (key) {948case SUMO_ATTR_PASS:949nbCon.mayDefinitelyPass = parse<bool>(value);950break;951case SUMO_ATTR_INDIRECT:952nbCon.indirectLeft = parse<bool>(value);953break;954case SUMO_ATTR_KEEP_CLEAR:955if (value == toString(KEEPCLEAR_UNSPECIFIED)) {956nbCon.keepClear = KEEPCLEAR_UNSPECIFIED;957} else {958nbCon.keepClear = parse<bool>(value) ? KEEPCLEAR_TRUE : KEEPCLEAR_FALSE;959}960break;961case SUMO_ATTR_UNCONTROLLED:962nbCon.uncontrolled = parse<bool>(value);963break;964case SUMO_ATTR_CONTPOS:965nbCon.contPos = parse<double>(value);966break;967case SUMO_ATTR_VISIBILITY_DISTANCE:968nbCon.visibility = parse<double>(value);969break;970case SUMO_ATTR_SPEED:971if (value.empty() || (value == "default")) {972nbCon.speed = NBEdge::UNSPECIFIED_SPEED;973} else {974nbCon.speed = parse<double>(value);975}976break;977case SUMO_ATTR_LENGTH:978nbCon.customLength = parse<double>(value);979break;980case SUMO_ATTR_ALLOW:981nbCon.permissions = parseVehicleClasses(value);982break;983case SUMO_ATTR_DISALLOW:984nbCon.permissions = invertPermissions(parseVehicleClasses(value));985break;986case SUMO_ATTR_CHANGE_LEFT:987nbCon.changeLeft = value == "" ? SVC_UNSPECIFIED : parseVehicleClasses(value);988break;989case SUMO_ATTR_CHANGE_RIGHT:990nbCon.changeRight = value == "" ? SVC_UNSPECIFIED : parseVehicleClasses(value);991break;992case SUMO_ATTR_STATE:993throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");994case SUMO_ATTR_DIR:995throw InvalidArgument("Attribute of '" + toString(key) + "' cannot be modified");996case SUMO_ATTR_SHAPE:997case SUMO_ATTR_CUSTOMSHAPE:998nbCon.customShape = parse<PositionVector>(value);999break;1000case SUMO_ATTR_TYPE:1001nbCon.edgeType = value;1002break;1003default:1004setCommonAttribute(key, value);1005break;1006}1007// Update Geometry after setting a new attribute (but avoided for certain attributes)1008if ((key != SUMO_ATTR_ID) && (key != GNE_ATTR_PARAMETERS) && (key != GNE_ATTR_SELECTED)) {1009markConnectionGeometryDeprecated();1010updateGeometry();1011}1012// invalidate demand path calculator1013myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();1014}10151016/****************************************************************************/101710181019