Path: blob/main/src/netedit/elements/network/GNEJunction.cpp
193716 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 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 GNEJunction.cpp14/// @author Jakob Erdmann15/// @date Feb 201116///17// A class for visualizing and editing junctions in netedit (adapted from18// GUIJunctionWrapper)19/****************************************************************************/2021#include <netbuild/NBAlgorithms.h>22#include <netbuild/NBLoadedSUMOTLDef.h>23#include <netbuild/NBNetBuilder.h>24#include <netbuild/NBOwnTLDef.h>25#include <netedit/changes/GNEChange_Attribute.h>26#include <netedit/changes/GNEChange_Connection.h>27#include <netedit/changes/GNEChange_TLS.h>28#include <netedit/elements/demand/GNEPlanParents.h>29#include <netedit/elements/moving/GNEMoveElementJunction.h>30#include <netedit/frames/common/GNEDeleteFrame.h>31#include <netedit/frames/common/GNEMoveFrame.h>32#include <netedit/frames/demand/GNEContainerFrame.h>33#include <netedit/frames/demand/GNEContainerPlanFrame.h>34#include <netedit/frames/demand/GNEPersonFrame.h>35#include <netedit/frames/demand/GNEPersonPlanFrame.h>36#include <netedit/frames/demand/GNEVehicleFrame.h>37#include <netedit/frames/network/GNECreateEdgeFrame.h>38#include <netedit/frames/network/GNECrossingFrame.h>39#include <netedit/frames/network/GNETLSEditorFrame.h>40#include <netedit/GNENet.h>41#include <netedit/GNEApplicationWindow.h>42#include <netedit/GNEUndoList.h>43#include <netedit/GNEViewParent.h>44#include <utils/gui/div/GLHelper.h>45#include <utils/gui/div/GUIDesigns.h>46#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>47#include <utils/gui/images/GUITextureSubSys.h>48#include <utils/gui/windows/GUIAppEnum.h>49#include <utils/options/OptionsCont.h>5051#include "GNEConnection.h"52#include "GNEJunction.h"53#include "GNECrossing.h"54#include "GNEWalkingArea.h"55#include "GNEInternalLane.h"5657// ===========================================================================58// method definitions59// ===========================================================================6061GNEJunction::GNEJunction(GNENet* net, NBNode* nbn, bool loaded) :62GNENetworkElement(net, nbn->getID(), SUMO_TAG_JUNCTION),63myMoveElementJunction(new GNEMoveElementJunction(this)),64myNBNode(nbn),65myDrawingToggle(new int),66myLogicStatus(loaded ? FEATURE_LOADED : FEATURE_GUESSED),67myHasValidLogic(loaded),68myTesselation(nbn->getID(), "", RGBColor::MAGENTA, nbn->getShape(), false, true, 0) {69// update centering boundary without updating grid70updateCenteringBoundary(false);71}727374GNEJunction::~GNEJunction() {75// delete drawing toggle76delete myDrawingToggle;77// delete all GNECrossing78for (const auto& crossing : myGNECrossings) {79crossing->decRef();80if (crossing->unreferenced()) {81// check if remove it from Attribute Carriers82if (myNet->getAttributeCarriers()->getCrossings().count(crossing) > 0) {83myNet->getAttributeCarriers()->deleteCrossing(crossing);84}85delete crossing;86}87}88// delete all GNEWalkingArea89for (const auto& walkingArea : myGNEWalkingAreas) {90walkingArea->decRef();91if (walkingArea->unreferenced()) {92// check if remove it from Attribute Carriers93if (myNet->getAttributeCarriers()->getWalkingAreas().count(walkingArea) > 0) {94myNet->getAttributeCarriers()->deleteWalkingArea(walkingArea);95}96delete walkingArea;97}98}99if (myAmResponsible) {100delete myNBNode;101}102}103104105GNEMoveElement*106GNEJunction::getMoveElement() const {107return myMoveElementJunction;108}109110111Parameterised*112GNEJunction::getParameters() {113return myNBNode;114}115116117const Parameterised*118GNEJunction::getParameters() const {119return myNBNode;120}121122123const PositionVector&124GNEJunction::getJunctionShape() const {125return myNBNode->getShape();126}127128129void130GNEJunction::updateGeometry() {131updateGeometryAfterNetbuild(true);132// trigger rebuilding tesselation133myExaggeration = 2;134}135136137void138GNEJunction::updateGeometryAfterNetbuild(bool rebuildNBNodeCrossings) {139// rebuild crossings140rebuildGNECrossings(rebuildNBNodeCrossings);141// clear walking areas142clearWalkingAreas();143// clear missing connections144checkMissingConnections();145}146147148Position149GNEJunction::getPositionInView() const {150return myNBNode->getPosition();151}152153154bool155GNEJunction::checkDrawFromContour() const {156// get modes and viewParent (for code legibility)157const auto& modes = myNet->getViewNet()->getEditModes();158const auto& viewParent = myNet->getViewParent();159const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();160// continue depending of current status161if (inspectedElements.isInspectingSingleElement()) {162const auto inspectedAC = inspectedElements.getFirstAC();163// check if starts in this junction164if (inspectedAC->hasAttribute(SUMO_ATTR_FROM_JUNCTION) &&165(inspectedAC->getAttribute(SUMO_ATTR_FROM_JUNCTION) == getID())) {166return true;167} else if ((inspectedAC->getTagProperty()->getTag() == SUMO_TAG_EDGE) &&168(inspectedAC->getAttribute(SUMO_ATTR_FROM) == getID())) {169return true;170} else if ((inspectedAC->getTagProperty()->getTag() == SUMO_TAG_LANE) &&171(inspectedAC->getAttribute(SUMO_ATTR_FROM_JUNCTION) == getID())) {172return true;173}174} else if (modes.isCurrentSupermodeNetwork()) {175if (modes.networkEditMode == NetworkEditMode::NETWORK_CREATE_EDGE) {176if (viewParent->getCreateEdgeFrame()->getJunctionSource()) {177return viewParent->getCreateEdgeFrame()->getJunctionSource() == this;178} else {179return myNet->getViewNet()->getViewObjectsSelector().getJunctionFront() == this;180}181} else if ((modes.networkEditMode == NetworkEditMode::NETWORK_TLS) &&182viewParent->getTLSEditorFrame()->getTLSJunction()->isJoiningJunctions()) {183for (const auto& id : viewParent->getTLSEditorFrame()->getTLSJunction()->getSelectedJunctionIDs()) {184if (id == getMicrosimID()) {185return true;186}187}188}189} else if (modes.isCurrentSupermodeDemand()) {190// get current GNEPlanCreator191GNEPlanCreator* planCreator = nullptr;192if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {193planCreator = viewParent->getPersonFrame()->getPlanCreator();194} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {195planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();196} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {197planCreator = viewParent->getContainerFrame()->getPlanCreator();198} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {199planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();200}201// continue depending of planCreator202if (planCreator) {203if (planCreator->getPlanParameteres().fromJunction == getID()) {204return true;205}206} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {207const auto& selectedJunctions = viewParent->getVehicleFrame()->getPathCreator()->getSelectedJunctions();208// check if this is the first selected junction209if ((selectedJunctions.size() > 0) && (selectedJunctions.front() == this)) {210return true;211}212}213}214// nothing to draw215return false;216}217218219bool220GNEJunction::checkDrawToContour() const {221// get modes and viewParent (for code legibility)222const auto& modes = myNet->getViewNet()->getEditModes();223const auto& viewParent = myNet->getViewParent();224const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();225// continue depending of current status226if (inspectedElements.isInspectingSingleElement()) {227const auto inspectedAC = inspectedElements.getFirstAC();228// check if ends in this junction229if (inspectedAC->getTagProperty()->vehicleJunctions() &&230(inspectedAC->getAttribute(SUMO_ATTR_TO_JUNCTION) == getID())) {231return true;232} else if ((inspectedAC->getTagProperty()->getTag() == SUMO_TAG_EDGE) &&233(inspectedAC->getAttribute(SUMO_ATTR_TO) == getID())) {234return true;235} else if ((inspectedAC->getTagProperty()->getTag() == SUMO_TAG_LANE) &&236(inspectedAC->getAttribute(SUMO_ATTR_TO_JUNCTION) == getID())) {237return true;238}239} else if (modes.isCurrentSupermodeNetwork()) {240if (modes.networkEditMode == NetworkEditMode::NETWORK_CREATE_EDGE) {241if (viewParent->getCreateEdgeFrame()->getJunctionSource() &&242(viewParent->getCreateEdgeFrame()->getJunctionSource() != this)) {243return myNet->getViewNet()->getViewObjectsSelector().getJunctionFront() == this;244}245} else if (modes.networkEditMode == NetworkEditMode::NETWORK_MOVE) {246// check if we're moving a junction247const auto moveElementJunction = dynamic_cast<GNEMoveElementJunction*>(myNet->getViewNet()->getMoveSingleElementValues().getMovedElement());248if (moveElementJunction && (moveElementJunction->getJunction() != this)) {249// continue depending of junction shape250if (myNBNode->getShape().area() < 4) {251// calculate distance between both centers252const double junctionBubbleRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.junctionBubbleRadius;253const double radiusTo = getExaggeration(myNet->getViewNet()->getVisualisationSettings()) * junctionBubbleRadius;254if (myNBNode->getPosition().distanceSquaredTo2D(moveElementJunction->getJunction()->getPositionInView()) < (radiusTo * radiusTo)) {255// add both it in the list of merging junction256gViewObjectsHandler.addMergingJunctions(moveElementJunction->getJunction());257gViewObjectsHandler.addMergingJunctions(this);258return true;259}260} else if (myNBNode->getShape().around(moveElementJunction->getJunction()->getNBNode()->getPosition())) {261// add both it in the list of merging junction262gViewObjectsHandler.addMergingJunctions(moveElementJunction->getJunction());263gViewObjectsHandler.addMergingJunctions(this);264return true;265}266}267}268} else if (modes.isCurrentSupermodeDemand()) {269// get current GNEPlanCreator270GNEPlanCreator* planCreator = nullptr;271if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {272planCreator = viewParent->getPersonFrame()->getPlanCreator();273} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {274planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();275} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {276planCreator = viewParent->getContainerFrame()->getPlanCreator();277} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {278planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();279}280// continue depending of planCreator281if (planCreator) {282if (planCreator->getPlanParameteres().toJunction == getID()) {283return true;284}285} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {286const auto& selectedJunctions = viewParent->getVehicleFrame()->getPathCreator()->getSelectedJunctions();287// check if this is the first selected junction288if ((selectedJunctions.size() > 1) && (selectedJunctions.back() == this)) {289return true;290}291}292}293// nothing to draw294return false;295}296297298bool299GNEJunction::checkDrawRelatedContour() const {300if (myNet->getViewParent()->getCrossingFrame()->getEdgesSelector()->getCurrentJunction() == this) {301return true;302}303// check opened popup304if (myNet->getViewNet()->getPopup()) {305return myNet->getViewNet()->getPopup()->getGLObject() == this;306}307return false;308}309310311bool312GNEJunction::checkDrawOverContour() const {313// get modes and viewParent (for code legibility)314const auto& modes = myNet->getViewNet()->getEditModes();315const auto& viewParent = myNet->getViewParent();316const auto& viewObjectsSelector = myNet->getViewNet()->getViewObjectsSelector();317if (viewObjectsSelector.getJunctionFront() != this) {318return false;319} else {320if (modes.isCurrentSupermodeNetwork()) {321if (modes.networkEditMode == NetworkEditMode::NETWORK_CROSSING) {322return (viewObjectsSelector.getJunctionFront() == this);323}324} else if (modes.isCurrentSupermodeDemand()) {325// get current plan selector326GNEPlanSelector* planSelector = nullptr;327if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {328planSelector = viewParent->getPersonFrame()->getPlanSelector();329} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {330planSelector = viewParent->getPersonPlanFrame()->getPlanSelector();331} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {332planSelector = viewParent->getContainerFrame()->getPlanSelector();333} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {334planSelector = viewParent->getContainerPlanFrame()->getPlanSelector();335}336// continue depending of plan selector337if (planSelector && planSelector->markJunctions()) {338return (viewObjectsSelector.getAttributeCarrierFront() == viewObjectsSelector.getJunctionFront());339} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {340// get current vehicle template341const auto& vehicleTemplate = viewParent->getVehicleFrame()->getVehicleTagSelector()->getCurrentTemplateAC();342// check if vehicle can be placed over from-to TAZs343if (vehicleTemplate && vehicleTemplate->getTagProperty()->vehicleJunctions()) {344return (viewObjectsSelector.getAttributeCarrierFront() == viewObjectsSelector.getJunctionFront());345}346}347}348return false;349}350}351352353bool354GNEJunction::checkDrawDeleteContour() const {355// get edit modes356const auto& editModes = myNet->getViewNet()->getEditModes();357// check if we're in delete mode358if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {359return myNet->getViewNet()->checkOverLockedElement(this, mySelected);360} else {361return false;362}363}364365366bool367GNEJunction::checkDrawDeleteContourSmall() const {368return false;369}370371372bool373GNEJunction::checkDrawSelectContour() const {374// get edit modes375const auto& editModes = myNet->getViewNet()->getEditModes();376// check if we're in select mode377if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {378return myNet->getViewNet()->checkOverLockedElement(this, mySelected);379} else {380return false;381}382}383384385bool386GNEJunction::checkDrawMoveContour() const {387// get edit modes388const auto& editModes = myNet->getViewNet()->getEditModes();389// check if we're in move mode390if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&391(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {392// check if we're editing this network element393const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();394if (editedNetworkElement) {395return editedNetworkElement == this;396} else {397// only move the first element398return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;399}400} else {401return false;402}403}404405406void407GNEJunction::rebuildGNECrossings(bool rebuildNBNodeCrossings) {408// rebuild GNECrossings only if create crossings and walkingAreas in net is enabled409if (myNet->getNetBuilder()->haveNetworkCrossings()) {410if (rebuildNBNodeCrossings) {411// build new NBNode::Crossings and walking areas412mirrorXLeftHand();413myNBNode->buildCrossingsAndWalkingAreas();414mirrorXLeftHand();415}416// create a vector to keep retrieved and created crossings417std::vector<GNECrossing*> retrievedCrossings;418// iterate over NBNode::Crossings of GNEJunction419for (const auto& crossing : myNBNode->getCrossingsIncludingInvalid()) {420// retrieve existent GNECrossing, or create it421GNECrossing* retrievedGNECrossing = retrieveGNECrossing(crossing.get());422retrievedCrossings.push_back(retrievedGNECrossing);423// check if previously this GNECrossings exists, and if true, remove it from myGNECrossings and insert in tree again424std::vector<GNECrossing*>::iterator retrievedExists = std::find(myGNECrossings.begin(), myGNECrossings.end(), retrievedGNECrossing);425if (retrievedExists != myGNECrossings.end()) {426myGNECrossings.erase(retrievedExists);427// update geometry of retrieved crossing428retrievedGNECrossing->updateGeometry();429// update boundary430retrievedGNECrossing->updateCenteringBoundary(false);431} else {432// include reference to created GNECrossing433retrievedGNECrossing->incRef();434}435}436// delete non retrieved GNECrossings (we don't need to extract if from Tree two times)437for (const auto& crossing : myGNECrossings) {438crossing->decRef();439// check if crossing is selected440if (crossing->isAttributeCarrierSelected()) {441crossing->unselectAttributeCarrier();442}443// remove it from inspected ACS444if (myNet->getViewNet()) {445myNet->getViewNet()->getInspectedElements().uninspectAC(crossing);446}447// remove it from net448myNet->removeGLObjectFromGrid(crossing);449// remove it from attributeCarriers450myNet->getAttributeCarriers()->deleteCrossing(crossing);451if (crossing->unreferenced()) {452delete crossing;453}454}455// copy retrieved (existent and created) GNECrossings to myGNECrossings456myGNECrossings = retrievedCrossings;457}458}459460461void462GNEJunction::mirrorXLeftHand() {463if (OptionsCont::getOptions().getBool("lefthand")) {464myNBNode->mirrorX();465for (NBEdge* e : myNBNode->getEdges()) {466e->mirrorX();467468}469}470}471472473void474GNEJunction::buildTLSOperations(GUISUMOAbstractView& parent, GUIGLObjectPopupMenu* ret, const int numSelectedJunctions) {475// create menu pane for edge operations476FXMenuPane* TLSOperations = new FXMenuPane(ret);477ret->insertMenuPaneChild(TLSOperations);478new FXMenuCascade(ret, TL("TLS operations"), GUIIconSubSys::getIcon(GUIIcon::MODETLS), TLSOperations);479// create menu commands for all TLS operations480FXMenuCommand* mcAddTLS = GUIDesigns::buildFXMenuCommand(TLSOperations, TL("Add TLS"), nullptr, &parent, MID_GNE_JUNCTION_ADDTLS);481FXMenuCommand* mcAddJoinedTLS = GUIDesigns::buildFXMenuCommand(TLSOperations, TL("Add joined TLS"), nullptr, &parent, MID_GNE_JUNCTION_ADDJOINTLS);482// check if disable create TLS483if (myNBNode->getControllingTLS().size() > 0) {484mcAddTLS->disable();485mcAddJoinedTLS->disable();486} else {487mcAddTLS->enable();488// check if add joined TLS489if (isAttributeCarrierSelected() && (numSelectedJunctions > 1)) {490mcAddJoinedTLS->enable();491} else {492mcAddJoinedTLS->disable();493}494}495}496497498GUIGLObjectPopupMenu*499GNEJunction::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {500if (myShapeEdited) {501return getShapeEditedPopUpMenu(app, parent, myNBNode->getShape());502} else {503// create popup504GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);505// build common options506buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected, myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork());507// check if we're in supermode network508if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {509const int numSelectedJunctions = myNet->getAttributeCarriers()->getNumberOfSelectedJunctions();510const int numEndpoints = (int)myNBNode->getEndPoints().size();511// check if we're handling a selection512bool handlingSelection = isAttributeCarrierSelected() && (numSelectedJunctions > 1);513// check if menu commands has to be disabled514const bool invalidMode = (myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_CONNECT) ||515(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_TLS) ||516(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_CREATE_EDGE);517// build TLS operation518if (!invalidMode) {519buildTLSOperations(parent, ret, numSelectedJunctions);520}521// create menu commands522GUIDesigns::buildFXMenuCommand(ret, TL("Reset edge endpoints"), nullptr, &parent, MID_GNE_JUNCTION_RESET_EDGE_ENDPOINTS);523FXMenuCommand* mcCustomShape = GUIDesigns::buildFXMenuCommand(ret, TL("Set custom junction shape"), nullptr, &parent, MID_GNE_JUNCTION_EDIT_SHAPE);524FXMenuCommand* mcResetCustomShape = GUIDesigns::buildFXMenuCommand(ret, TL("Reset junction shape"), nullptr, &parent, MID_GNE_JUNCTION_RESET_SHAPE);525FXMenuCommand* mcReplaceByGeometryPoint = GUIDesigns::buildFXMenuCommand(ret, TL("Replace junction by geometry point"), nullptr, &parent, MID_GNE_JUNCTION_REPLACE);526FXMenuCommand* mcSplitJunction = GUIDesigns::buildFXMenuCommand(ret, TLF("Split junction (% end points)", numEndpoints), nullptr, &parent, MID_GNE_JUNCTION_SPLIT);527FXMenuCommand* mcSplitJunctionAndReconnect = GUIDesigns::buildFXMenuCommand(ret, TL("Split junction and reconnect"), nullptr, &parent, MID_GNE_JUNCTION_SPLIT_RECONNECT);528// check if is a roundabout529if (myNBNode->isRoundabout()) {530GUIDesigns::buildFXMenuCommand(ret, TL("Select roundabout"), nullptr, &parent, MID_GNE_JUNCTION_SELECT_ROUNDABOUT);531} else {532// get radius533const double radius = (myNBNode->getRadius() == NBNode::UNSPECIFIED_RADIUS) ? OptionsCont::getOptions().getFloat("default.junctions.radius") : myNBNode->getRadius();534const std::string menuEntryInfo = TLF("Convert to roundabout (using junction attribute radius %)", toString(radius));535FXMenuCommand* mcRoundabout = GUIDesigns::buildFXMenuCommand(ret, menuEntryInfo.c_str(), nullptr, &parent, MID_GNE_JUNCTION_CONVERT_ROUNDABOUT);536// check if disable depending of number of edges537if ((getChildEdges().size() < 2) ||538((myGNEIncomingEdges.size() == 1) && (myGNEOutgoingEdges.size() == 1) && (myGNEIncomingEdges[0]->getFromJunction() == myGNEOutgoingEdges[0]->getToJunction()))) {539mcRoundabout->disable();540}541}542// check multijunctions543const std::string multi = ((numSelectedJunctions > 1) && isAttributeCarrierSelected()) ? TLF(" of % junctions", numSelectedJunctions) : "";544FXMenuCommand* mcClearConnections = GUIDesigns::buildFXMenuCommand(ret, TL("Clear connections") + multi, nullptr, &parent, MID_GNE_JUNCTION_CLEAR_CONNECTIONS);545FXMenuCommand* mcResetConnections = GUIDesigns::buildFXMenuCommand(ret, TL("Reset connections") + multi, nullptr, &parent, MID_GNE_JUNCTION_RESET_CONNECTIONS);546// check if current mode is correct547if (invalidMode) {548mcCustomShape->disable();549mcClearConnections->disable();550mcResetConnections->disable();551}552// check if we're handling a selection553if (handlingSelection) {554mcResetCustomShape->setText(TL("Reset junction shapes"));555}556// disable mcClearConnections if junction hasn't connections557if (getGNEConnections().empty()) {558mcClearConnections->disable();559}560// disable mcResetCustomShape if junction doesn't have a custom shape561if (myNBNode->getShape().size() == 0) {562mcResetCustomShape->disable();563}564// checkIsRemovable requires turnarounds to be computed. This is ugly565if ((myNBNode->getIncomingEdges().size() == 2) && (myNBNode->getOutgoingEdges().size() == 2)) {566NBTurningDirectionsComputer::computeTurnDirectionsForNode(myNBNode, false);567}568std::string reason = TL("wrong edit mode");569if (invalidMode || !myNBNode->checkIsRemovableReporting(reason)) {570mcReplaceByGeometryPoint->setText(mcReplaceByGeometryPoint->getText() + " (" + reason.c_str() + ")");571mcReplaceByGeometryPoint->disable();572}573// check if disable split junctions574if (numEndpoints == 1) {575mcSplitJunction->disable();576mcSplitJunctionAndReconnect->disable();577}578}579return ret;580}581}582583584double585GNEJunction::getExaggeration(const GUIVisualizationSettings& s) const {586return s.junctionSize.getExaggeration(s, this, 4);587}588589590Boundary591GNEJunction::getCenteringBoundary() const {592return myJunctionBoundary;593}594595596void597GNEJunction::updateCenteringBoundary(const bool updateGrid) {598// Remove object from grid599if (updateGrid) {600myNet->removeGLObjectFromGrid(this);601}602// calculate boundary using a radius bigger than geometry point603myJunctionBoundary = Boundary(myNBNode->getPosition().x() - 1, myNBNode->getPosition().y() - 1,604myNBNode->getPosition().x() + 1, myNBNode->getPosition().y() + 1);605myJunctionBoundary.grow(10);606// add shape607if (myNBNode->getShape().size() > 0) {608myJunctionBoundary.add(myNBNode->getShape().getBoxBoundary());609myJunctionBoundary.grow(5);610}611// add boundaries of all connections, walking areas and crossings612for (const auto& edge : myGNEIncomingEdges) {613for (const auto& connection : edge->getGNEConnections()) {614const auto boundary = connection->getCenteringBoundary();615if (boundary.isInitialised()) {616myJunctionBoundary.add(boundary);617}618}619}620for (const auto& crossing : myGNECrossings) {621const auto boundary = crossing->getCenteringBoundary();622if (boundary.isInitialised()) {623myJunctionBoundary.add(boundary);624}625}626for (const auto& walkingArea : myGNEWalkingAreas) {627const auto boundary = walkingArea->getCenteringBoundary();628if (boundary.isInitialised()) {629myJunctionBoundary.add(boundary);630}631}632633// add object into grid634if (updateGrid) {635// if junction has at least one edge, then don't add in grid (because uses the edge's grid)636if (myGNEIncomingEdges.size() + myGNEOutgoingEdges.size() == 0) {637myNet->addGLObjectIntoGrid(this);638}639}640// trigger rebuilding tesselation641myExaggeration = 2;642}643644645void646GNEJunction::drawGL(const GUIVisualizationSettings& s) const {647// first check drawing toggle and boundary selection648if ((*myDrawingToggle != myNet->getViewNet()->getDrawingToggle()) && checkDrawingBoundarySelection()) {649// draw boundaries650if (inGrid()) {651GLHelper::drawBoundary(s, getCenteringBoundary());652}653// get junction exaggeration654const double junctionExaggeration = getExaggeration(s);655// only continue if exaggeration is greater than 0656if (junctionExaggeration > 0) {657// get detail level658const auto d = s.getDetailLevel(junctionExaggeration);659// get shape area660const double junctionShapeArea = myNBNode->getShape().area();661// check if draw junction as shape662const bool drawBubble = drawAsBubble(s, junctionShapeArea);663// draw geometry only if we'rent in drawForObjectUnderCursor mode664if (!s.drawForViewObjectsHandler) {665// push layer matrix666GLHelper::pushMatrix();667// translate to front668drawInLayer(GLO_JUNCTION);669if (drawBubble) {670// draw junction as bubble671drawJunctionAsBubble(s, d, junctionExaggeration);672} else {673// draw junction as shape674drawJunctionAsShape(s, d, junctionExaggeration);675}676// draw junction center (only in move mode)677drawJunctionCenter(s, d);678// draw TLS679drawTLSIcon(s);680// draw elevation681drawElevation(s);682// pop layer Matrix683GLHelper::popMatrix();684// draw lock icon685GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), 1);686// draw junction name687drawJunctionName(s);688// draw dotted contour depending if we're editing the custom shape689const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();690if (editedNetworkElement && (editedNetworkElement == this)) {691// draw dotted contour geometry points692myNetworkElementContour.drawDottedContourGeometryPoints(s, d, this, myNBNode->getShape(), s.neteditSizeSettings.junctionGeometryPointRadius,693junctionExaggeration, s.dottedContourSettings.segmentWidthSmall);694} else {695if (drawBubble) {696// draw dotted contour for bubble697myCircleContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);698} else {699// draw dotted contour for shape700if (junctionShapeArea >= 4) {701myNetworkElementContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);702}703}704}705}706// calculate junction contour (always before children)707calculateJunctioncontour(s, d, junctionExaggeration, drawBubble);708// draw Junction childs709drawJunctionChildren(s, d);710}711// update drawing toggle712*myDrawingToggle = myNet->getViewNet()->getDrawingToggle();713}714}715716717void718GNEJunction::deleteGLObject() {719// Check if edge can be deleted720if (GNEDeleteFrame::SubordinatedElements(this).checkElements(myNet->getViewParent()->getDeleteFrame()->getProtectElements())) {721myNet->deleteJunction(this, myNet->getUndoList());722}723}724725726void727GNEJunction::updateGLObject() {728updateGeometry();729}730731732NBNode*733GNEJunction::getNBNode() const {734return myNBNode;735}736737738std::vector<GNEJunction*>739GNEJunction::getJunctionNeighbours() const {740// use set to avoid duplicates junctions741std::set<GNEJunction*> junctions;742for (const auto& incomingEdge : myGNEIncomingEdges) {743junctions.insert(incomingEdge->getFromJunction());744}745for (const auto& outgoingEdge : myGNEOutgoingEdges) {746junctions.insert(outgoingEdge->getToJunction());747}748return std::vector<GNEJunction*>(junctions.begin(), junctions.end());749}750751752void753GNEJunction::addIncomingGNEEdge(GNEEdge* edge) {754// Check if incoming edge was already inserted755std::vector<GNEEdge*>::iterator i = std::find(myGNEIncomingEdges.begin(), myGNEIncomingEdges.end(), edge);756if (i != myGNEIncomingEdges.end()) {757throw InvalidArgument("Incoming " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' was already inserted into " + getTagStr() + " with ID " + getID() + "'");758} else {759// Add edge into containers760myGNEIncomingEdges.push_back(edge);761}762}763764765766void767GNEJunction::addOutgoingGNEEdge(GNEEdge* edge) {768// Check if outgoing edge was already inserted769const auto i = std::find(myGNEOutgoingEdges.begin(), myGNEOutgoingEdges.end(), edge);770if (i != myGNEOutgoingEdges.end()) {771throw InvalidArgument("Outgoing " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' was already inserted into " + getTagStr() + " with ID " + getID() + "'");772} else {773// Add edge into containers774myGNEOutgoingEdges.push_back(edge);775}776// update centering boundary and grid777updateCenteringBoundary(true);778}779780781void782GNEJunction::removeIncomingGNEEdge(GNEEdge* edge) {783// Check if incoming edge was already inserted784auto i = std::find(myGNEIncomingEdges.begin(), myGNEIncomingEdges.end(), edge);785if (i == myGNEIncomingEdges.end()) {786throw InvalidArgument("Incoming " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' doesn't found into " + getTagStr() + " with ID " + getID() + "'");787} else {788// remove edge from containers789myGNEIncomingEdges.erase(i);790}791// update centering boundary and grid792updateCenteringBoundary(true);793}794795796void797GNEJunction::removeOutgoingGNEEdge(GNEEdge* edge) {798// Check if outgoing edge was already inserted799std::vector<GNEEdge*>::iterator i = std::find(myGNEOutgoingEdges.begin(), myGNEOutgoingEdges.end(), edge);800if (i == myGNEOutgoingEdges.end()) {801throw InvalidArgument("Outgoing " + toString(SUMO_TAG_EDGE) + " with ID '" + edge->getID() + "' doesn't found into " + getTagStr() + " with ID " + getID() + "'");802} else {803// remove edge from containers804myGNEOutgoingEdges.erase(i);805}806}807808809const std::vector<GNEEdge*>&810GNEJunction::getGNEIncomingEdges() const {811return myGNEIncomingEdges;812}813814815const std::vector<GNEEdge*>&816GNEJunction::getGNEOutgoingEdges() const {817return myGNEOutgoingEdges;818}819820821const std::vector<GNECrossing*>&822GNEJunction::getGNECrossings() const {823return myGNECrossings;824}825826827const std::vector<GNEWalkingArea*>&828GNEJunction::getGNEWalkingAreas() const {829return myGNEWalkingAreas;830}831832833std::vector<GNEConnection*>834GNEJunction::getGNEConnections() const {835std::vector<GNEConnection*> connections;836for (const auto& incomingEdge : myGNEIncomingEdges) {837for (const auto& connection : incomingEdge->getGNEConnections()) {838connections.push_back(connection);839}840}841return connections;842}843844845void846GNEJunction::markAsCreateEdgeSource() {847myAmCreateEdgeSource = true;848}849850851void852GNEJunction::unMarkAsCreateEdgeSource() {853myAmCreateEdgeSource = false;854}855856857void858GNEJunction::selectTLS(bool selected) {859myAmTLSSelected = selected;860}861862863void864GNEJunction::invalidateShape() {865if (!myNBNode->hasCustomShape()) {866if (myNBNode->myPoly.size() > 0) {867// clear poly868myNBNode->myPoly.clear();869// update centering boundary870updateCenteringBoundary(true);871}872myNet->requireRecompute();873}874}875876877void878GNEJunction::setLogicValid(bool valid, GNEUndoList* undoList, const std::string& status) {879myHasValidLogic = valid;880if (!valid) {881assert(undoList != 0);882assert(undoList->hasCommandGroup());883NBTurningDirectionsComputer::computeTurnDirectionsForNode(myNBNode, false);884EdgeVector incoming = myNBNode->getIncomingEdges();885for (EdgeVector::iterator it = incoming.begin(); it != incoming.end(); it++) {886GNEEdge* srcEdge = myNet->getAttributeCarriers()->retrieveEdge((*it)->getID());887removeConnectionsFrom(srcEdge, undoList, false); // false, because the whole tls will be invalidated at the end888GNEChange_Attribute::changeAttribute(srcEdge, GNE_ATTR_MODIFICATION_STATUS, status, undoList, true);889}890GNEChange_Attribute::changeAttribute(this, GNE_ATTR_MODIFICATION_STATUS, status, undoList, true);891invalidateTLS(undoList);892} else {893// logic valed, then rebuild GNECrossings to adapt it to the new logic894// (but don't rebuild the crossings in NBNode because they are already finished)895rebuildGNECrossings(false);896}897}898899900void901GNEJunction::removeConnectionsFrom(GNEEdge* edge, GNEUndoList* undoList, bool updateTLS, int lane) {902NBEdge* srcNBE = edge->getNBEdge();903NBEdge* turnEdge = srcNBE->getTurnDestination();904// Make a copy of connections905std::vector<NBEdge::Connection> connections = srcNBE->getConnections();906// delete in reverse so that undoing will add connections in the original order907for (std::vector<NBEdge::Connection>::reverse_iterator con_it = connections.rbegin(); con_it != connections.rend(); con_it++) {908if (lane >= 0 && (*con_it).fromLane != lane) {909continue;910}911bool hasTurn = con_it->toEdge == turnEdge;912undoList->add(new GNEChange_Connection(edge, *con_it, false, false), true);913// needs to come after GNEChange_Connection914// XXX bug: this code path will not be used on a redo!915if (hasTurn) {916myNet->addExplicitTurnaround(srcNBE->getID());917}918}919if (updateTLS) {920std::vector<NBConnection> removeConnections;921for (NBEdge::Connection con : connections) {922removeConnections.push_back(NBConnection(srcNBE, con.fromLane, con.toEdge, con.toLane));923}924removeTLSConnections(removeConnections, undoList);925}926}927928929void930GNEJunction::removeConnectionsTo(GNEEdge* edge, GNEUndoList* undoList, bool updateTLS, int lane) {931NBEdge* destNBE = edge->getNBEdge();932std::vector<NBConnection> removeConnections;933for (NBEdge* srcNBE : myNBNode->getIncomingEdges()) {934GNEEdge* srcEdge = myNet->getAttributeCarriers()->retrieveEdge(srcNBE->getID());935std::vector<NBEdge::Connection> connections = srcNBE->getConnections();936for (std::vector<NBEdge::Connection>::reverse_iterator con_it = connections.rbegin(); con_it != connections.rend(); con_it++) {937if ((*con_it).toEdge == destNBE) {938if (lane >= 0 && (*con_it).toLane != lane) {939continue;940}941bool hasTurn = srcNBE->getTurnDestination() == destNBE;942undoList->add(new GNEChange_Connection(srcEdge, *con_it, false, false), true);943// needs to come after GNEChange_Connection944// XXX bug: this code path will not be used on a redo!945if (hasTurn) {946myNet->addExplicitTurnaround(srcNBE->getID());947}948removeConnections.push_back(NBConnection(srcNBE, (*con_it).fromLane, destNBE, (*con_it).toLane));949}950}951}952if (updateTLS) {953removeTLSConnections(removeConnections, undoList);954}955}956957958void959GNEJunction::removeTLSConnections(std::vector<NBConnection>& connections, GNEUndoList* undoList) {960if (connections.size() > 0) {961const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode->getControllingTLS(); // make a copy!962for (const auto& TLS : coypOfTls) {963NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(TLS);964// guessed TLS (NBOwnTLDef) do not need to be updated965if (tlDef != nullptr) {966std::string newID = tlDef->getID();967// create replacement before deleting the original because deletion will mess up saving original nodes968NBLoadedSUMOTLDef* replacementDef = new NBLoadedSUMOTLDef(*tlDef, *tlDef->getLogic());969for (NBConnection& con : connections) {970replacementDef->removeConnection(con);971}972undoList->add(new GNEChange_TLS(this, tlDef, false), true);973undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);974// the removed traffic light may have controlled more than one junction. These too have become invalid now975const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!976for (const auto& node : copyOfNodes) {977GNEJunction* sharing = myNet->getAttributeCarriers()->retrieveJunction(node->getID());978undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);979undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);980}981}982}983}984}985986987void988GNEJunction::replaceIncomingConnections(GNEEdge* which, GNEEdge* by, GNEUndoList* undoList) {989// remap connections of the edge990assert(which->getChildLanes().size() == by->getChildLanes().size());991std::vector<NBEdge::Connection> connections = which->getNBEdge()->getConnections();992for (NBEdge::Connection& c : connections) {993undoList->add(new GNEChange_Connection(which, c, false, false), true);994undoList->add(new GNEChange_Connection(by, c, false, true), true);995}996// also remap tls connections997const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode->getControllingTLS(); // make a copy!998for (const auto& TLS : coypOfTls) {999NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(TLS);1000// guessed TLS (NBOwnTLDef) do not need to be updated1001if (tlDef != nullptr) {1002std::string newID = tlDef->getID();1003// create replacement before deleting the original because deletion will mess up saving original nodes1004NBLoadedSUMOTLDef* replacementDef = new NBLoadedSUMOTLDef(*tlDef, *tlDef->getLogic());1005for (int i = 0; i < (int)which->getChildLanes().size(); ++i) {1006replacementDef->replaceRemoved(which->getNBEdge(), i, by->getNBEdge(), i, true);1007}1008undoList->add(new GNEChange_TLS(this, tlDef, false), true);1009undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);1010// the removed traffic light may have controlled more than one junction. These too have become invalid now1011const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!1012for (const auto& node : copyOfNodes) {1013GNEJunction* sharing = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1014undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);1015undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);1016}1017}1018}1019}102010211022void1023GNEJunction::markAsModified(GNEUndoList* undoList) {1024EdgeVector incoming = myNBNode->getIncomingEdges();1025for (EdgeVector::iterator it = incoming.begin(); it != incoming.end(); it++) {1026NBEdge* srcNBE = *it;1027GNEEdge* srcEdge = myNet->getAttributeCarriers()->retrieveEdge(srcNBE->getID());1028GNEChange_Attribute::changeAttribute(srcEdge, GNE_ATTR_MODIFICATION_STATUS, FEATURE_MODIFIED, undoList, true);1029}1030}103110321033void1034GNEJunction::invalidateTLS(GNEUndoList* undoList, const NBConnection& deletedConnection, const NBConnection& addedConnection) {1035assert(undoList->hasCommandGroup());1036// NBLoadedSUMOTLDef becomes invalid, replace with NBOwnTLDef which will be dynamically recomputed1037const std::set<NBTrafficLightDefinition*> coypOfTls = myNBNode->getControllingTLS(); // make a copy!1038for (const auto& TLS : coypOfTls) {1039NBLoadedSUMOTLDef* tlDef = dynamic_cast<NBLoadedSUMOTLDef*>(TLS);1040if (tlDef != nullptr) {1041// the removed traffic light may have controlled more than one junction. These too have become invalid now1042const std::vector<NBNode*> copyOfNodes = tlDef->getNodes(); // make a copy!1043if (myGNECrossings.size() == 0 && getNBNode()->getCrossings().size() != 0) {1044// crossings were not computed yet. We need them as netedit elements to manage tlIndex resetting1045myNet->getNetBuilder()->setHaveNetworkCrossings(true);1046rebuildGNECrossings();1047for (const auto& node : copyOfNodes) {1048GNEJunction* sharing = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1049if (sharing != this) {1050sharing->rebuildGNECrossings();1051}1052}1053}1054NBTrafficLightDefinition* replacementDef = nullptr;1055std::string newID = tlDef->getID(); // + "_reguessed"; // changes due to reguessing will be visible in diff1056if (deletedConnection != NBConnection::InvalidConnection) {1057// create replacement before deleting the original because deletion will mess up saving original nodes1058NBLoadedSUMOTLDef* repl = new NBLoadedSUMOTLDef(*tlDef, *tlDef->getLogic());1059repl->removeConnection(deletedConnection);1060replacementDef = repl;1061} else if (addedConnection != NBConnection::InvalidConnection) {1062if (addedConnection.getTLIndex() == NBConnection::InvalidTlIndex) {1063// custom tl indices of crossings might become invalid upon recomputation so we must save them1064// however, they could remain valid so we register a change but keep them at their old value1065for (const auto& crossing : myGNECrossings) {1066const std::string oldValue = crossing->getAttribute(SUMO_ATTR_TLLINKINDEX);1067GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX, toString(NBConnection::InvalidTlIndex), undoList, true);1068GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX, oldValue, undoList, true);1069const std::string oldValue2 = crossing->getAttribute(SUMO_ATTR_TLLINKINDEX2);1070GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX2, toString(NBConnection::InvalidTlIndex), undoList, true);1071GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX2, oldValue2, undoList, true);1072}1073}1074NBLoadedSUMOTLDef* repl = new NBLoadedSUMOTLDef(*tlDef, *tlDef->getLogic());1075repl->addConnection(addedConnection.getFrom(), addedConnection.getTo(),1076addedConnection.getFromLane(), addedConnection.getToLane(), addedConnection.getTLIndex(), addedConnection.getTLIndex2());1077replacementDef = repl;1078} else {1079// recompute crossing indices along with everything else1080for (const auto& crossing : myGNECrossings) {1081GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX, toString(NBConnection::InvalidTlIndex), undoList, true);1082GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX2, toString(NBConnection::InvalidTlIndex), undoList, true);1083}1084replacementDef = new NBOwnTLDef(newID, tlDef->getOffset(), tlDef->getType());1085replacementDef->setProgramID(tlDef->getProgramID());1086}1087undoList->add(new GNEChange_TLS(this, tlDef, false), true);1088undoList->add(new GNEChange_TLS(this, replacementDef, true, false, newID), true);1089// reset nodes of joint tls1090for (const auto& node : copyOfNodes) {1091GNEJunction* sharing = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1092if (sharing != this) {1093if (deletedConnection == NBConnection::InvalidConnection && addedConnection == NBConnection::InvalidConnection) {1094// recompute crossing indices for shared1095// (they won't do this on subsequent call to invalidateTLS if they received an NBOwnTLDef)1096for (const auto& crossing : sharing->getGNECrossings()) {1097GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX, toString(NBConnection::InvalidTlIndex), undoList, true);1098GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX2, toString(NBConnection::InvalidTlIndex), undoList, true);1099}1100}1101undoList->add(new GNEChange_TLS(sharing, tlDef, false), true);1102undoList->add(new GNEChange_TLS(sharing, replacementDef, true, false, newID), true);1103}1104}1105}1106}1107}11081109void1110GNEJunction::removeEdgeFromCrossings(GNEEdge* edge, GNEUndoList* undoList) {1111// obtain a copy of GNECrossing of junctions1112const auto copyOfGNECrossings = myGNECrossings;1113// iterate over copy of GNECrossings1114for (const auto& crossing : copyOfGNECrossings) {1115// obtain the set of edges vinculated with the crossing (due it works as ID)1116EdgeSet edgeSet(crossing->getCrossingEdges().begin(), crossing->getCrossingEdges().end());1117// If this edge is part of the set of edges of crossing1118if (edgeSet.count(edge->getNBEdge()) == 1) {1119// delete crossing if this is their last edge1120if ((crossing->getCrossingEdges().size() == 1) && (crossing->getCrossingEdges().front() == edge->getNBEdge())) {1121myNet->deleteCrossing(crossing, undoList);1122} else {1123// remove this edge of the edge's attribute of crossing (note: This can invalidate the crossing)1124std::vector<std::string> edges = GNEAttributeCarrier::parse<std::vector<std::string>>(crossing->getAttribute(SUMO_ATTR_EDGES));1125edges.erase(std::find(edges.begin(), edges.end(), edge->getID()));1126crossing->setAttribute(SUMO_ATTR_EDGES, joinToString(edges, " "), undoList);1127}1128}1129}1130}113111321133bool1134GNEJunction::isLogicValid() {1135return myHasValidLogic;1136}113711381139GNECrossing*1140GNEJunction::retrieveGNECrossing(NBNode::Crossing* NBNodeCrossing, bool createIfNoExist) {1141// iterate over all crossing1142for (const auto& crossing : myGNECrossings) {1143// if found, return it1144if (crossing->getCrossingEdges() == NBNodeCrossing->edges) {1145return crossing;1146}1147}1148if (createIfNoExist) {1149// create new GNECrossing1150GNECrossing* createdGNECrossing = new GNECrossing(this, NBNodeCrossing->edges);1151// update geometry after creating1152createdGNECrossing->updateGeometry();1153// add it in Network1154myNet->addGLObjectIntoGrid(createdGNECrossing);1155// add it in attributeCarriers1156myNet->getAttributeCarriers()->insertCrossing(createdGNECrossing);1157return createdGNECrossing;1158} else {1159return nullptr;1160}1161}116211631164GNEWalkingArea*1165GNEJunction::retrieveGNEWalkingArea(const std::string& NBNodeWalkingAreaID, bool createIfNoExist) {1166// iterate over all walkingArea1167for (const auto& walkingArea : myGNEWalkingAreas) {1168// if found, return it1169if (walkingArea->getID() == NBNodeWalkingAreaID) {1170return walkingArea;1171}1172}1173if (createIfNoExist) {1174// create new GNEWalkingArea1175GNEWalkingArea* createdGNEWalkingArea = new GNEWalkingArea(this, NBNodeWalkingAreaID);1176// update geometry after creating1177createdGNEWalkingArea->updateGeometry();1178// add it in Network1179myNet->addGLObjectIntoGrid(createdGNEWalkingArea);1180// add it in attributeCarriers1181myNet->getAttributeCarriers()->insertWalkingArea(createdGNEWalkingArea);1182return createdGNEWalkingArea;1183} else {1184return nullptr;1185}1186}118711881189void1190GNEJunction::markConnectionsDeprecated(bool includingNeighbours) {1191// only it's needed to mark the connections of incoming edges1192for (const auto& i : myGNEIncomingEdges) {1193for (const auto& j : i->getGNEConnections()) {1194j->markConnectionGeometryDeprecated();1195}1196if (includingNeighbours) {1197i->getFromJunction()->markConnectionsDeprecated(false);1198}1199}1200}120112021203void1204GNEJunction::setJunctionType(const std::string& value, GNEUndoList* undoList) {1205undoList->begin(this, "change " + getTagStr() + " type");1206if (NBNode::isTrafficLight(SUMOXMLDefinitions::NodeTypes.get(value))) {1207if (getNBNode()->isTLControlled() &&1208// if switching changing from or to traffic_light_right_on_red we need to remove the old plan1209(getNBNode()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED1210|| SUMOXMLDefinitions::NodeTypes.get(value) == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED)1211) {1212// make a copy because we will modify the original1213const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1214for (const auto& TLS : copyOfTls) {1215undoList->add(new GNEChange_TLS(this, TLS, false), true);1216}1217}1218if (!getNBNode()->isTLControlled()) {1219// create new traffic light1220undoList->add(new GNEChange_TLS(this, nullptr, true), true);1221}1222} else if (getNBNode()->isTLControlled()) {1223// delete old traffic light1224// make a copy because we will modify the original1225const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1226for (const auto& TLS : copyOfTls) {1227undoList->add(new GNEChange_TLS(this, TLS, false, false), true);1228const std::vector<NBNode*> copyOfNodes = TLS->getNodes(); // make a copy!1229for (const auto& node : copyOfNodes) {1230GNEJunction* sharing = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1231sharing->invalidateTLS(undoList);1232}1233}1234}1235// must be the final step, otherwise we do not know which traffic lights to remove via GNEChange_TLS1236GNEChange_Attribute::changeAttribute(this, SUMO_ATTR_TYPE, value, undoList, true);1237for (const auto& crossing : myGNECrossings) {1238GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX, "-1", undoList, true);1239GNEChange_Attribute::changeAttribute(crossing, SUMO_ATTR_TLLINKINDEX2, "-1", undoList, true);1240}1241undoList->end();1242}124312441245void1246GNEJunction::clearWalkingAreas() {1247// delete non retrieved GNEWalkingAreas (we don't need to extract if from Tree two times)1248for (const auto& walkingArea : myGNEWalkingAreas) {1249walkingArea->decRef();1250// check if walkingArea is selected1251if (walkingArea->isAttributeCarrierSelected()) {1252walkingArea->unselectAttributeCarrier();1253}1254// remove it from inspected ACS1255myNet->getViewNet()->getInspectedElements().uninspectAC(walkingArea);1256// remove it from net1257myNet->removeGLObjectFromGrid(walkingArea);1258// remove it from attributeCarriers1259myNet->getAttributeCarriers()->deleteWalkingArea(walkingArea);1260if (walkingArea->unreferenced()) {1261delete walkingArea;1262}1263}1264myGNEWalkingAreas.clear();1265}126612671268void1269GNEJunction::rebuildGNEWalkingAreas() {1270// first clear GNEWalkingAreas1271clearWalkingAreas();1272// iterate over NBNode::WalkingAreas of GNEJunction1273for (const auto& walkingArea : myNBNode->getWalkingAreas()) {1274// retrieve existent GNEWalkingArea, or create it1275GNEWalkingArea* retrievedGNEWalkingArea = retrieveGNEWalkingArea(walkingArea.id, true);1276// include reference to created GNEWalkingArea1277retrievedGNEWalkingArea->incRef();1278// update geometry of retrieved walkingArea1279retrievedGNEWalkingArea->updateGeometry();1280// update boundary1281retrievedGNEWalkingArea->updateCenteringBoundary(false);1282// add in walkingAreas1283myGNEWalkingAreas.push_back(retrievedGNEWalkingArea);1284}1285}1286128712881289void1290GNEJunction::addInternalLane(const GNEInternalLane* internalLane) {1291if (std::find(myInternalLanes.begin(), myInternalLanes.end(), internalLane) != myInternalLanes.end()) {1292throw ProcessError(internalLane->getTagStr() + " with ID='" + internalLane->getID() + "' already exist");1293} else {1294myInternalLanes.push_back(internalLane);1295}1296}129712981299void1300GNEJunction::removeInternalLane(const GNEInternalLane* internalLane) {1301const auto finder = std::find(myInternalLanes.begin(), myInternalLanes.end(), internalLane);1302if (finder == myInternalLanes.end()) {1303throw ProcessError(internalLane->getTagStr() + " with ID='" + internalLane->getID() + "' wasn't previously inserted");1304} else {1305myInternalLanes.erase(finder);1306}1307}130813091310std::string1311GNEJunction::getAttribute(SumoXMLAttr key) const {1312switch (key) {1313case SUMO_ATTR_ID:1314return getMicrosimID();1315case SUMO_ATTR_POSITION:1316return toString(myNBNode->getPosition());1317case SUMO_ATTR_TYPE:1318return toString(myNBNode->getType());1319case GNE_ATTR_MODIFICATION_STATUS:1320return myLogicStatus;1321case SUMO_ATTR_SHAPE:1322return toString(myNBNode->getShape());1323case SUMO_ATTR_RADIUS:1324if (myNBNode->getRadius() < 0) {1325return "default";1326} else {1327return toString(myNBNode->getRadius());1328}1329case SUMO_ATTR_TLTYPE:1330if (isAttributeEnabled(SUMO_ATTR_TLTYPE)) {1331// @todo this causes problems if the node were to have multiple programs of different type (plausible)1332return toString((*myNBNode->getControllingTLS().begin())->getType());1333} else {1334return "No TLS";1335}1336case SUMO_ATTR_TLLAYOUT:1337if (isAttributeEnabled(SUMO_ATTR_TLLAYOUT)) {1338return toString((*myNBNode->getControllingTLS().begin())->getLayout());1339} else {1340return "No TLS";1341}1342case SUMO_ATTR_TLID:1343if (isAttributeEnabled(SUMO_ATTR_TLID)) {1344return toString((*myNBNode->getControllingTLS().begin())->getID());1345} else {1346return "No TLS";1347}1348case GNE_ATTR_IS_ROUNDABOUT:1349return myNBNode->isRoundabout() ? TRUE_STR : FALSE_STR;1350case SUMO_ATTR_KEEP_CLEAR:1351// keep clear is only used as a convenience feature in plain xml1352// input. When saving to .net.xml the status is saved only for the connections1353// to show the correct state we must check all connections1354for (const auto& i : myGNEIncomingEdges) {1355for (const auto& j : i->getGNEConnections()) {1356if (j->getNBEdgeConnection().keepClear) {1357return TRUE_STR;1358}1359}1360}1361return FALSE_STR;1362case SUMO_ATTR_RIGHT_OF_WAY:1363return SUMOXMLDefinitions::RightOfWayValues.getString(myNBNode->getRightOfWay());1364case SUMO_ATTR_FRINGE:1365return SUMOXMLDefinitions::FringeTypeValues.getString(myNBNode->getFringeType());1366case SUMO_ATTR_ROUNDABOUT:1367return SUMOXMLDefinitions::RoundaboutTypeValues.getString(myNBNode->getRoundaboutType());1368case SUMO_ATTR_NAME:1369return myNBNode->getName();1370default:1371return getCommonAttribute(key);1372}1373}137413751376double1377GNEJunction::getAttributeDouble(SumoXMLAttr key) const {1378return getCommonAttributeDouble(key);1379}138013811382Position1383GNEJunction::getAttributePosition(SumoXMLAttr key) const {1384return getCommonAttributePosition(key);1385}138613871388PositionVector1389GNEJunction::getAttributePositionVector(SumoXMLAttr key) const {1390switch (key) {1391case SUMO_ATTR_SHAPE:1392return myNBNode->getShape();1393default:1394return getCommonAttributePositionVector(key);1395}1396}139713981399void1400GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {1401if (value == getAttribute(key)) {1402return; //avoid needless changes, later logic relies on the fact that attributes have changed1403}1404switch (key) {1405case SUMO_ATTR_ID:1406case GNE_ATTR_MODIFICATION_STATUS:1407case SUMO_ATTR_SHAPE:1408case SUMO_ATTR_RADIUS:1409case SUMO_ATTR_RIGHT_OF_WAY:1410case SUMO_ATTR_FRINGE:1411case SUMO_ATTR_ROUNDABOUT:1412case SUMO_ATTR_NAME:1413GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);1414break;1415case SUMO_ATTR_POSITION: {1416const GNEJunction* junctionToMerge = nullptr;1417bool alreadyAsked = false;1418// parse position1419Position newPosition = GNEAttributeCarrier::parse<Position>(value);1420// check if caculate new position based in edges1421if (newPosition == Position::INVALID) {1422Boundary b;1423// set new position of adjacent edges1424for (const auto& edge : myGNEIncomingEdges) {1425b.add(edge->getNBEdge()->getGeometry().back());1426}1427for (const auto& edge : myGNEOutgoingEdges) {1428b.add(edge->getNBEdge()->getGeometry().front());1429}1430newPosition = b.getCenter();1431}1432// retrieve all junctions placed in this position1433myNet->getViewNet()->updateObjectsInPosition(newPosition);1434for (const auto& junction : myNet->getViewNet()->getViewObjectsSelector().getJunctions()) {1435// check distance position1436if ((junctionToMerge == nullptr) && (junction != this) &&1437(junction->getPositionInView().distanceTo2D(newPosition) < myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.junctionBubbleRadius) &&1438myNet->getViewNet()->askMergeJunctions(this, junction, alreadyAsked)) {1439junctionToMerge = junction;1440}1441}1442// also check the merging junctions located during drawGL1443for (const auto& junction : myNet->getViewNet()->getViewObjectsSelector().getMergingJunctions()) {1444// check distance position1445if ((junctionToMerge == nullptr) && (junction != this) && myNet->getViewNet()->askMergeJunctions(this, junction, alreadyAsked)) {1446junctionToMerge = junction;1447}1448}1449// if we merge the junction, this junction will be removed, therefore we don't have to change the position1450if (junctionToMerge) {1451myNet->mergeJunctions(this, junctionToMerge, undoList);1452} else {1453// change Keep Clear attribute in all connections1454undoList->begin(this, TL("change junction position"));1455// obtain NBNode position1456const Position orig = myNBNode->getPosition();1457// change junction position1458GNEChange_Attribute::changeAttribute(this, key, toString(newPosition), undoList, true);1459// calculate delta using new position1460const bool moveOnlyCenter = myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getMoveOnlyJunctionCenter();1461const Position delta = myNBNode->getPosition() - (moveOnlyCenter ? myNBNode->getPosition() : orig);1462// set new position of adjacent edges1463for (const auto& edge : myGNEIncomingEdges) {1464const Position newEnd = edge->getNBEdge()->getGeometry().back() + delta;1465GNEChange_Attribute::changeAttribute(edge, GNE_ATTR_SHAPE_END, toString(newEnd), undoList, true);1466}1467for (const auto& edge : myGNEOutgoingEdges) {1468const Position newStart = edge->getNBEdge()->getGeometry().front() + delta;1469GNEChange_Attribute::changeAttribute(edge, GNE_ATTR_SHAPE_START, toString(newStart), undoList, true);1470}1471undoList->end();1472}1473break;1474}1475case SUMO_ATTR_KEEP_CLEAR:1476// change Keep Clear attribute in all connections1477undoList->begin(this, TL("change keepClear for whole junction"));1478for (const auto& incomingEdge : myGNEIncomingEdges) {1479for (const auto& junction : incomingEdge->getGNEConnections()) {1480GNEChange_Attribute::changeAttribute(junction, key, value, undoList, true);1481}1482}1483undoList->end();1484break;1485case SUMO_ATTR_TYPE: {1486// set junction type1487setJunctionType(value, undoList);1488break;1489}1490case SUMO_ATTR_TLTYPE: {1491undoList->begin(this, "change " + getTagStr() + " tl-type");1492// make a copy because we will modify the original1493const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1494for (const auto& TLS : copyOfTls) {1495NBLoadedSUMOTLDef* oldLoaded = dynamic_cast<NBLoadedSUMOTLDef*>(TLS);1496if (oldLoaded != nullptr) {1497NBTrafficLightDefinition* newDef = nullptr;1498if (value == toString(TrafficLightType::NEMA) || oldLoaded->getType() == TrafficLightType::NEMA) {1499// rebuild the program because the old and new ones are incompatible1500newDef = new NBOwnTLDef(oldLoaded->getID(), oldLoaded->getOffset(), TrafficLightType::NEMA);1501newDef->setProgramID(oldLoaded->getProgramID());1502} else {1503NBLoadedSUMOTLDef* newLDef = new NBLoadedSUMOTLDef(*oldLoaded, *oldLoaded->getLogic());1504newLDef->guessMinMaxDuration(); // minDur and maxDur are never written for a static tls1505newDef = newLDef;1506}1507std::vector<NBNode*> nodes = TLS->getNodes();1508for (const auto& node : nodes) {1509GNEJunction* junction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1510undoList->add(new GNEChange_TLS(junction, TLS, false), true);1511undoList->add(new GNEChange_TLS(junction, newDef, true), true);1512}1513}1514}1515GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);1516undoList->end();1517break;1518}1519case SUMO_ATTR_TLLAYOUT: {1520undoList->begin(this, "change " + getTagStr() + " tlLayout");1521const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1522for (const auto& oldTLS : copyOfTls) {1523std::vector<NBNode*> copyOfNodes = oldTLS->getNodes();1524NBOwnTLDef* newTLS = new NBOwnTLDef(oldTLS->getID(), oldTLS->getOffset(), oldTLS->getType());1525newTLS->setLayout(SUMOXMLDefinitions::TrafficLightLayouts.get(value));1526newTLS->setProgramID(oldTLS->getProgramID());1527for (const auto& node : copyOfNodes) {1528GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1529undoList->add(new GNEChange_TLS(oldJunction, oldTLS, false), true);1530}1531for (const auto& node : copyOfNodes) {1532GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1533undoList->add(new GNEChange_TLS(oldJunction, newTLS, true), true);1534}1535}1536undoList->end();1537break;1538}1539case SUMO_ATTR_TLID: {1540undoList->begin(this, "change " + toString(SUMO_TAG_TRAFFIC_LIGHT) + " id");1541const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1542assert(copyOfTls.size() > 0);1543NBTrafficLightDefinition* currentTLS = *copyOfTls.begin();1544NBTrafficLightDefinition* currentTLSCopy = nullptr;1545const bool currentIsSingle = currentTLS->getNodes().size() == 1;1546const bool currentIsLoaded = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS) != nullptr;1547if (currentIsLoaded) {1548currentTLSCopy = new NBLoadedSUMOTLDef(*currentTLS,1549*dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS)->getLogic());1550}1551// remove from previous tls1552for (const auto& TLS : copyOfTls) {1553undoList->add(new GNEChange_TLS(this, TLS, false), true);1554}1555NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();1556// programs to which the current node shall be added1557const std::map<std::string, NBTrafficLightDefinition*> programs = tlCont.getPrograms(value);1558if (programs.size() > 0) {1559for (const auto& TLSProgram : programs) {1560NBTrafficLightDefinition* oldTLS = TLSProgram.second;1561if (dynamic_cast<NBOwnTLDef*>(oldTLS) != nullptr) {1562undoList->add(new GNEChange_TLS(this, oldTLS, true), true);1563} else {1564// delete and re-create the definition because the loaded phases are now invalid1565if (dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS) != nullptr &&1566dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->usingSignalGroups()) {1567// keep the old program and add all-red state for the added links1568NBLoadedSUMOTLDef* newTLSJoined = new NBLoadedSUMOTLDef(*oldTLS, *dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->getLogic());1569newTLSJoined->joinLogic(currentTLSCopy);1570undoList->add(new GNEChange_TLS(this, newTLSJoined, true, true), true);1571} else {1572undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);1573}1574NBTrafficLightDefinition* newTLS = *myNBNode->getControllingTLS().begin();1575// switch from old to new definition1576std::vector<NBNode*> copyOfNodes = oldTLS->getNodes();1577for (const auto& node : copyOfNodes) {1578GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1579undoList->add(new GNEChange_TLS(oldJunction, oldTLS, false), true);1580undoList->add(new GNEChange_TLS(oldJunction, newTLS, true), true);1581}1582}1583}1584} else {1585if (currentIsSingle && currentIsLoaded) {1586// rename the traffic light but keep everything else1587NBTrafficLightLogic* renamedLogic = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLSCopy)->getLogic();1588renamedLogic->setID(value);1589NBLoadedSUMOTLDef* renamedTLS = new NBLoadedSUMOTLDef(*currentTLSCopy, *renamedLogic);1590renamedTLS->setID(value);1591undoList->add(new GNEChange_TLS(this, renamedTLS, true, true), true);1592} else {1593// create new traffic light1594undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);1595}1596}1597delete currentTLSCopy;1598undoList->end();1599break;1600}1601default:1602setCommonAttribute(key, value, undoList);1603break;1604}1605}160616071608bool1609GNEJunction::isValid(SumoXMLAttr key, const std::string& value) {1610switch (key) {1611case SUMO_ATTR_ID:1612return SUMOXMLDefinitions::isValidNetID(value) && (myNet->getAttributeCarriers()->retrieveJunction(value, false) == nullptr);1613case SUMO_ATTR_TYPE:1614return SUMOXMLDefinitions::NodeTypes.hasString(value);1615case SUMO_ATTR_POSITION:1616if (value.empty()) {1617return (myGNEIncomingEdges.size() + myGNEOutgoingEdges.size()) > 0;1618} else {1619return canParse<Position>(value);1620}1621case SUMO_ATTR_SHAPE:1622// empty shapes are allowed1623return canParse<PositionVector>(value);1624case SUMO_ATTR_RADIUS:1625if (value.empty() || (value == "default")) {1626return true;1627} else {1628return canParse<double>(value) && ((parse<double>(value) >= 0) || (parse<double>(value) == -1));1629}1630case SUMO_ATTR_TLTYPE:1631return myNBNode->isTLControlled() && SUMOXMLDefinitions::TrafficLightTypes.hasString(value);1632case SUMO_ATTR_TLLAYOUT:1633return myNBNode->isTLControlled() && SUMOXMLDefinitions::TrafficLightLayouts.hasString(value);1634case SUMO_ATTR_TLID:1635return myNBNode->isTLControlled() && (value != "");1636case SUMO_ATTR_KEEP_CLEAR:1637return canParse<bool>(value);1638case SUMO_ATTR_RIGHT_OF_WAY:1639return SUMOXMLDefinitions::RightOfWayValues.hasString(value);1640case SUMO_ATTR_FRINGE:1641return SUMOXMLDefinitions::FringeTypeValues.hasString(value);1642case SUMO_ATTR_ROUNDABOUT:1643return SUMOXMLDefinitions::RoundaboutTypeValues.hasString(value);1644case SUMO_ATTR_NAME:1645return true;1646default:1647return isCommonAttributeValid(key, value);1648}1649}165016511652bool1653GNEJunction::isAttributeEnabled(SumoXMLAttr key) const {1654switch (key) {1655case SUMO_ATTR_TLTYPE:1656case SUMO_ATTR_TLLAYOUT:1657case SUMO_ATTR_TLID:1658return myNBNode->isTLControlled();1659case SUMO_ATTR_KEEP_CLEAR: {1660// check if at least there is an incoming connection1661for (const auto& incomingEdge : myGNEIncomingEdges) {1662if (incomingEdge->getGNEConnections().size() > 0) {1663return true;1664}1665}1666return false;1667}1668case GNE_ATTR_IS_ROUNDABOUT:1669return false;1670default:1671return true;1672}1673}167416751676bool1677GNEJunction::isAttributeComputed(SumoXMLAttr key) const {1678switch (key) {1679case SUMO_ATTR_SHAPE:1680return !myNBNode->hasCustomShape();1681default:1682return false;1683}1684}168516861687void1688GNEJunction::setResponsible(bool newVal) {1689myAmResponsible = newVal;1690}16911692// ===========================================================================1693// private1694// ===========================================================================16951696bool1697GNEJunction::drawAsBubble(const GUIVisualizationSettings& s, const double junctionShapeArea) const {1698const auto& editModes = myNet->getViewNet()->getEditModes();1699const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();1700// check conditions1701if (junctionShapeArea < 4) {1702// force draw if this junction is a candidate1703if (mySourceCandidate || myTargetCandidate || mySpecialCandidate ||1704myPossibleCandidate || myConflictedCandidate) {1705return true;1706}1707// force draw if we're in person/container plan mode1708if (editModes.isCurrentSupermodeDemand() &&1709((editModes.demandEditMode == DemandEditMode::DEMAND_PERSON) ||1710(editModes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) ||1711(editModes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) ||1712(editModes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN))) {1713return true;1714}1715// force draw if we're inspecting a vehicle that start or ends in a junction1716if (inspectedElements.isInspectingSingleElement()) {1717// check if starts or ends in this junction1718if ((inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_FROM_JUNCTION) &&1719(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_FROM_JUNCTION) == getID())) ||1720(inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_TO_JUNCTION) &&1721(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_TO_JUNCTION) == getID()))) {1722return true;1723}1724}1725}1726if (!s.drawJunctionShape) {1727// don't draw bubble if it was disabled in GUIVisualizationSettings1728return false;1729}1730if (myNet->getViewNet()->showJunctionAsBubbles()) {1731// force draw bubbles if we enabled option in checkbox of viewNet1732return true;1733}1734if (junctionShapeArea >= 4) {1735// don't draw if shape area is greater than 41736return false;1737}1738if (!editModes.isCurrentSupermodeNetwork()) {1739// only draw bubbles in network mode1740return false;1741}1742return true;1743}174417451746void1747GNEJunction::drawJunctionAsBubble(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,1748const double exaggeration) const {1749// calculate bubble radius1750const double bubbleRadius = s.neteditSizeSettings.junctionBubbleRadius * exaggeration;1751// set bubble color1752const RGBColor bubbleColor = setColor(s, true);1753// push matrix1754GLHelper::pushMatrix();1755// set color1756GLHelper::setColor(bubbleColor);1757// move matrix junction center1758glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 1.5);1759// draw filled circle1760GLHelper::drawFilledCircleDetailled(d, bubbleRadius);1761// pop matrix1762GLHelper::popMatrix();1763}176417651766void1767GNEJunction::drawJunctionAsShape(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double exaggeration) const {1768// first check drawing conditions1769if (s.drawJunctionShape && (myNBNode->getShape().size() > 0)) {1770// set shape color1771const RGBColor junctionShapeColor = setColor(s, false);1772// set color1773GLHelper::setColor(junctionShapeColor);1774// adjust shape to exaggeration (check)1775if ((exaggeration > 1 || myExaggeration > 1) && exaggeration != myExaggeration) {1776myExaggeration = exaggeration;1777myTesselation.setShape(myNBNode->getShape());1778myTesselation.getShapeRef().closePolygon();1779myTesselation.getShapeRef().scaleRelative(exaggeration);1780myTesselation.myTesselation.clear();1781}1782// check if draw tesselation or or polygon1783if (d <= GUIVisualizationSettings::Detail::DrawPolygonTesselation) {1784// draw shape with high detail1785myTesselation.drawTesselation(myTesselation.getShape());1786} else {1787// draw shape1788GLHelper::drawFilledPoly(myNBNode->getShape(), true);1789}1790// draw shape points only in Network supermode1791if (myShapeEdited && myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&1792s.drawMovingGeometryPoint(exaggeration, s.neteditSizeSettings.junctionGeometryPointRadius)) {1793// set color1794const RGBColor darkerColor = junctionShapeColor.changedBrightness(-32);1795// calculate geometry1796GUIGeometry junctionGeometry;1797// obtain junction Shape1798PositionVector junctionOpenShape = myNBNode->getShape();1799// adjust shape to exaggeration1800if (exaggeration > 1) {1801junctionOpenShape.scaleRelative(exaggeration);1802}1803// update geometry1804junctionGeometry.updateGeometry(junctionOpenShape);1805// set color1806GLHelper::setColor(darkerColor);1807// draw shape1808GUIGeometry::drawGeometry(d, junctionGeometry, s.neteditSizeSettings.junctionGeometryPointRadius * 0.5);1809// draw geometry points1810GUIGeometry::drawGeometryPoints(d, junctionOpenShape, darkerColor,1811s.neteditSizeSettings.junctionGeometryPointRadius, exaggeration,1812myNet->getViewNet()->getNetworkViewOptions().editingElevation());1813}1814}1815}181618171818void1819GNEJunction::drawJunctionCenter(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d) const {1820if (myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getMoveOnlyJunctionCenter()) {1821// push matrix1822GLHelper::pushMatrix();1823// set color1824GLHelper::setColor(setColor(s, true).changedBrightness(-20));1825// move matrix junction center1826glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 1.7);1827// draw filled circle1828GLHelper::drawFilledCircleDetailled(d, s.neteditSizeSettings.edgeGeometryPointRadius);1829// pop matrix1830GLHelper::popMatrix();1831}1832}183318341835void1836GNEJunction::drawTLSIcon(const GUIVisualizationSettings& s) const {1837// draw TLS icon if isn't being drawn for selecting1838if (myNBNode->isTLControlled() && !myAmTLSSelected &&1839(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_TLS)) {1840GLHelper::pushMatrix();1841const Position pos = myNBNode->getPosition();1842glTranslated(pos.x(), pos.y(), 2.2);1843glColor3d(1, 1, 1);1844const double halfWidth = 32 / s.scale;1845const double halfHeight = 64 / s.scale;1846GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GUITexture::TLS), -halfWidth, -halfHeight, halfWidth, halfHeight);1847GLHelper::popMatrix();1848}1849}185018511852void1853GNEJunction::drawElevation(const GUIVisualizationSettings& s) const {1854// check if draw elevation1855if (myNet->getViewNet()->getNetworkViewOptions().editingElevation()) {1856GLHelper::pushMatrix();1857// Translate to center of junction1858glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 0.1);1859// draw Z value1860GLHelper::drawText(toString(myNBNode->getPosition().z()), Position(), GLO_MAX - 5, s.junctionID.scaledSize(s.scale), s.junctionID.color);1861GLHelper::popMatrix();1862}1863}186418651866void1867GNEJunction::drawJunctionName(const GUIVisualizationSettings& s) const {1868drawName(myNBNode->getPosition(), s.scale, s.junctionID);1869if (s.junctionName.show(this) && myNBNode->getName() != "") {1870GLHelper::drawTextSettings(s.junctionName, myNBNode->getName(), myNBNode->getPosition(), s.scale, s.angle);1871}1872}187318741875void1876GNEJunction::drawJunctionChildren(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d) const {1877// check if draw junction elements1878if (s.drawForViewObjectsHandler || (d <= GUIVisualizationSettings::Detail::JunctionElement)) {1879// draw crossings1880for (const auto& crossing : myGNECrossings) {1881crossing->drawGL(s);1882}1883// draw walking areas1884for (const auto& walkingArea : myGNEWalkingAreas) {1885walkingArea->drawGL(s);1886}1887// draw internalLanes1888for (const auto& internalLanes : myInternalLanes) {1889internalLanes->drawGL(s);1890}1891// draw connections1892for (const auto& incomingEdge : myGNEIncomingEdges) {1893for (const auto& connection : incomingEdge->getGNEConnections()) {1894connection->drawGL(s);1895}1896}1897// draw child demand elements1898for (const auto& demandElement : getChildDemandElements()) {1899demandElement->drawGL(s);1900}1901// draw child demand elements1902for (const auto& demandElement : getChildDemandElements()) {1903demandElement->drawGL(s);1904}1905// draw path additional elements1906myNet->getNetworkPathManager()->drawJunctionPathElements(s, this);1907myNet->getDemandPathManager()->drawJunctionPathElements(s, this);1908myNet->getDataPathManager()->drawJunctionPathElements(s, this);1909}1910}191119121913void1914GNEJunction::calculateJunctioncontour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,1915const double exaggeration, const bool drawBubble) const {1916// if we're selecting using a boundary, first don't calculate contour bt check if edge boundary is within selection boundary1917if (gViewObjectsHandler.selectingUsingRectangle() && gViewObjectsHandler.getSelectionTriangle().isBoundaryFullWithin(myJunctionBoundary)) {1918// simply add object in ViewObjectsHandler with full boundary1919gViewObjectsHandler.selectObject(this, getType(), false, nullptr);1920} else {1921// always calculate for shape1922myNetworkElementContour.calculateContourClosedShape(s, d, this, myNBNode->getShape(), getType(), exaggeration, this);1923// check if calculate contour for bubble1924if (drawBubble) {1925myCircleContour.calculateContourCircleShape(s, d, this, myNBNode->getPosition(), s.neteditSizeSettings.junctionBubbleRadius, getType(), exaggeration, this);1926}1927// check geometry points if we're editing shape1928if (myShapeEdited) {1929myNetworkElementContour.calculateContourAllGeometryPoints(s, d, this, myNBNode->getShape(), getType(), s.neteditSizeSettings.junctionGeometryPointRadius,1930exaggeration, true);1931}1932}1933}193419351936void1937GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value) {1938switch (key) {1939case SUMO_ATTR_KEEP_CLEAR: {1940throw InvalidArgument(toString(key) + " cannot be edited");1941}1942case SUMO_ATTR_ID: {1943myNet->getAttributeCarriers()->updateJunctionID(this, value);1944break;1945}1946case SUMO_ATTR_TYPE: {1947SumoXMLNodeType type = SUMOXMLDefinitions::NodeTypes.get(value);1948if (myNBNode->getType() == SumoXMLNodeType::PRIORITY1949&& (type == SumoXMLNodeType::RIGHT_BEFORE_LEFT || type == SumoXMLNodeType::LEFT_BEFORE_RIGHT)) {1950myNet->getNetBuilder()->getEdgeCont().removeRoundabout(myNBNode);1951}1952myNBNode->reinit(myNBNode->getPosition(), type);1953break;1954}1955case SUMO_ATTR_POSITION: {1956// set new position in NBNode updating edge boundaries1957moveJunctionGeometry(parse<Position>(value), true);1958// mark this connections and all of the junction's Neighbours as deprecated1959markConnectionsDeprecated(true);1960// update centering boundary and grid1961if (myGNEIncomingEdges.size() + myGNEOutgoingEdges.size() > 0) {1962updateCenteringBoundary(false);1963} else {1964updateCenteringBoundary(true);1965}1966break;1967}1968case GNE_ATTR_MODIFICATION_STATUS:1969if (myLogicStatus == FEATURE_GUESSED && value != FEATURE_GUESSED) {1970// clear guessed connections. previous connections will be restored1971myNBNode->invalidateIncomingConnections();1972// Clear GNEConnections of incoming edges1973for (const auto& i : myGNEIncomingEdges) {1974i->clearGNEConnections();1975}1976}1977myLogicStatus = value;1978break;1979case SUMO_ATTR_SHAPE: {1980// set new shape (without updating grid)1981myNBNode->setCustomShape(parse<PositionVector>(value));1982// mark this connections and all of the junction's neighbors as deprecated1983markConnectionsDeprecated(true);1984// update centering boundary and grid1985updateCenteringBoundary(true);1986break;1987}1988case SUMO_ATTR_RADIUS: {1989if (value.empty() || (value == "default")) {1990myNBNode->setRadius(-1);1991} else {1992myNBNode->setRadius(parse<double>(value));1993}1994break;1995}1996case SUMO_ATTR_TLTYPE: {1997// we need to make a copy of controlling TLS (because original will be updated)1998const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1999for (const auto& TLS : copyOfTls) {2000TLS->setType(SUMOXMLDefinitions::TrafficLightTypes.get(value));2001}2002break;2003}2004case SUMO_ATTR_TLLAYOUT:2005// should not be triggered (handled via GNEChange_TLS)2006break;2007case SUMO_ATTR_RIGHT_OF_WAY:2008myNBNode->setRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(value));2009break;2010case SUMO_ATTR_FRINGE:2011myNBNode->setFringeType(SUMOXMLDefinitions::FringeTypeValues.get(value));2012break;2013case SUMO_ATTR_ROUNDABOUT:2014myNBNode->setRoundaboutType(SUMOXMLDefinitions::RoundaboutTypeValues.get(value));2015break;2016case SUMO_ATTR_NAME:2017myNBNode->setName(value);2018break;2019default:2020setCommonAttribute(key, value);2021break;2022}2023// invalidate demand path calculator2024myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();2025}202620272028double2029GNEJunction::getColorValue(const GUIVisualizationSettings& /* s */, int activeScheme) const {2030switch (activeScheme) {2031case 0:2032if (myColorForMissingConnections) {2033return 3;2034} else {2035return 0;2036}2037case 1:2038return isAttributeCarrierSelected();2039case 2:2040switch (myNBNode->getType()) {2041case SumoXMLNodeType::TRAFFIC_LIGHT:2042return 0;2043case SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION:2044return 1;2045case SumoXMLNodeType::PRIORITY:2046return 2;2047case SumoXMLNodeType::PRIORITY_STOP:2048return 3;2049case SumoXMLNodeType::RIGHT_BEFORE_LEFT:2050return 4;2051case SumoXMLNodeType::ALLWAY_STOP:2052return 5;2053case SumoXMLNodeType::DISTRICT:2054return 6;2055case SumoXMLNodeType::NOJUNCTION:2056return 7;2057case SumoXMLNodeType::DEAD_END:2058case SumoXMLNodeType::DEAD_END_DEPRECATED:2059return 8;2060case SumoXMLNodeType::UNKNOWN:2061return 8; // may happen before first network computation2062case SumoXMLNodeType::INTERNAL:2063assert(false);2064return 8;2065case SumoXMLNodeType::RAIL_SIGNAL:2066return 9;2067case SumoXMLNodeType::ZIPPER:2068return 10;2069case SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED:2070return 11;2071case SumoXMLNodeType::RAIL_CROSSING:2072return 12;2073case SumoXMLNodeType::LEFT_BEFORE_RIGHT:2074return 13;2075default:2076assert(false);2077return 0;2078}2079case 3:2080return myNBNode->getPosition().z();2081default:2082assert(false);2083return 0;2084}2085}20862087void2088GNEJunction::checkMissingConnections() {2089for (auto edge : myGNEIncomingEdges) {2090if (edge->getGNEConnections().size() > 0) {2091myColorForMissingConnections = false;2092return;2093}2094}2095// no connections. Use normal color for border edges and cul-de-sac2096if (myGNEIncomingEdges.size() == 0 || myGNEOutgoingEdges.size() == 0) {2097myColorForMissingConnections = false;2098return;2099} else if (myGNEIncomingEdges.size() == 1 && myGNEOutgoingEdges.size() == 1) {2100NBEdge* in = myGNEIncomingEdges[0]->getNBEdge();2101NBEdge* out = myGNEOutgoingEdges[0]->getNBEdge();2102if (in->isTurningDirectionAt(out)) {2103myColorForMissingConnections = false;2104return;2105}2106}2107myColorForMissingConnections = true;2108}210921102111void2112GNEJunction::moveJunctionGeometry(const Position& pos, const bool updateEdgeBoundaries) {2113// reinit NBNode2114myNBNode->reinit(pos, myNBNode->getType());2115// declare three sets with all affected GNEJunctions, GNEEdges and GNEConnections2116std::set<GNEJunction*> affectedJunctions;2117std::set<GNEEdge*> affectedEdges;2118// Iterate over GNEEdges2119for (const auto& edge : getChildEdges()) {2120// Add source and destination junctions2121affectedJunctions.insert(edge->getFromJunction());2122affectedJunctions.insert(edge->getToJunction());2123// Obtain neighbors of Junction source2124for (const auto& junctionSourceEdge : edge->getFromJunction()->getChildEdges()) {2125affectedEdges.insert(junctionSourceEdge);2126}2127// Obtain neighbors of Junction destination2128for (const auto& junctionDestinationEdge : edge->getToJunction()->getChildEdges()) {2129affectedEdges.insert(junctionDestinationEdge);2130}2131}2132// reset walking areas of affected edges2133for (const auto& affectedJunction : affectedJunctions) {2134affectedJunction->clearWalkingAreas();2135}2136// Iterate over affected Edges2137for (const auto& affectedEdge : affectedEdges) {2138// update edge boundaries2139if (updateEdgeBoundaries) {2140affectedEdge->updateCenteringBoundary(true);2141}2142// Update edge geometry2143affectedEdge->updateGeometry();2144}2145}214621472148RGBColor2149GNEJunction::setColor(const GUIVisualizationSettings& s, bool bubble) const {2150// get active scheme2151const int scheme = s.junctionColorer.getActive();2152// first check if we're editing shape2153if (myShapeEdited) {2154return s.colorSettings.editShapeColor;2155}2156// set default color2157RGBColor color = s.junctionColorer.getScheme().getColor(getColorValue(s, scheme));2158// set special bubble color2159if (bubble && (scheme == 0) && !myColorForMissingConnections) {2160color = s.junctionColorer.getScheme().getColor(1);2161}2162// override with special colors (unless the color scheme is based on selection)2163if (drawUsingSelectColor() && scheme != 1) {2164color = s.colorSettings.selectionColor;2165}2166// overwrite color if we're in data mode2167if (myNet->getViewNet()->getEditModes().isCurrentSupermodeData()) {2168color = s.junctionColorer.getScheme().getColor(6);2169}2170// special color for source candidate junction2171if (mySourceCandidate) {2172color = s.candidateColorSettings.source;2173}2174// special color for target candidate junction2175if (myTargetCandidate) {2176color = s.candidateColorSettings.target;2177}2178// special color for special candidate junction2179if (mySpecialCandidate) {2180color = s.candidateColorSettings.special;2181}2182// special color for possible candidate junction2183if (myPossibleCandidate) {2184color = s.candidateColorSettings.possible;2185}2186// special color for conflicted candidate junction2187if (myConflictedCandidate) {2188color = s.candidateColorSettings.conflict;2189}2190// return color2191return color;2192}219321942195void2196GNEJunction::addTrafficLight(NBTrafficLightDefinition* tlDef, bool forceInsert) {2197NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();2198tlCont.insert(tlDef, forceInsert); // may return false for tlDef which controls multiple junctions2199tlDef->addNode(myNBNode);2200}220122022203void2204GNEJunction::removeTrafficLight(NBTrafficLightDefinition* tlDef) {2205NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();2206if (tlDef->getNodes().size() == 1) {2207tlCont.extract(tlDef);2208}2209myNBNode->removeTrafficLight(tlDef);2210}221122122213/****************************************************************************/221422152216