Path: blob/main/src/netedit/elements/network/GNECrossing.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 GNECrossing.cpp14/// @author Jakob Erdmann15/// @date June 201116///17// A class for visualizing Inner Lanes (used when editing traffic lights)18/****************************************************************************/1920#include <netedit/changes/GNEChange_Attribute.h>21#include <netedit/elements/moving/GNEMoveElementCrossing.h>22#include <netedit/GNENet.h>23#include <netedit/GNETagProperties.h>24#include <utils/gui/div/GLHelper.h>25#include <utils/gui/div/GUIDesigns.h>26#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>27#include <utils/gui/windows/GUIAppEnum.h>2829#include "GNECrossing.h"3031// ===========================================================================32// method definitions33// ===========================================================================3435GNECrossing::GNECrossing(GNENet* net) :36GNENetworkElement(net, "", SUMO_TAG_CROSSING),37myMoveElementCrossing(new GNEMoveElementCrossing(this)),38myTemplateNBCrossing(new NBNode::Crossing(nullptr, {}, 0, false, 0, 0, {})) {39}404142GNECrossing::GNECrossing(GNEJunction* junction, std::vector<NBEdge*> crossingEdges) :43GNENetworkElement(junction->getNet(), junction->getNBNode()->getCrossing(crossingEdges)->id, SUMO_TAG_CROSSING),44myMoveElementCrossing(new GNEMoveElementCrossing(this)),45myCrossingEdges(crossingEdges),46myTemplateNBCrossing(nullptr) {47// set parent48setParent<GNEJunction*>(junction);49}505152GNECrossing::~GNECrossing() {53if (myTemplateNBCrossing) {54delete myTemplateNBCrossing;55}56}575859GNEMoveElement*60GNECrossing::getMoveElement() const {61return myMoveElementCrossing;62}636465Parameterised*66GNECrossing::getParameters() {67return myTemplateNBCrossing;68}697071const Parameterised*72GNECrossing::getParameters() const {73return myTemplateNBCrossing;74}757677bool78GNECrossing::isNetworkElementValid() const {79return getNBCrossing()->valid;80}818283std::string84GNECrossing::getNetworkElementProblem() const {85return TL("Crossing's edges don't support pedestrians");86}878889const PositionVector&90GNECrossing::getCrossingShape() const {91const auto crossing = getNBCrossing();92return (crossing->customShape.size() > 0) ? crossing->customShape : crossing->shape;93}949596void97GNECrossing::updateGeometry() {98const auto crossing = getNBCrossing();99// update crossing geometry100myCrossingGeometry.updateGeometry(crossing->customShape.size() > 0 ? crossing->customShape : crossing->shape);101}102103104Position105GNECrossing::getPositionInView() const {106// currently unused107return Position(0, 0);108}109110111bool112GNECrossing::checkDrawFromContour() const {113return false;114}115116117bool118GNECrossing::checkDrawToContour() const {119return false;120}121122123bool124GNECrossing::checkDrawRelatedContour() const {125// check opened popup126if (myNet->getViewNet()->getPopup()) {127return myNet->getViewNet()->getPopup()->getGLObject() == this;128}129return false;130}131132133bool134GNECrossing::checkDrawOverContour() const {135return false;136}137138139bool140GNECrossing::checkDrawDeleteContour() const {141// get edit modes142const auto& editModes = myNet->getViewNet()->getEditModes();143// check if we're in select mode144if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {145return myNet->getViewNet()->checkOverLockedElement(this, mySelected);146} else {147return false;148}149}150151152bool153GNECrossing::checkDrawDeleteContourSmall() const {154return false;155}156157158bool159GNECrossing::checkDrawSelectContour() const {160// get edit modes161const auto& editModes = myNet->getViewNet()->getEditModes();162// check if we're in select mode163if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {164return myNet->getViewNet()->checkOverLockedElement(this, mySelected);165} else {166return false;167}168}169170171bool172GNECrossing::checkDrawMoveContour() const {173// get edit modes174const auto& editModes = myNet->getViewNet()->getEditModes();175// check if we're in move mode176if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&177(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {178// check if we're editing this network element179const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();180if (editedNetworkElement) {181return editedNetworkElement == this;182} else {183// only move the first element184return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;185}186} else {187return false;188}189}190191192const std::vector<NBEdge*>&193GNECrossing::getCrossingEdges() const {194return myCrossingEdges;195}196197198NBNode::Crossing*199GNECrossing::getNBCrossing() const {200if (myTemplateNBCrossing) {201return myTemplateNBCrossing;202} else {203return getParentJunctions().front()->getNBNode()->getCrossing(myCrossingEdges);204}205}206207208void209GNECrossing::drawGL(const GUIVisualizationSettings& s) const {210// continue depending of drawCrossing flag211if (checkDrawCrossing(s)) {212// get NBCrossing213const auto NBCrossing = getNBCrossing();214// get scaling depending if attribute carrier is selected215const double crossingExaggeration = isAttributeCarrierSelected() ? s.selectorFrameScale : 1;216// get width217const double crossingWidth = NBCrossing->width * 0.5 * crossingExaggeration;218// get detail level219const auto d = s.getDetailLevel(crossingExaggeration);220// check if draw geometry221if (!s.drawForViewObjectsHandler) {222// draw crossing223drawCrossing(s, d, NBCrossing, crossingWidth, crossingExaggeration);224// draw TLS Links No225drawTLSLinkNo(s, NBCrossing);226// draw crossing name227if (s.cwaEdgeName.show(this)) {228drawName(myCrossingGeometry.getShape().getCentroid(), s.scale, s.edgeName, 0, true);229}230// draw lock icon231GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), 1);232// draw dotted contour depending if we're editing the custom shape233const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();234if (editedNetworkElement && (editedNetworkElement == this)) {235// draw dotted contour geometry points236myNetworkElementContour.drawDottedContourGeometryPoints(s, d, this, myCrossingGeometry.getShape(), s.neteditSizeSettings.crossingGeometryPointRadius,237crossingExaggeration, s.dottedContourSettings.segmentWidthSmall);238} else {239// draw dotted contour240myNetworkElementContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);241}242}243// calculate contour244calculateCrossingContour(s, d, crossingWidth, crossingExaggeration);245}246}247248249void GNECrossing::deleteGLObject() {250myNet->deleteNetworkElement(this, myNet->getUndoList());251}252253254void255GNECrossing::updateGLObject() {256updateGeometry();257}258259260void261GNECrossing::drawTLSLinkNo(const GUIVisualizationSettings& s, const NBNode::Crossing* crossing) const {262// check if draw263if (s.drawLinkTLIndex.show(getParentJunctions().front())) {264// push matrix265GLHelper::pushMatrix();266// move to GLO_Crossing267glTranslated(0, 0, GLO_CROSSING + 0.5);268// make a copy of shape269PositionVector shape = crossing->shape;270// extrapolate271shape.extrapolate(0.5); // draw on top of the walking area272// get link indexes273const int linkNo = crossing->tlLinkIndex;274const int linkNo2 = crossing->tlLinkIndex2 > 0 ? crossing->tlLinkIndex2 : linkNo;275// draw link indexes276GLHelper::drawTextAtEnd(toString(linkNo2), shape, 0, s.drawLinkTLIndex, s.scale);277GLHelper::drawTextAtEnd(toString(linkNo), shape.reverse(), 0, s.drawLinkTLIndex, s.scale);278// push matrix279GLHelper::popMatrix();280}281}282283284GUIGLObjectPopupMenu*285GNECrossing::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {286if (myShapeEdited) {287return getShapeEditedPopUpMenu(app, parent, getNBCrossing()->customShape);288} else {289GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);290// build common options291buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);292// check if we're in supermode network293if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {294// create menu commands295FXMenuCommand* mcCustomShape = GUIDesigns::buildFXMenuCommand(ret, TL("Set custom crossing shape"), nullptr, &parent, MID_GNE_CROSSING_EDIT_SHAPE);296// check if menu commands has to be disabled297NetworkEditMode editMode = myNet->getViewNet()->getEditModes().networkEditMode;298if ((editMode == NetworkEditMode::NETWORK_CONNECT) || (editMode == NetworkEditMode::NETWORK_TLS) || (editMode == NetworkEditMode::NETWORK_CREATE_EDGE)) {299mcCustomShape->disable();300}301}302return ret;303}304}305306307Boundary308GNECrossing::getCenteringBoundary() const {309return myNetworkElementContour.getContourBoundary();310}311312313void314GNECrossing::updateCenteringBoundary(const bool /*updateGrid*/) {315// nothing to update316}317318319std::string320GNECrossing::getAttribute(SumoXMLAttr key) const {321const auto crossing = getNBCrossing();322switch (key) {323case SUMO_ATTR_ID:324// get attribute requires a special case325if (crossing) {326return crossing->id;327} else {328return "Temporal Unreferenced";329}330case SUMO_ATTR_WIDTH:331return toString(crossing->customWidth);332case SUMO_ATTR_PRIORITY:333return crossing->priority ? "true" : "false";334case SUMO_ATTR_EDGES:335return toString(crossing->edges);336case SUMO_ATTR_TLLINKINDEX:337return toString(crossing->customTLIndex < 0 ? crossing->tlLinkIndex : crossing->customTLIndex);338case SUMO_ATTR_TLLINKINDEX2:339return toString(crossing->customTLIndex2 < 0 ? crossing->tlLinkIndex2 : crossing->customTLIndex2);340case SUMO_ATTR_SHAPE:341case SUMO_ATTR_CUSTOMSHAPE:342return toString(crossing->customShape);343default:344return getCommonAttribute(key);345}346}347348349double350GNECrossing::getAttributeDouble(SumoXMLAttr key) const {351return getCommonAttributeDouble(key);352}353354355Position356GNECrossing::getAttributePosition(SumoXMLAttr key) const {357return getCommonAttributePosition(key);358}359360361PositionVector362GNECrossing::getAttributePositionVector(SumoXMLAttr key) const {363switch (key) {364case SUMO_ATTR_SHAPE:365case SUMO_ATTR_CUSTOMSHAPE:366return getNBCrossing()->customShape;367default:368return getCommonAttributePositionVector(key);369}370}371372373void374GNECrossing::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {375if (value == getAttribute(key)) {376return; //avoid needless changes, later logic relies on the fact that attributes have changed377}378switch (key) {379case SUMO_ATTR_ID:380throw InvalidArgument("Modifying attribute '" + toString(key) + "' of " + getTagStr() + " isn't allowed");381case SUMO_ATTR_EDGES:382case SUMO_ATTR_WIDTH:383case SUMO_ATTR_PRIORITY:384case SUMO_ATTR_TLLINKINDEX:385case SUMO_ATTR_TLLINKINDEX2:386case SUMO_ATTR_SHAPE:387case SUMO_ATTR_CUSTOMSHAPE:388GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);389break;390default:391setCommonAttribute(key, value, undoList);392break;393}394}395396397bool398GNECrossing::isAttributeEnabled(SumoXMLAttr key) const {399switch (key) {400case SUMO_ATTR_ID:401// id isn't editable402return false;403case SUMO_ATTR_TLLINKINDEX:404case SUMO_ATTR_TLLINKINDEX2:405return (getNBCrossing()->tlID != "");406default:407return true;408}409}410411412bool413GNECrossing::isValid(SumoXMLAttr key, const std::string& value) {414const auto crossing = getNBCrossing();415switch (key) {416case SUMO_ATTR_ID:417return false;418case SUMO_ATTR_EDGES:419if (canParse<std::vector<GNEEdge*> >(myNet, value, false)) {420// parse edges and save their IDs in a set421std::vector<GNEEdge*> parsedEdges = parse<std::vector<GNEEdge*> >(myNet, value);422EdgeVector nbEdges;423for (const auto& edge : parsedEdges) {424nbEdges.push_back(edge->getNBEdge());425}426std::sort(nbEdges.begin(), nbEdges.end());427//428EdgeVector originalEdges = crossing->edges;429std::sort(originalEdges.begin(), originalEdges.end());430// return true if we're setting the same edges431if (toString(nbEdges) == toString(originalEdges)) {432return true;433} else {434return !getParentJunctions().front()->getNBNode()->checkCrossingDuplicated(nbEdges);435}436} else {437return false;438}439case SUMO_ATTR_WIDTH:440return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == -1)); // can not be 0, or -1 (it means default)441case SUMO_ATTR_PRIORITY:442return canParse<bool>(value);443case SUMO_ATTR_TLLINKINDEX:444case SUMO_ATTR_TLLINKINDEX2:445// -1 means that tlLinkIndex2 takes on the same value as tlLinkIndex when setting indices446return (isAttributeEnabled(key) &&447canParse<int>(value)448&& (parse<double>(value) >= 0 || parse<double>(value) == -1)449&& getParentJunctions().front()->getNBNode()->getControllingTLS().size() > 0450&& (*getParentJunctions().front()->getNBNode()->getControllingTLS().begin())->getMaxValidIndex() >= parse<int>(value));451case SUMO_ATTR_SHAPE:452case SUMO_ATTR_CUSTOMSHAPE:453// empty shapes are allowed454return canParse<PositionVector>(value);455default:456return isValid(key, value);457}458}459460461bool462GNECrossing::checkEdgeBelong(GNEEdge* edge) const {463const auto crossing = getNBCrossing();464if (std::find(crossing->edges.begin(), crossing->edges.end(), edge->getNBEdge()) != crossing->edges.end()) {465return true;466} else {467return false;468}469}470471472bool473GNECrossing::checkEdgeBelong(const std::vector<GNEEdge*>& edges) const {474for (auto i : edges) {475if (checkEdgeBelong(i)) {476return true;477}478}479return false;480}481482// ===========================================================================483// private484// ===========================================================================485486bool487GNECrossing::checkDrawCrossing(const GUIVisualizationSettings& s) const {488// don't draw in supermode data489if (myNet->getViewNet()->getEditModes().isCurrentSupermodeData()) {490return false;491}492// check shape rotations493if (myCrossingGeometry.getShapeRotations().empty()) {494return false;495}496// check shape lengths497if (myCrossingGeometry.getShapeLengths().empty()) {498return false;499}500return s.drawCrossingsAndWalkingareas;501}502503504void505GNECrossing::drawCrossing(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,506const NBNode::Crossing* crossing, const double width, const double exaggeration) const {507// don't draw crossing in TLS Mode508if (myNet->getViewNet()->getEditModes().networkEditMode != NetworkEditMode::NETWORK_TLS) {509// get color510RGBColor crossingColor = getCrossingColor(s, crossing);511// push layer matrix512GLHelper::pushMatrix();513// translate to front514drawInLayer(GLO_CROSSING);515// set color516GLHelper::setColor(crossingColor);517// draw depending of level of detail518if (d <= GUIVisualizationSettings::Detail::JunctionElementDetails) {519drawCrossingDetailed(width, exaggeration);520} else {521GUIGeometry::drawGeometry(d, myCrossingGeometry, width);522}523// draw shape points only in Network supermode524if (myShapeEdited && myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&525s.drawMovingGeometryPoint(exaggeration, s.neteditSizeSettings.crossingGeometryPointRadius)) {526// color527const RGBColor darkerColor = crossingColor.changedBrightness(-32);528// draw on top of of the white area between the rails529glTranslated(0, 0, 0.2);530// set color531GLHelper::setColor(darkerColor);532// draw shape533GUIGeometry::drawGeometry(d, myCrossingGeometry.getShape(), s.neteditSizeSettings.crossingGeometryPointRadius * 0.4);534// draw geometry points535GUIGeometry::drawGeometryPoints(d, myCrossingGeometry.getShape(), darkerColor,536s.neteditSizeSettings.crossingGeometryPointRadius, exaggeration,537myNet->getViewNet()->getNetworkViewOptions().editingElevation());538}539// pop layer matrix540GLHelper::popMatrix();541}542}543544545RGBColor546GNECrossing::getCrossingColor(const GUIVisualizationSettings& s, const NBNode::Crossing* crossing) const {547if (myShapeEdited) {548return s.colorSettings.editShapeColor;549} else if (drawUsingSelectColor()) {550return s.colorSettings.selectedCrossingColor;551} else if (!crossing->valid) {552return s.colorSettings.crossingInvalidColor;553} else if (crossing->priority) {554return s.colorSettings.crossingPriorityColor;555} else if (myNet->getViewNet()->getEditModes().isCurrentSupermodeData()) {556return s.laneColorer.getSchemes()[0].getColor(8);557} else {558return s.colorSettings.crossingColor;559}560}561562563void564GNECrossing::drawCrossingDetailed(const double width, const double exaggeration) const {565// get length and spacing566const double length = 0.5 * exaggeration;567const double spacing = 1.0 * exaggeration;568// push rail matrix569GLHelper::pushMatrix();570// draw on top of of the white area between the rails571glTranslated(0, 0, 0.1);572for (int i = 0; i < (int)myCrossingGeometry.getShape().size() - 1; i++) {573// push draw matrix574GLHelper::pushMatrix();575// translate and rotate576glTranslated(myCrossingGeometry.getShape()[i].x(), myCrossingGeometry.getShape()[i].y(), 0.0);577glRotated(myCrossingGeometry.getShapeRotations()[i], 0, 0, 1);578// draw crossing depending if isn't being drawn for selecting579for (double t = 0; t < myCrossingGeometry.getShapeLengths()[i]; t += spacing) {580glBegin(GL_QUADS);581glVertex2d(-width, -t);582glVertex2d(-width, -t - length);583glVertex2d(width, -t - length);584glVertex2d(width, -t);585glEnd();586}587// pop draw matrix588GLHelper::popMatrix();589}590// pop rail matrix591GLHelper::popMatrix();592}593594595void596GNECrossing::calculateCrossingContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,597const double width, const double exaggeration) const {598// first check if junction parent was inserted with full boundary599if (!gViewObjectsHandler.checkBoundaryParentObject(this, getType(), getParentJunctions().front())) {600// check if calculate contour for geometry points601if (myShapeEdited) {602myNetworkElementContour.calculateContourAllGeometryPoints(s, d, this, myCrossingGeometry.getShape(),603getType(), s.neteditSizeSettings.crossingGeometryPointRadius, exaggeration, true);604} else {605// in move mode, add to selected object if this is the edited element606const auto& editModes = myNet->getViewNet()->getEditModes();607const bool addToSelectedObjects = (editModes.isCurrentSupermodeNetwork() && editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) ?608(myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() == this) : true;609// calculate contour and610myNetworkElementContour.calculateContourExtrudedShape(s, d, this, myCrossingGeometry.getShape(), getType(),611width, exaggeration, true, true, 0, nullptr, getParentJunctions().front(), addToSelectedObjects);612}613}614}615616617void618GNECrossing::setAttribute(SumoXMLAttr key, const std::string& value) {619const auto crossing = getNBCrossing();620switch (key) {621case SUMO_ATTR_ID:622throw InvalidArgument("Modifying attribute '" + toString(key) + "' of " + getTagStr() + " isn't allowed");623case SUMO_ATTR_EDGES: {624// obtain GNEEdges625std::vector<GNEEdge*> edges = parse<std::vector<GNEEdge*> >(myNet, value);626// remove NBEdges of crossing627crossing->edges.clear();628// set NBEdge of every GNEEdge into Crossing Edges629for (auto i : edges) {630crossing->edges.push_back(i->getNBEdge());631}632// sort new edges633std::sort(crossing->edges.begin(), crossing->edges.end());634// change myCrossingEdges by the new edges635myCrossingEdges = crossing->edges;636// update geometry of parent junction637getParentJunctions().front()->updateGeometry();638break;639}640case SUMO_ATTR_WIDTH:641// Change width an refresh element642crossing->customWidth = parse<double>(value);643break;644case SUMO_ATTR_PRIORITY:645crossing->priority = parse<bool>(value);646break;647case SUMO_ATTR_TLLINKINDEX:648crossing->customTLIndex = parse<int>(value);649// make new value visible immediately650crossing->tlLinkIndex = crossing->customTLIndex;651break;652case SUMO_ATTR_TLLINKINDEX2:653crossing->customTLIndex2 = parse<int>(value);654// make new value visible immediately655crossing->tlLinkIndex2 = crossing->customTLIndex2;656break;657case SUMO_ATTR_SHAPE:658case SUMO_ATTR_CUSTOMSHAPE:659// set custom shape660crossing->customShape = parse<PositionVector>(value);661break;662default:663setCommonAttribute(key, value);664break;665}666// Crossing are a special case and we need ot update geometry of junction instead of crossing667if ((getParentJunctions().size() > 0) && (key != SUMO_ATTR_ID) && (key != GNE_ATTR_PARAMETERS) && (key != GNE_ATTR_SELECTED)) {668getParentJunctions().front()->updateGeometry();669}670// invalidate demand path calculator671myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();672}673674/****************************************************************************/675676677