Path: blob/main/src/netedit/elements/network/GNEJunction.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 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_NAME:1367return myNBNode->getName();1368default:1369return getCommonAttribute(key);1370}1371}137213731374double1375GNEJunction::getAttributeDouble(SumoXMLAttr key) const {1376return getCommonAttributeDouble(key);1377}137813791380Position1381GNEJunction::getAttributePosition(SumoXMLAttr key) const {1382return getCommonAttributePosition(key);1383}138413851386PositionVector1387GNEJunction::getAttributePositionVector(SumoXMLAttr key) const {1388switch (key) {1389case SUMO_ATTR_SHAPE:1390return myNBNode->getShape();1391default:1392return getCommonAttributePositionVector(key);1393}1394}139513961397void1398GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {1399if (value == getAttribute(key)) {1400return; //avoid needless changes, later logic relies on the fact that attributes have changed1401}1402switch (key) {1403case SUMO_ATTR_ID:1404case GNE_ATTR_MODIFICATION_STATUS:1405case SUMO_ATTR_SHAPE:1406case SUMO_ATTR_RADIUS:1407case SUMO_ATTR_RIGHT_OF_WAY:1408case SUMO_ATTR_FRINGE:1409case SUMO_ATTR_NAME:1410GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);1411break;1412case SUMO_ATTR_POSITION: {1413const GNEJunction* junctionToMerge = nullptr;1414bool alreadyAsked = false;1415// parse position1416Position newPosition = GNEAttributeCarrier::parse<Position>(value);1417// check if caculate new position based in edges1418if (newPosition == Position::INVALID) {1419Boundary b;1420// set new position of adjacent edges1421for (const auto& edge : myGNEIncomingEdges) {1422b.add(edge->getNBEdge()->getGeometry().back());1423}1424for (const auto& edge : myGNEOutgoingEdges) {1425b.add(edge->getNBEdge()->getGeometry().front());1426}1427newPosition = b.getCenter();1428}1429// retrieve all junctions placed in this position1430myNet->getViewNet()->updateObjectsInPosition(newPosition);1431for (const auto& junction : myNet->getViewNet()->getViewObjectsSelector().getJunctions()) {1432// check distance position1433if ((junctionToMerge == nullptr) && (junction != this) &&1434(junction->getPositionInView().distanceTo2D(newPosition) < myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.junctionBubbleRadius) &&1435myNet->getViewNet()->askMergeJunctions(this, junction, alreadyAsked)) {1436junctionToMerge = junction;1437}1438}1439// also check the merging junctions located during drawGL1440for (const auto& junction : myNet->getViewNet()->getViewObjectsSelector().getMergingJunctions()) {1441// check distance position1442if ((junctionToMerge == nullptr) && (junction != this) && myNet->getViewNet()->askMergeJunctions(this, junction, alreadyAsked)) {1443junctionToMerge = junction;1444}1445}1446// if we merge the junction, this junction will be removed, therefore we don't have to change the position1447if (junctionToMerge) {1448myNet->mergeJunctions(this, junctionToMerge, undoList);1449} else {1450// change Keep Clear attribute in all connections1451undoList->begin(this, TL("change junction position"));1452// obtain NBNode position1453const Position orig = myNBNode->getPosition();1454// change junction position1455GNEChange_Attribute::changeAttribute(this, key, toString(newPosition), undoList, true);1456// calculate delta using new position1457const bool moveOnlyCenter = myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getMoveOnlyJunctionCenter();1458const Position delta = myNBNode->getPosition() - (moveOnlyCenter ? myNBNode->getPosition() : orig);1459// set new position of adjacent edges1460for (const auto& edge : myGNEIncomingEdges) {1461const Position newEnd = edge->getNBEdge()->getGeometry().back() + delta;1462GNEChange_Attribute::changeAttribute(edge, GNE_ATTR_SHAPE_END, toString(newEnd), undoList, true);1463}1464for (const auto& edge : myGNEOutgoingEdges) {1465const Position newStart = edge->getNBEdge()->getGeometry().front() + delta;1466GNEChange_Attribute::changeAttribute(edge, GNE_ATTR_SHAPE_START, toString(newStart), undoList, true);1467}1468undoList->end();1469}1470break;1471}1472case SUMO_ATTR_KEEP_CLEAR:1473// change Keep Clear attribute in all connections1474undoList->begin(this, TL("change keepClear for whole junction"));1475for (const auto& incomingEdge : myGNEIncomingEdges) {1476for (const auto& junction : incomingEdge->getGNEConnections()) {1477GNEChange_Attribute::changeAttribute(junction, key, value, undoList, true);1478}1479}1480undoList->end();1481break;1482case SUMO_ATTR_TYPE: {1483// set junction type1484setJunctionType(value, undoList);1485break;1486}1487case SUMO_ATTR_TLTYPE: {1488undoList->begin(this, "change " + getTagStr() + " tl-type");1489// make a copy because we will modify the original1490const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1491for (const auto& TLS : copyOfTls) {1492NBLoadedSUMOTLDef* oldLoaded = dynamic_cast<NBLoadedSUMOTLDef*>(TLS);1493if (oldLoaded != nullptr) {1494NBTrafficLightDefinition* newDef = nullptr;1495if (value == toString(TrafficLightType::NEMA) || oldLoaded->getType() == TrafficLightType::NEMA) {1496// rebuild the program because the old and new ones are incompatible1497newDef = new NBOwnTLDef(oldLoaded->getID(), oldLoaded->getOffset(), TrafficLightType::NEMA);1498newDef->setProgramID(oldLoaded->getProgramID());1499} else {1500NBLoadedSUMOTLDef* newLDef = new NBLoadedSUMOTLDef(*oldLoaded, *oldLoaded->getLogic());1501newLDef->guessMinMaxDuration(); // minDur and maxDur are never written for a static tls1502newDef = newLDef;1503}1504std::vector<NBNode*> nodes = TLS->getNodes();1505for (const auto& node : nodes) {1506GNEJunction* junction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1507undoList->add(new GNEChange_TLS(junction, TLS, false), true);1508undoList->add(new GNEChange_TLS(junction, newDef, true), true);1509}1510}1511}1512GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);1513undoList->end();1514break;1515}1516case SUMO_ATTR_TLLAYOUT: {1517undoList->begin(this, "change " + getTagStr() + " tlLayout");1518const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1519for (const auto& oldTLS : copyOfTls) {1520std::vector<NBNode*> copyOfNodes = oldTLS->getNodes();1521NBOwnTLDef* newTLS = new NBOwnTLDef(oldTLS->getID(), oldTLS->getOffset(), oldTLS->getType());1522newTLS->setLayout(SUMOXMLDefinitions::TrafficLightLayouts.get(value));1523newTLS->setProgramID(oldTLS->getProgramID());1524for (const auto& node : copyOfNodes) {1525GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1526undoList->add(new GNEChange_TLS(oldJunction, oldTLS, false), true);1527}1528for (const auto& node : copyOfNodes) {1529GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1530undoList->add(new GNEChange_TLS(oldJunction, newTLS, true), true);1531}1532}1533undoList->end();1534break;1535}1536case SUMO_ATTR_TLID: {1537undoList->begin(this, "change " + toString(SUMO_TAG_TRAFFIC_LIGHT) + " id");1538const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1539assert(copyOfTls.size() > 0);1540NBTrafficLightDefinition* currentTLS = *copyOfTls.begin();1541NBTrafficLightDefinition* currentTLSCopy = nullptr;1542const bool currentIsSingle = currentTLS->getNodes().size() == 1;1543const bool currentIsLoaded = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS) != nullptr;1544if (currentIsLoaded) {1545currentTLSCopy = new NBLoadedSUMOTLDef(*currentTLS,1546*dynamic_cast<NBLoadedSUMOTLDef*>(currentTLS)->getLogic());1547}1548// remove from previous tls1549for (const auto& TLS : copyOfTls) {1550undoList->add(new GNEChange_TLS(this, TLS, false), true);1551}1552NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();1553// programs to which the current node shall be added1554const std::map<std::string, NBTrafficLightDefinition*> programs = tlCont.getPrograms(value);1555if (programs.size() > 0) {1556for (const auto& TLSProgram : programs) {1557NBTrafficLightDefinition* oldTLS = TLSProgram.second;1558if (dynamic_cast<NBOwnTLDef*>(oldTLS) != nullptr) {1559undoList->add(new GNEChange_TLS(this, oldTLS, true), true);1560} else {1561// delete and re-create the definition because the loaded phases are now invalid1562if (dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS) != nullptr &&1563dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->usingSignalGroups()) {1564// keep the old program and add all-red state for the added links1565NBLoadedSUMOTLDef* newTLSJoined = new NBLoadedSUMOTLDef(*oldTLS, *dynamic_cast<NBLoadedSUMOTLDef*>(oldTLS)->getLogic());1566newTLSJoined->joinLogic(currentTLSCopy);1567undoList->add(new GNEChange_TLS(this, newTLSJoined, true, true), true);1568} else {1569undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);1570}1571NBTrafficLightDefinition* newTLS = *myNBNode->getControllingTLS().begin();1572// switch from old to new definition1573std::vector<NBNode*> copyOfNodes = oldTLS->getNodes();1574for (const auto& node : copyOfNodes) {1575GNEJunction* oldJunction = myNet->getAttributeCarriers()->retrieveJunction(node->getID());1576undoList->add(new GNEChange_TLS(oldJunction, oldTLS, false), true);1577undoList->add(new GNEChange_TLS(oldJunction, newTLS, true), true);1578}1579}1580}1581} else {1582if (currentIsSingle && currentIsLoaded) {1583// rename the traffic light but keep everything else1584NBTrafficLightLogic* renamedLogic = dynamic_cast<NBLoadedSUMOTLDef*>(currentTLSCopy)->getLogic();1585renamedLogic->setID(value);1586NBLoadedSUMOTLDef* renamedTLS = new NBLoadedSUMOTLDef(*currentTLSCopy, *renamedLogic);1587renamedTLS->setID(value);1588undoList->add(new GNEChange_TLS(this, renamedTLS, true, true), true);1589} else {1590// create new traffic light1591undoList->add(new GNEChange_TLS(this, nullptr, true, false, value), true);1592}1593}1594delete currentTLSCopy;1595undoList->end();1596break;1597}1598default:1599setCommonAttribute(key, value, undoList);1600break;1601}1602}160316041605bool1606GNEJunction::isValid(SumoXMLAttr key, const std::string& value) {1607switch (key) {1608case SUMO_ATTR_ID:1609return SUMOXMLDefinitions::isValidNetID(value) && (myNet->getAttributeCarriers()->retrieveJunction(value, false) == nullptr);1610case SUMO_ATTR_TYPE:1611return SUMOXMLDefinitions::NodeTypes.hasString(value);1612case SUMO_ATTR_POSITION:1613if (value.empty()) {1614return (myGNEIncomingEdges.size() + myGNEOutgoingEdges.size()) > 0;1615} else {1616return canParse<Position>(value);1617}1618case SUMO_ATTR_SHAPE:1619// empty shapes are allowed1620return canParse<PositionVector>(value);1621case SUMO_ATTR_RADIUS:1622if (value.empty() || (value == "default")) {1623return true;1624} else {1625return canParse<double>(value) && ((parse<double>(value) >= 0) || (parse<double>(value) == -1));1626}1627case SUMO_ATTR_TLTYPE:1628return myNBNode->isTLControlled() && SUMOXMLDefinitions::TrafficLightTypes.hasString(value);1629case SUMO_ATTR_TLLAYOUT:1630return myNBNode->isTLControlled() && SUMOXMLDefinitions::TrafficLightLayouts.hasString(value);1631case SUMO_ATTR_TLID:1632return myNBNode->isTLControlled() && (value != "");1633case SUMO_ATTR_KEEP_CLEAR:1634return canParse<bool>(value);1635case SUMO_ATTR_RIGHT_OF_WAY:1636return SUMOXMLDefinitions::RightOfWayValues.hasString(value);1637case SUMO_ATTR_FRINGE:1638return SUMOXMLDefinitions::FringeTypeValues.hasString(value);1639case SUMO_ATTR_NAME:1640return true;1641default:1642return isCommonAttributeValid(key, value);1643}1644}164516461647bool1648GNEJunction::isAttributeEnabled(SumoXMLAttr key) const {1649switch (key) {1650case SUMO_ATTR_TLTYPE:1651case SUMO_ATTR_TLLAYOUT:1652case SUMO_ATTR_TLID:1653return myNBNode->isTLControlled();1654case SUMO_ATTR_KEEP_CLEAR: {1655// check if at least there is an incoming connection1656for (const auto& incomingEdge : myGNEIncomingEdges) {1657if (incomingEdge->getGNEConnections().size() > 0) {1658return true;1659}1660}1661return false;1662}1663case GNE_ATTR_IS_ROUNDABOUT:1664return false;1665default:1666return true;1667}1668}166916701671bool1672GNEJunction::isAttributeComputed(SumoXMLAttr key) const {1673switch (key) {1674case SUMO_ATTR_SHAPE:1675return !myNBNode->hasCustomShape();1676default:1677return false;1678}1679}168016811682void1683GNEJunction::setResponsible(bool newVal) {1684myAmResponsible = newVal;1685}16861687// ===========================================================================1688// private1689// ===========================================================================16901691bool1692GNEJunction::drawAsBubble(const GUIVisualizationSettings& s, const double junctionShapeArea) const {1693const auto& editModes = myNet->getViewNet()->getEditModes();1694const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();1695// check conditions1696if (junctionShapeArea < 4) {1697// force draw if this junction is a candidate1698if (mySourceCandidate || myTargetCandidate || mySpecialCandidate ||1699myPossibleCandidate || myConflictedCandidate) {1700return true;1701}1702// force draw if we're in person/container plan mode1703if (editModes.isCurrentSupermodeDemand() &&1704((editModes.demandEditMode == DemandEditMode::DEMAND_PERSON) ||1705(editModes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) ||1706(editModes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) ||1707(editModes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN))) {1708return true;1709}1710// force draw if we're inspecting a vehicle that start or ends in a junction1711if (inspectedElements.isInspectingSingleElement()) {1712// check if starts or ends in this junction1713if ((inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_FROM_JUNCTION) &&1714(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_FROM_JUNCTION) == getID())) ||1715(inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_TO_JUNCTION) &&1716(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_TO_JUNCTION) == getID()))) {1717return true;1718}1719}1720}1721if (!s.drawJunctionShape) {1722// don't draw bubble if it was disabled in GUIVisualizationSettings1723return false;1724}1725if (myNet->getViewNet()->showJunctionAsBubbles()) {1726// force draw bubbles if we enabled option in checkbox of viewNet1727return true;1728}1729if (junctionShapeArea >= 4) {1730// don't draw if shape area is greater than 41731return false;1732}1733if (!editModes.isCurrentSupermodeNetwork()) {1734// only draw bubbles in network mode1735return false;1736}1737return true;1738}173917401741void1742GNEJunction::drawJunctionAsBubble(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,1743const double exaggeration) const {1744// calculate bubble radius1745const double bubbleRadius = s.neteditSizeSettings.junctionBubbleRadius * exaggeration;1746// set bubble color1747const RGBColor bubbleColor = setColor(s, true);1748// push matrix1749GLHelper::pushMatrix();1750// set color1751GLHelper::setColor(bubbleColor);1752// move matrix junction center1753glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 1.5);1754// draw filled circle1755GLHelper::drawFilledCircleDetailled(d, bubbleRadius);1756// pop matrix1757GLHelper::popMatrix();1758}175917601761void1762GNEJunction::drawJunctionAsShape(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double exaggeration) const {1763// first check drawing conditions1764if (s.drawJunctionShape && (myNBNode->getShape().size() > 0)) {1765// set shape color1766const RGBColor junctionShapeColor = setColor(s, false);1767// set color1768GLHelper::setColor(junctionShapeColor);1769// adjust shape to exaggeration (check)1770if ((exaggeration > 1 || myExaggeration > 1) && exaggeration != myExaggeration) {1771myExaggeration = exaggeration;1772myTesselation.setShape(myNBNode->getShape());1773myTesselation.getShapeRef().closePolygon();1774myTesselation.getShapeRef().scaleRelative(exaggeration);1775myTesselation.myTesselation.clear();1776}1777// check if draw tesselation or or polygon1778if (d <= GUIVisualizationSettings::Detail::DrawPolygonTesselation) {1779// draw shape with high detail1780myTesselation.drawTesselation(myTesselation.getShape());1781} else {1782// draw shape1783GLHelper::drawFilledPoly(myNBNode->getShape(), true);1784}1785// draw shape points only in Network supermode1786if (myShapeEdited && myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&1787s.drawMovingGeometryPoint(exaggeration, s.neteditSizeSettings.junctionGeometryPointRadius)) {1788// set color1789const RGBColor darkerColor = junctionShapeColor.changedBrightness(-32);1790// calculate geometry1791GUIGeometry junctionGeometry;1792// obtain junction Shape1793PositionVector junctionOpenShape = myNBNode->getShape();1794// adjust shape to exaggeration1795if (exaggeration > 1) {1796junctionOpenShape.scaleRelative(exaggeration);1797}1798// update geometry1799junctionGeometry.updateGeometry(junctionOpenShape);1800// set color1801GLHelper::setColor(darkerColor);1802// draw shape1803GUIGeometry::drawGeometry(d, junctionGeometry, s.neteditSizeSettings.junctionGeometryPointRadius * 0.5);1804// draw geometry points1805GUIGeometry::drawGeometryPoints(d, junctionOpenShape, darkerColor,1806s.neteditSizeSettings.junctionGeometryPointRadius, exaggeration,1807myNet->getViewNet()->getNetworkViewOptions().editingElevation());1808}1809}1810}181118121813void1814GNEJunction::drawJunctionCenter(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d) const {1815if (myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getMoveOnlyJunctionCenter()) {1816// push matrix1817GLHelper::pushMatrix();1818// set color1819GLHelper::setColor(setColor(s, true).changedBrightness(-20));1820// move matrix junction center1821glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 1.7);1822// draw filled circle1823GLHelper::drawFilledCircleDetailled(d, s.neteditSizeSettings.edgeGeometryPointRadius);1824// pop matrix1825GLHelper::popMatrix();1826}1827}182818291830void1831GNEJunction::drawTLSIcon(const GUIVisualizationSettings& s) const {1832// draw TLS icon if isn't being drawn for selecting1833if (myNBNode->isTLControlled() && !myAmTLSSelected &&1834(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_TLS)) {1835GLHelper::pushMatrix();1836const Position pos = myNBNode->getPosition();1837glTranslated(pos.x(), pos.y(), 2.2);1838glColor3d(1, 1, 1);1839const double halfWidth = 32 / s.scale;1840const double halfHeight = 64 / s.scale;1841GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GUITexture::TLS), -halfWidth, -halfHeight, halfWidth, halfHeight);1842GLHelper::popMatrix();1843}1844}184518461847void1848GNEJunction::drawElevation(const GUIVisualizationSettings& s) const {1849// check if draw elevation1850if (myNet->getViewNet()->getNetworkViewOptions().editingElevation()) {1851GLHelper::pushMatrix();1852// Translate to center of junction1853glTranslated(myNBNode->getPosition().x(), myNBNode->getPosition().y(), 0.1);1854// draw Z value1855GLHelper::drawText(toString(myNBNode->getPosition().z()), Position(), GLO_MAX - 5, s.junctionID.scaledSize(s.scale), s.junctionID.color);1856GLHelper::popMatrix();1857}1858}185918601861void1862GNEJunction::drawJunctionName(const GUIVisualizationSettings& s) const {1863drawName(myNBNode->getPosition(), s.scale, s.junctionID);1864if (s.junctionName.show(this) && myNBNode->getName() != "") {1865GLHelper::drawTextSettings(s.junctionName, myNBNode->getName(), myNBNode->getPosition(), s.scale, s.angle);1866}1867}186818691870void1871GNEJunction::drawJunctionChildren(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d) const {1872// check if draw junction elements1873if (s.drawForViewObjectsHandler || (d <= GUIVisualizationSettings::Detail::JunctionElement)) {1874// draw crossings1875for (const auto& crossing : myGNECrossings) {1876crossing->drawGL(s);1877}1878// draw walking areas1879for (const auto& walkingArea : myGNEWalkingAreas) {1880walkingArea->drawGL(s);1881}1882// draw internalLanes1883for (const auto& internalLanes : myInternalLanes) {1884internalLanes->drawGL(s);1885}1886// draw connections1887for (const auto& incomingEdge : myGNEIncomingEdges) {1888for (const auto& connection : incomingEdge->getGNEConnections()) {1889connection->drawGL(s);1890}1891}1892// draw child demand elements1893for (const auto& demandElement : getChildDemandElements()) {1894demandElement->drawGL(s);1895}1896// draw child demand elements1897for (const auto& demandElement : getChildDemandElements()) {1898demandElement->drawGL(s);1899}1900// draw path additional elements1901myNet->getNetworkPathManager()->drawJunctionPathElements(s, this);1902myNet->getDemandPathManager()->drawJunctionPathElements(s, this);1903myNet->getDataPathManager()->drawJunctionPathElements(s, this);1904}1905}190619071908void1909GNEJunction::calculateJunctioncontour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,1910const double exaggeration, const bool drawBubble) const {1911// if we're selecting using a boundary, first don't calculate contour bt check if edge boundary is within selection boundary1912if (gViewObjectsHandler.selectingUsingRectangle() && gViewObjectsHandler.getSelectionTriangle().isBoundaryFullWithin(myJunctionBoundary)) {1913// simply add object in ViewObjectsHandler with full boundary1914gViewObjectsHandler.selectObject(this, getType(), false, nullptr);1915} else {1916// always calculate for shape1917myNetworkElementContour.calculateContourClosedShape(s, d, this, myNBNode->getShape(), getType(), exaggeration, this);1918// check if calculate contour for bubble1919if (drawBubble) {1920myCircleContour.calculateContourCircleShape(s, d, this, myNBNode->getPosition(), s.neteditSizeSettings.junctionBubbleRadius, getType(), exaggeration, this);1921}1922// check geometry points if we're editing shape1923if (myShapeEdited) {1924myNetworkElementContour.calculateContourAllGeometryPoints(s, d, this, myNBNode->getShape(), getType(), s.neteditSizeSettings.junctionGeometryPointRadius,1925exaggeration, true);1926}1927}1928}192919301931void1932GNEJunction::setAttribute(SumoXMLAttr key, const std::string& value) {1933switch (key) {1934case SUMO_ATTR_KEEP_CLEAR: {1935throw InvalidArgument(toString(key) + " cannot be edited");1936}1937case SUMO_ATTR_ID: {1938myNet->getAttributeCarriers()->updateJunctionID(this, value);1939break;1940}1941case SUMO_ATTR_TYPE: {1942SumoXMLNodeType type = SUMOXMLDefinitions::NodeTypes.get(value);1943if (myNBNode->getType() == SumoXMLNodeType::PRIORITY1944&& (type == SumoXMLNodeType::RIGHT_BEFORE_LEFT || type == SumoXMLNodeType::LEFT_BEFORE_RIGHT)) {1945myNet->getNetBuilder()->getEdgeCont().removeRoundabout(myNBNode);1946}1947myNBNode->reinit(myNBNode->getPosition(), type);1948break;1949}1950case SUMO_ATTR_POSITION: {1951// set new position in NBNode updating edge boundaries1952moveJunctionGeometry(parse<Position>(value), true);1953// mark this connections and all of the junction's Neighbours as deprecated1954markConnectionsDeprecated(true);1955// update centering boundary and grid1956if (myGNEIncomingEdges.size() + myGNEOutgoingEdges.size() > 0) {1957updateCenteringBoundary(false);1958} else {1959updateCenteringBoundary(true);1960}1961break;1962}1963case GNE_ATTR_MODIFICATION_STATUS:1964if (myLogicStatus == FEATURE_GUESSED && value != FEATURE_GUESSED) {1965// clear guessed connections. previous connections will be restored1966myNBNode->invalidateIncomingConnections();1967// Clear GNEConnections of incoming edges1968for (const auto& i : myGNEIncomingEdges) {1969i->clearGNEConnections();1970}1971}1972myLogicStatus = value;1973break;1974case SUMO_ATTR_SHAPE: {1975// set new shape (without updating grid)1976myNBNode->setCustomShape(parse<PositionVector>(value));1977// mark this connections and all of the junction's neighbors as deprecated1978markConnectionsDeprecated(true);1979// update centering boundary and grid1980updateCenteringBoundary(true);1981break;1982}1983case SUMO_ATTR_RADIUS: {1984if (value.empty() || (value == "default")) {1985myNBNode->setRadius(-1);1986} else {1987myNBNode->setRadius(parse<double>(value));1988}1989break;1990}1991case SUMO_ATTR_TLTYPE: {1992// we need to make a copy of controlling TLS (because original will be updated)1993const std::set<NBTrafficLightDefinition*> copyOfTls = myNBNode->getControllingTLS();1994for (const auto& TLS : copyOfTls) {1995TLS->setType(SUMOXMLDefinitions::TrafficLightTypes.get(value));1996}1997break;1998}1999case SUMO_ATTR_TLLAYOUT:2000// should not be triggered (handled via GNEChange_TLS)2001break;2002case SUMO_ATTR_RIGHT_OF_WAY:2003myNBNode->setRightOfWay(SUMOXMLDefinitions::RightOfWayValues.get(value));2004break;2005case SUMO_ATTR_FRINGE:2006myNBNode->setFringeType(SUMOXMLDefinitions::FringeTypeValues.get(value));2007break;2008case SUMO_ATTR_NAME:2009myNBNode->setName(value);2010break;2011default:2012setCommonAttribute(key, value);2013break;2014}2015// invalidate demand path calculator2016myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();2017}201820192020double2021GNEJunction::getColorValue(const GUIVisualizationSettings& /* s */, int activeScheme) const {2022switch (activeScheme) {2023case 0:2024if (myColorForMissingConnections) {2025return 3;2026} else {2027return 0;2028}2029case 1:2030return isAttributeCarrierSelected();2031case 2:2032switch (myNBNode->getType()) {2033case SumoXMLNodeType::TRAFFIC_LIGHT:2034return 0;2035case SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION:2036return 1;2037case SumoXMLNodeType::PRIORITY:2038return 2;2039case SumoXMLNodeType::PRIORITY_STOP:2040return 3;2041case SumoXMLNodeType::RIGHT_BEFORE_LEFT:2042return 4;2043case SumoXMLNodeType::ALLWAY_STOP:2044return 5;2045case SumoXMLNodeType::DISTRICT:2046return 6;2047case SumoXMLNodeType::NOJUNCTION:2048return 7;2049case SumoXMLNodeType::DEAD_END:2050case SumoXMLNodeType::DEAD_END_DEPRECATED:2051return 8;2052case SumoXMLNodeType::UNKNOWN:2053return 8; // may happen before first network computation2054case SumoXMLNodeType::INTERNAL:2055assert(false);2056return 8;2057case SumoXMLNodeType::RAIL_SIGNAL:2058return 9;2059case SumoXMLNodeType::ZIPPER:2060return 10;2061case SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED:2062return 11;2063case SumoXMLNodeType::RAIL_CROSSING:2064return 12;2065case SumoXMLNodeType::LEFT_BEFORE_RIGHT:2066return 13;2067default:2068assert(false);2069return 0;2070}2071case 3:2072return myNBNode->getPosition().z();2073default:2074assert(false);2075return 0;2076}2077}20782079void2080GNEJunction::checkMissingConnections() {2081for (auto edge : myGNEIncomingEdges) {2082if (edge->getGNEConnections().size() > 0) {2083myColorForMissingConnections = false;2084return;2085}2086}2087// no connections. Use normal color for border edges and cul-de-sac2088if (myGNEIncomingEdges.size() == 0 || myGNEOutgoingEdges.size() == 0) {2089myColorForMissingConnections = false;2090return;2091} else if (myGNEIncomingEdges.size() == 1 && myGNEOutgoingEdges.size() == 1) {2092NBEdge* in = myGNEIncomingEdges[0]->getNBEdge();2093NBEdge* out = myGNEOutgoingEdges[0]->getNBEdge();2094if (in->isTurningDirectionAt(out)) {2095myColorForMissingConnections = false;2096return;2097}2098}2099myColorForMissingConnections = true;2100}210121022103void2104GNEJunction::moveJunctionGeometry(const Position& pos, const bool updateEdgeBoundaries) {2105// reinit NBNode2106myNBNode->reinit(pos, myNBNode->getType());2107// declare three sets with all affected GNEJunctions, GNEEdges and GNEConnections2108std::set<GNEJunction*> affectedJunctions;2109std::set<GNEEdge*> affectedEdges;2110// Iterate over GNEEdges2111for (const auto& edge : getChildEdges()) {2112// Add source and destination junctions2113affectedJunctions.insert(edge->getFromJunction());2114affectedJunctions.insert(edge->getToJunction());2115// Obtain neighbors of Junction source2116for (const auto& junctionSourceEdge : edge->getFromJunction()->getChildEdges()) {2117affectedEdges.insert(junctionSourceEdge);2118}2119// Obtain neighbors of Junction destination2120for (const auto& junctionDestinationEdge : edge->getToJunction()->getChildEdges()) {2121affectedEdges.insert(junctionDestinationEdge);2122}2123}2124// reset walking areas of affected edges2125for (const auto& affectedJunction : affectedJunctions) {2126affectedJunction->clearWalkingAreas();2127}2128// Iterate over affected Edges2129for (const auto& affectedEdge : affectedEdges) {2130// update edge boundaries2131if (updateEdgeBoundaries) {2132affectedEdge->updateCenteringBoundary(true);2133}2134// Update edge geometry2135affectedEdge->updateGeometry();2136}2137}213821392140RGBColor2141GNEJunction::setColor(const GUIVisualizationSettings& s, bool bubble) const {2142// get active scheme2143const int scheme = s.junctionColorer.getActive();2144// first check if we're editing shape2145if (myShapeEdited) {2146return s.colorSettings.editShapeColor;2147}2148// set default color2149RGBColor color = s.junctionColorer.getScheme().getColor(getColorValue(s, scheme));2150// set special bubble color2151if (bubble && (scheme == 0) && !myColorForMissingConnections) {2152color = s.junctionColorer.getScheme().getColor(1);2153}2154// override with special colors (unless the color scheme is based on selection)2155if (drawUsingSelectColor() && scheme != 1) {2156color = s.colorSettings.selectionColor;2157}2158// overwrite color if we're in data mode2159if (myNet->getViewNet()->getEditModes().isCurrentSupermodeData()) {2160color = s.junctionColorer.getScheme().getColor(6);2161}2162// special color for source candidate junction2163if (mySourceCandidate) {2164color = s.candidateColorSettings.source;2165}2166// special color for target candidate junction2167if (myTargetCandidate) {2168color = s.candidateColorSettings.target;2169}2170// special color for special candidate junction2171if (mySpecialCandidate) {2172color = s.candidateColorSettings.special;2173}2174// special color for possible candidate junction2175if (myPossibleCandidate) {2176color = s.candidateColorSettings.possible;2177}2178// special color for conflicted candidate junction2179if (myConflictedCandidate) {2180color = s.candidateColorSettings.conflict;2181}2182// return color2183return color;2184}218521862187void2188GNEJunction::addTrafficLight(NBTrafficLightDefinition* tlDef, bool forceInsert) {2189NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();2190tlCont.insert(tlDef, forceInsert); // may return false for tlDef which controls multiple junctions2191tlDef->addNode(myNBNode);2192}219321942195void2196GNEJunction::removeTrafficLight(NBTrafficLightDefinition* tlDef) {2197NBTrafficLightLogicCont& tlCont = myNet->getTLLogicCont();2198if (tlDef->getNodes().size() == 1) {2199tlCont.extract(tlDef);2200}2201myNBNode->removeTrafficLight(tlDef);2202}220322042205/****************************************************************************/220622072208