Path: blob/main/src/netedit/elements/network/GNEEdge.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 GNEEdge.cpp14/// @author Jakob Erdmann15/// @date Feb 201116///17// A road/street connecting two junctions (netedit-version, adapted from GUIEdge)18// Basically a container for an NBEdge with drawing and editing capabilities19/****************************************************************************/2021#include <netedit/changes/GNEChange_Attribute.h>22#include <netedit/changes/GNEChange_Lane.h>23#include <netedit/elements/additional/GNERouteProbe.h>24#include <netedit/elements/demand/GNEPlanParents.h>25#include <netedit/elements/demand/GNERoute.h>26#include <netedit/elements/moving/GNEMoveElementEdge.h>27#include <netedit/frames/common/GNEDeleteFrame.h>28#include <netedit/frames/common/GNEInspectorFrame.h>29#include <netedit/frames/common/GNEMoveFrame.h>30#include <netedit/frames/data/GNEEdgeDataFrame.h>31#include <netedit/frames/data/GNEEdgeRelDataFrame.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/GNEElementTree.h>38#include <netedit/frames/GNEViewObjectSelector.h>39#include <netedit/frames/network/GNEAdditionalFrame.h>40#include <netedit/GNENet.h>41#include <netedit/GNETagPropertiesDatabase.h>42#include <netedit/GNEUndoList.h>43#include <netedit/GNEViewParent.h>44#include <utils/gui/div/GLHelper.h>45#include <utils/options/OptionsCont.h>4647#include "GNEConnection.h"48#include "GNECrossing.h"49#include "GNEEdge.h"50#include "GNEEdgeType.h"51#include "GNELaneType.h"52#include "GNEEdgeTemplate.h"53#include "GNELaneTemplate.h"5455// ===========================================================================56// defines57// ===========================================================================5859//#define DEBUG_SMOOTH_GEOM60//#define DEBUGCOND(obj) (true)61#define VEHICLE_GAP 162#define ENDPOINT_TOLERANCE 26364// ===========================================================================65// static66// ===========================================================================6768const double GNEEdge::SNAP_RADIUS = SUMO_const_halfLaneWidth;69const double GNEEdge::SNAP_RADIUS_SQUARED = (SUMO_const_halfLaneWidth* SUMO_const_halfLaneWidth);7071// ===========================================================================72// members methods73// ===========================================================================7475GNEEdge::GNEEdge(GNENet* net, NBEdge* nbe, bool wasSplit, bool loaded):76GNENetworkElement(net, nbe->getID(), SUMO_TAG_EDGE),77myNBEdge(nbe),78myAmResponsible(false),79myWasSplit(wasSplit),80myConnectionStatus(loaded ? FEATURE_LOADED : FEATURE_GUESSED),81myMoveElementEdge(new GNEMoveElementEdge(this)),82myUpdateGeometry(true) {83// set parents84setParents<GNEJunction*>({85net->getAttributeCarriers()->retrieveJunction(nbe->getFromNode()->getID()),86net->getAttributeCarriers()->retrieveJunction(nbe->getToNode()->getID())87});88// Create lanes89for (int i = 0; i < myNBEdge->getNumLanes(); i++) {90auto lane = new GNELane(this, i);91lane->incRef("GNEEdge::GNEEdge");92addChildElement(lane);93}94// update Lane geometries95for (const auto& lane : getChildLanes()) {96lane->updateGeometry();97}98// update centering boundary without updating grid99updateCenteringBoundary(false);100}101102103GNEEdge::~GNEEdge() {104// Delete references to this edge in lanes105for (const auto& lane : getChildLanes()) {106lane->decRef("GNEEdge::~GNEEdge");107if (lane->unreferenced()) {108// check if remove it from Attribute Carriers109if (myNet->getAttributeCarriers()->getLanes().count(lane) > 0) {110myNet->getAttributeCarriers()->deleteLane(lane);111}112delete lane;113}114}115// delete references to this edge in connections116for (const auto& connection : myGNEConnections) {117connection->decRef("GNEEdge::~GNEEdge");118if (connection->unreferenced()) {119// check if remove it from Attribute Carriers120if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {121myNet->getAttributeCarriers()->deleteConnection(connection);122}123delete connection;124}125}126if (myAmResponsible) {127delete myNBEdge;128}129}130131132GNEMoveElement*133GNEEdge::getMoveElement() const {134return myMoveElementEdge;135}136137138Parameterised*139GNEEdge::getParameters() {140return myNBEdge;141}142143144const Parameterised*145GNEEdge::getParameters() const {146return myNBEdge;147}148149150bool151GNEEdge::isNetworkElementValid() const {152if (getFromJunction()->getNBNode()->getPosition() != getToJunction()->getNBNode()->getPosition()) {153return true;154} else {155return false;156}157}158159160std::string161GNEEdge::getNetworkElementProblem() const {162return TLF("Parent junctions are in the same position: %, %",163toString(getFromJunction()->getNBNode()->getPosition().x()),164toString(getFromJunction()->getNBNode()->getPosition().y()));165}166167168void169GNEEdge::updateGeometry() {170// first check if myUpdateGeometry flag is enabled171if (myUpdateGeometry) {172// Update geometry of lanes173for (const auto& lane : getChildLanes()) {174lane->updateGeometry();175}176// Update geometry of connections177for (const auto& connection : myGNEConnections) {178connection->updateGeometry();179}180// Update geometry of additionals children vinculated to this edge181for (const auto& childAdditional : getChildAdditionals()) {182childAdditional->updateGeometry();183}184// Update geometry of additionals demand elements vinculated to this edge185for (const auto& childDemandElement : getChildDemandElements()) {186childDemandElement->updateGeometry();187}188// Update geometry of additionals generic datas vinculated to this edge189for (const auto& childGenericData : getChildGenericDatas()) {190childGenericData->updateGeometry();191}192// compute geometry of path elements elements vinculated with this edge (depending of showDemandElements)193if (myNet->getViewNet() && myNet->getViewNet()->getNetworkViewOptions().showDemandElements()) {194for (const auto& childAdditional : getChildAdditionals()) {195childAdditional->computePathElement();196}197for (const auto& childDemandElement : getChildDemandElements()) {198childDemandElement->computePathElement();199}200for (const auto& childGenericData : getChildGenericDatas()) {201childGenericData->computePathElement();202}203}204}205// update vehicle geometry206updateVehicleSpreadGeometries();207// update stack labels208updateVehicleStackLabels();209updatePersonStackLabels();210updateContainerStackLabels();211}212213214Position215GNEEdge::getPositionInView() const {216return getChildLanes().front()->getPositionInView();217}218219220bool221GNEEdge::checkDrawFromContour() const {222// get modes and viewParent (for code legibility)223const auto& modes = myNet->getViewNet()->getEditModes();224const auto& viewParent = myNet->getViewParent();225const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();226// continue depending of current status227if (inspectedElements.isInspectingSingleElement()) {228// check if starts in this edge229if (inspectedElements.getFirstAC()->getTagProperty()->vehicleEdges() &&230inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_FROM) &&231(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_FROM) == getID())) {232return true;233}234} else if (modes.isCurrentSupermodeDemand()) {235// get current GNEPlanCreator236GNEPlanCreator* planCreator = nullptr;237if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {238planCreator = viewParent->getPersonFrame()->getPlanCreator();239} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {240planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();241} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {242planCreator = viewParent->getContainerFrame()->getPlanCreator();243} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {244planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();245}246// continue depending of planCreator247if (planCreator) {248if (planCreator->getPlanParameteres().fromEdge == getID()) {249return true;250} else if ((planCreator->getPlanParameteres().consecutiveEdges.size() > 0) &&251(planCreator->getPlanParameteres().consecutiveEdges.front() == getID())) {252return true;253}254} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {255const auto& selectedEdges = viewParent->getVehicleFrame()->getPathCreator()->getSelectedEdges();256// check if this is the first selected edge257if ((selectedEdges.size() > 0) && (selectedEdges.front() == this)) {258return true;259}260}261} else if (modes.isCurrentSupermodeData()) {262// get TAZRelDataFrame263const auto& edgeRelDataFrame = viewParent->getEdgeRelDataFrame();264if (edgeRelDataFrame->shown()) {265// check first Edge266if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().empty()) {267return false;268} else {269return edgeRelDataFrame->getPathCreator()->getSelectedEdges().front() == this;270}271}272}273// nothing to draw274return false;275}276277278bool279GNEEdge::checkDrawToContour() const {280// get modes and viewParent (for code legibility)281const auto& modes = myNet->getViewNet()->getEditModes();282const auto& viewParent = myNet->getViewParent();283const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();284// continue depending of current status285if (inspectedElements.isInspectingSingleElement()) {286// check if starts in this edge287if (inspectedElements.getFirstAC()->getTagProperty()->vehicleEdges() &&288inspectedElements.getFirstAC()->hasAttribute(SUMO_ATTR_TO) &&289(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_TO) == getID())) {290return true;291}292} else if (modes.isCurrentSupermodeDemand()) {293// get current GNEPlanCreator294GNEPlanCreator* planCreator = nullptr;295if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {296planCreator = viewParent->getPersonFrame()->getPlanCreator();297} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {298planCreator = viewParent->getPersonPlanFrame()->getPlanCreator();299} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {300planCreator = viewParent->getContainerFrame()->getPlanCreator();301} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {302planCreator = viewParent->getContainerPlanFrame()->getPlanCreator();303}304// continue depending of planCreator305if (planCreator) {306if (planCreator->getPlanParameteres().toEdge == getID()) {307return true;308} else if ((planCreator->getPlanParameteres().consecutiveEdges.size() > 1) &&309(planCreator->getPlanParameteres().consecutiveEdges.back() == getID())) {310return true;311}312} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {313const auto& selectedEdges = viewParent->getVehicleFrame()->getPathCreator()->getSelectedEdges();314// check if this is the last selected edge315if ((selectedEdges.size() > 1) && (selectedEdges.back() == this)) {316return true;317}318}319} else if (modes.isCurrentSupermodeData()) {320// get TAZRelDataFrame321const auto& edgeRelDataFrame = viewParent->getEdgeRelDataFrame();322if (edgeRelDataFrame->shown()) {323// check last Edge324if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().empty()) {325return false;326} else {327return edgeRelDataFrame->getPathCreator()->getSelectedEdges().back() == this;328}329}330}331// nothing to draw332return false;333}334335336bool337GNEEdge::checkDrawRelatedContour() const {338const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();339// continue depending of inspected elements340if (inspectedElements.isInspectingSingleElement() &&341inspectedElements.getFirstAC()->getTagProperty()->getTag() == SUMO_TAG_TAZ) {342// check if one of the sourceSink child is placed in this edge343for (const auto& sourceSink : getChildTAZSourceSinks()) {344if (sourceSink->getParentAdditionals().front() == inspectedElements.getFirstAC()) {345return true;346}347}348}349if (myNet->getViewNet()->getPopup()) {350return myNet->getViewNet()->getPopup()->getGLObject() == this;351}352return false;353}354355356bool357GNEEdge::checkDrawOverContour() const {358// get modes and viewParent (for code legibility)359const auto& modes = myNet->getViewNet()->getEditModes();360const auto& viewParent = myNet->getViewParent();361const auto& viewObjectsSelector = myNet->getViewNet()->getViewObjectsSelector();362// check if we're selecting edges in additional mode363if (modes.isCurrentSupermodeNetwork() && (modes.networkEditMode == NetworkEditMode::NETWORK_ADDITIONAL)) {364if (viewParent->getAdditionalFrame()->getViewObjetsSelector()->isNetworkElementSelected(this)) {365return true;366} else if (viewParent->getAdditionalFrame()->getViewObjetsSelector()->getTag() == myTagProperty->getTag()) {367return viewObjectsSelector.getEdgeFront() == this;368} else {369return false;370}371}372// check if this is the edge under cursor373if (viewObjectsSelector.getEdgeFront() != this) {374return false;375} else {376// continue depending of modes377if (modes.isCurrentSupermodeDemand()) {378// get current plan selector379GNEPlanSelector* planSelector = nullptr;380if (modes.demandEditMode == DemandEditMode::DEMAND_PERSON) {381planSelector = viewParent->getPersonFrame()->getPlanSelector();382} else if (modes.demandEditMode == DemandEditMode::DEMAND_PERSONPLAN) {383planSelector = viewParent->getPersonPlanFrame()->getPlanSelector();384} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINER) {385planSelector = viewParent->getContainerFrame()->getPlanSelector();386} else if (modes.demandEditMode == DemandEditMode::DEMAND_CONTAINERPLAN) {387planSelector = viewParent->getContainerPlanFrame()->getPlanSelector();388}389// continue depending of plan selector390if (planSelector && planSelector->markEdges()) {391return (viewObjectsSelector.getAttributeCarrierFront()->getTagProperty()->getTag() == SUMO_TAG_LANE);392} else if (modes.demandEditMode == DemandEditMode::DEMAND_VEHICLE) {393// get current vehicle template394const auto& vehicleTemplate = viewParent->getVehicleFrame()->getVehicleTagSelector()->getCurrentTemplateAC();395// check if vehicle can be placed over from-to TAZs396if (vehicleTemplate && vehicleTemplate->getTagProperty()->vehicleEdges()) {397return (viewObjectsSelector.getAttributeCarrierFront()->getTagProperty()->getTag() == SUMO_TAG_LANE);398}399}400} else if (modes.isCurrentSupermodeData()) {401// get frames402const auto& edgeDataFrame = viewParent->getEdgeDataFrame();403const auto& edgeRelDataFrame = viewParent->getEdgeRelDataFrame();404if (edgeDataFrame->shown()) {405return true;406} else if (edgeRelDataFrame->shown()) {407// check edges408if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().empty()) {409return true;410} else if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().size() == 2) {411return false;412} else if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().front() == this) {413return false;414} else if (edgeRelDataFrame->getPathCreator()->getSelectedEdges().back() == this) {415return false;416} else {417return true;418}419}420421}422// nothing to draw423return false;424}425}426427428bool429GNEEdge::checkDrawDeleteContour() const {430// first check if we're selecting edges or lanes431if (myNet->getViewNet()->checkSelectEdges()) {432// get edit modes433const auto& editModes = myNet->getViewNet()->getEditModes();434// check if we're in delete mode435if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {436// check lanes437for (const auto& lane : getChildLanes()) {438if (myNet->getViewNet()->checkOverLockedElement(lane, mySelected) &&439(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == lane)) {440return true;441}442}443// check edge444if (myNet->getViewNet()->checkOverLockedElement(this, mySelected) &&445(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this)) {446return true;447}448// nothing to draw449return false;450} else {451return false;452}453} else {454return false;455}456}457458459bool460GNEEdge::checkDrawDeleteContourSmall() const {461// get edit modes462const auto& editModes = myNet->getViewNet()->getEditModes();463// check if we're in delete mode464if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {465const auto junction = myNet->getViewNet()->getViewObjectsSelector().getJunctionFront();466if (junction == myNet->getViewNet()->getViewObjectsSelector().getAttributeCarrierFront()) {467return ((getFromJunction() == junction) || (getToJunction() == junction));468}469}470return false;471}472473474bool475GNEEdge::checkDrawSelectContour() const {476// first check if we're selecting edges or lanes477if (myNet->getViewNet()->checkSelectEdges()) {478// get edit modes479const auto& editModes = myNet->getViewNet()->getEditModes();480// check if we're in select mode481if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {482// check lanes483for (const auto& lane : getChildLanes()) {484if (myNet->getViewNet()->checkOverLockedElement(lane, mySelected) &&485(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == lane)) {486return true;487}488}489// check edge490if (myNet->getViewNet()->checkOverLockedElement(this, mySelected) &&491(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this)) {492return true;493}494// nothing to draw495return false;496} else {497return false;498}499} else {500return false;501}502}503504505bool506GNEEdge::checkDrawMoveContour() const {507// get edit modes508const auto& editModes = myNet->getViewNet()->getEditModes();509// get move element edge510const auto moveElementEdge = dynamic_cast<GNEMoveElementEdge*>(myNet->getViewNet()->getMoveSingleElementValues().getMovedElement());511// check if we're in move mode512if ((!myNet->getViewNet()->isCurrentlyMovingElements() || (moveElementEdge && (moveElementEdge->getEdge() == this))) && editModes.isCurrentSupermodeNetwork() &&513(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE)) {514// check if we're editing this network element515const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();516if (editedNetworkElement) {517if (editedNetworkElement == this) {518return true;519} else {520// check lanes521for (const auto& lane : getChildLanes()) {522if (editedNetworkElement == lane) {523return true;524}525}526}527} else {528// check lanes529for (const auto& lane : getChildLanes()) {530if (myNet->getViewNet()->checkOverLockedElement(lane, mySelected) &&531(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == lane)) {532return true;533}534}535// check edge536if (myNet->getViewNet()->checkOverLockedElement(this, mySelected) &&537(myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this)) {538return true;539}540}541// nothing to draw542return false;543} else {544return false;545}546}547548549bool550GNEEdge::hasCustomEndPoints() const {551if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {552return true;553} else if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {554return true;555} else {556return false;557}558}559560561bool562GNEEdge::clickedOverShapeStart(const Position& pos) const {563// get geometry point radius564const double geometryPointRadius = getGeometryPointRadius();565if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {566return (myNBEdge->getGeometry().front().distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius));567} else {568return false;569}570}571572573bool574GNEEdge::clickedOverShapeEnd(const Position& pos) const {575// get geometry point radius576const double geometryPointRadius = getGeometryPointRadius();577if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) {578return (myNBEdge->getGeometry().back().distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius));579} else {580return false;581}582}583584585bool586GNEEdge::clickedOverGeometryPoint(const Position& pos) const {587// get geometry point radius588const auto geometryPointRadius = getGeometryPointRadius();589// first check inner geometry590const PositionVector innenShape = myNBEdge->getInnerGeometry();591// iterate over geometry point592for (const auto& geometryPoint : innenShape) {593if (geometryPoint.distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius)) {594return true;595}596}597// check start and end shapes598if (clickedOverShapeStart(pos) || clickedOverShapeEnd(pos)) {599return true;600} else {601return false;602}603}604605606void607GNEEdge::updateJunctionPosition(GNEJunction* junction, const Position& origPos) {608Position delta = junction->getNBNode()->getPosition() - origPos;609PositionVector geom = myNBEdge->getGeometry();610// geometry endpoint need not equal junction position hence we modify it with delta611if (junction == getFromJunction()) {612geom[0].add(delta);613} else {614geom[-1].add(delta);615}616setGeometry(geom, false);617}618619620double621GNEEdge::getExaggeration(const GUIVisualizationSettings& s) const {622return s.addSize.getExaggeration(s, this);623}624625626Boundary627GNEEdge::getCenteringBoundary() const {628return myEdgeBoundary;629}630631632void633GNEEdge::updateCenteringBoundary(const bool updateGrid) {634// Remove object from net635if (updateGrid) {636myNet->removeGLObjectFromGrid(this);637}638// first add edge boundary639myEdgeBoundary = myNBEdge->getGeometry().getBoxBoundary();640// add lane boundaries641for (const auto& lane : getChildLanes()) {642const auto laneBoundary = lane->getCenteringBoundary();643if (laneBoundary.isInitialised()) {644myEdgeBoundary.add(laneBoundary);645// add additional and demand boundaries646for (const auto& additional : lane->getChildAdditionals()) {647const auto additionalBoundary = additional->getCenteringBoundary();648if (additionalBoundary.isInitialised()) {649myEdgeBoundary.add(additional->getCenteringBoundary());650}651}652}653}654// add additional and demand boundaries655for (const auto& additional : getChildAdditionals()) {656const auto additionalBoundary = additional->getCenteringBoundary();657if (additionalBoundary.isInitialised()) {658myEdgeBoundary.add(additionalBoundary);659}660}661// add junction positions662myEdgeBoundary.add(getFromJunction()->getCenteringBoundary());663myEdgeBoundary.add(getToJunction()->getCenteringBoundary());664// grow boundary665myEdgeBoundary.grow(5);666// add object into net667if (updateGrid) {668myNet->addGLObjectIntoGrid(this);669}670}671672673const std::string674GNEEdge::getOptionalName() const {675return myNBEdge->getStreetName();676}677678679GUIGLObjectPopupMenu*680GNEEdge::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {681// if we call this function, that's mean that we're clicked over a edge geometry point, then682// open the popup dialog of the lane[0] (back)683return getChildLanes().back()->getPopUpMenu(app, parent);684}685686687std::vector<GNEEdge*>688GNEEdge::getOppositeEdges() const {689return myNet->getAttributeCarriers()->retrieveEdges(getToJunction(), getFromJunction());690}691692693void694GNEEdge::drawGL(const GUIVisualizationSettings& s) const {695// check drawing boundary selection and size boundary696if (checkDrawingBoundarySelection() && s.checkDrawEdge(myEdgeBoundary)) {697// draw boundary698GLHelper::drawBoundary(s, getCenteringBoundary());699// get detail level from the first lane700const auto d = getChildLanes().front()->getDrawingConstants()->getDetail();701// calculate layer702double layer = GLO_EDGE;703if (myDrawInFront) {704layer = GLO_FRONTELEMENT;705} else if (getChildLanes().front()->getLaneShape().length2D() <= (s.neteditSizeSettings.junctionBubbleRadius * 2)) {706layer = GLO_JUNCTION + 1.8;707}708// check if draw details709if (!s.drawForViewObjectsHandler) {710// draw geometry points711drawEdgeGeometryPoints(s, d, layer);712// draw edge shape (a red line only visible if lane shape is strange)713drawEdgeShape(s, d, layer);714// draw edge stopOffset715drawLaneStopOffset(s, d, layer);716// draw edge name717drawEdgeName(s);718// draw lock icon719GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), 1);720// draw dotted contour721myNetworkElementContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);722}723// calculate edge contour (always before children)724calculateEdgeContour(s, d, layer);725// draw lanes726for (const auto& lane : getChildLanes()) {727lane->drawGL(s);728}729// draw junctions730getFromJunction()->drawGL(s);731getToJunction()->drawGL(s);732// draw childrens733drawChildrens(s);734}735}736737738void739GNEEdge::deleteGLObject() {740// Check if edge can be deleted741if (GNEDeleteFrame::SubordinatedElements(this).checkElements(myNet->getViewParent()->getDeleteFrame()->getProtectElements())) {742myNet->deleteEdge(this, myNet->getUndoList(), false);743}744}745746747void748GNEEdge::updateGLObject() {749updateGeometry();750}751752753NBEdge*754GNEEdge::getNBEdge() const {755return myNBEdge;756}757758759Position760GNEEdge::getSplitPos(const Position& clickPos) {761// get geometry point radius762const double geometryPointRadius = getGeometryPointRadius();763const PositionVector& geom = myNBEdge->getGeometry();764int index = geom.indexOfClosest(clickPos, true);765if (geom[index].distanceSquaredTo2D(clickPos) < (geometryPointRadius * geometryPointRadius)) {766// split at existing geometry point767return myNet->getViewNet()->snapToActiveGrid(geom[index]);768} else {769// split straight between the next two points770return myNet->getViewNet()->snapToActiveGrid(geom.positionAtOffset(geom.nearest_offset_to_point2D(clickPos)));771}772}773774775void776GNEEdge::editEndpoint(Position pos, GNEUndoList* undoList) {777// get geometry point radius778const double geometryPointRadius = getGeometryPointRadius();779if ((myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) &&780(myNBEdge->getGeometry().front().distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius))) {781undoList->begin(this, "remove endpoint");782setAttribute(GNE_ATTR_SHAPE_START, "", undoList);783undoList->end();784} else if ((myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) > ENDPOINT_TOLERANCE) &&785(myNBEdge->getGeometry().back().distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius))) {786undoList->begin(this, "remove endpoint");787setAttribute(GNE_ATTR_SHAPE_END, "", undoList);788undoList->end();789} else {790// we need to create new Start/End position over Edge shape, not over clicked position791double offset = myNBEdge->getGeometry().nearest_offset_to_point2D(myNet->getViewNet()->snapToActiveGrid(pos), true);792if (offset != GeomHelper::INVALID_OFFSET) {793PositionVector geom = myNBEdge->getGeometry();794// calculate position over edge shape relative to clicked position795Position newPos = geom.positionAtOffset2D(offset);796// snap new position to grid797newPos = myNet->getViewNet()->snapToActiveGrid(newPos);798undoList->begin(this, "set endpoint");799const int index = geom.indexOfClosest(pos, true);800const Position destPos = getToJunction()->getNBNode()->getPosition();801const Position sourcePos = getFromJunction()->getNBNode()->getPosition();802if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {803// check if snap to existing geometrypoint804if (geom[index].distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius)) {805newPos = geom[index];806// remove existent geometry point to avoid double points807myMoveElementEdge->removeGeometryPoint(newPos, undoList);808}809setAttribute(GNE_ATTR_SHAPE_END, toString(newPos), undoList);810getToJunction()->invalidateShape();811} else {812// check if snap to existing geometry point813if (geom[index].distanceSquaredTo2D(pos) < (geometryPointRadius * geometryPointRadius)) {814newPos = geom[index];815// remove existent geometry point to avoid double points816myMoveElementEdge->removeGeometryPoint(newPos, undoList);817}818setAttribute(GNE_ATTR_SHAPE_START, toString(newPos), undoList);819getFromJunction()->invalidateShape();820}821undoList->end();822}823}824}825826827void828GNEEdge::resetEndpoint(const Position& pos, GNEUndoList* undoList) {829Position destPos = getToJunction()->getNBNode()->getPosition();830Position sourcePos = getFromJunction()->getNBNode()->getPosition();831if (pos.distanceTo2D(destPos) < pos.distanceTo2D(sourcePos)) {832setAttribute(GNE_ATTR_SHAPE_END, toString(destPos), undoList);833getToJunction()->invalidateShape();834} else {835setAttribute(GNE_ATTR_SHAPE_START, toString(sourcePos), undoList);836getFromJunction()->invalidateShape();837}838}839840841void842GNEEdge::resetBothEndpoint(GNEUndoList* undoList) {843// reset shape start844setAttribute(GNE_ATTR_SHAPE_END, "", undoList);845getToJunction()->invalidateShape();846// reset shape end847setAttribute(GNE_ATTR_SHAPE_START, "", undoList);848getFromJunction()->invalidateShape();849}850851void852GNEEdge::setGeometry(PositionVector geom, bool inner) {853// set new geometry854const bool lefthand = OptionsCont::getOptions().getBool("lefthand");855if (lefthand) {856geom.mirrorX();857myNBEdge->mirrorX();858}859myNBEdge->setGeometry(geom, inner);860if (lefthand) {861myNBEdge->mirrorX();862}863// update geometry864updateGeometry();865// invalidate junction source shape866getFromJunction()->invalidateShape();867// iterate over first parent junction edges and update geometry868for (const auto& edge : getFromJunction()->getGNEIncomingEdges()) {869edge->updateGeometry();870}871for (const auto& edge : getFromJunction()->getGNEOutgoingEdges()) {872edge->updateGeometry();873}874// invalidate junction destination shape875getToJunction()->invalidateShape();876// iterate over second parent junction edges and update geometry877for (const auto& edge : getToJunction()->getGNEIncomingEdges()) {878edge->updateGeometry();879}880for (const auto& edge : getToJunction()->getGNEOutgoingEdges()) {881edge->updateGeometry();882}883}884885886const Position887GNEEdge::getFrontUpShapePosition() const {888PositionVector laneShape = getChildLanes().front()->getLaneShape();889laneShape.move2side(getChildLanes().front()->getParentEdge()->getNBEdge()->getLaneWidth(getChildLanes().front()->getIndex()) / 2);890return laneShape.front();891}892893894const Position895GNEEdge::getFrontDownShapePosition() const {896PositionVector laneShape = getChildLanes().back()->getLaneShape();897laneShape.move2side(-1 * getChildLanes().back()->getParentEdge()->getNBEdge()->getLaneWidth(getChildLanes().back()->getIndex()) / 2);898return laneShape.front();899}900901902const Position903GNEEdge::getBackUpShapePosition() const {904PositionVector laneShape = getChildLanes().front()->getLaneShape();905laneShape.move2side(getChildLanes().front()->getParentEdge()->getNBEdge()->getLaneWidth(getChildLanes().front()->getIndex()) / 2);906return laneShape.back();907}908909910const Position911GNEEdge::getBackDownShapePosition() const {912PositionVector laneShape = getChildLanes().back()->getLaneShape();913laneShape.move2side(-1 * getChildLanes().back()->getParentEdge()->getNBEdge()->getLaneWidth(getChildLanes().back()->getIndex()) / 2);914return laneShape.back();915}916917void918GNEEdge::remakeGNEConnections(bool junctionsReady) {919// create new and removed unused GNEConnections920const std::vector<NBEdge::Connection>& connections = myNBEdge->getConnections();921// create a vector to keep retrieved and created connections922std::vector<GNEConnection*> retrievedConnections;923// iterate over NBEdge::Connections of GNEEdge924for (const auto& connection : connections) {925// retrieve existent GNEConnection, or create it926GNEConnection* retrievedGNEConnection = retrieveGNEConnection(connection.fromLane, connection.toEdge, connection.toLane);927if (junctionsReady) {928retrievedGNEConnection->updateLinkState();929}930retrievedConnections.push_back(retrievedGNEConnection);931// check if previously this GNEConnections exists, and if true, remove it from myGNEConnections932std::vector<GNEConnection*>::iterator retrievedExists = std::find(myGNEConnections.begin(), myGNEConnections.end(), retrievedGNEConnection);933if (retrievedExists != myGNEConnections.end()) {934myGNEConnections.erase(retrievedExists);935} else {936// include reference to created GNEConnection937retrievedGNEConnection->incRef("GNEEdge::remakeGNEConnections");938}939// mark it as deprecated940retrievedGNEConnection->markConnectionGeometryDeprecated();941}942// delete non retrieved GNEConnections943for (const auto& connection : myGNEConnections) {944// decrease reference945connection->decRef();946// remove it from network947myNet->removeGLObjectFromGrid(connection);948// and from AttributeCarriers949if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {950myNet->getAttributeCarriers()->deleteConnection(connection);951}952// delete GNEConnection if is unreferenced953if (connection->unreferenced()) {954delete connection;955}956}957// copy retrieved (existent and created) GNECrossings to myGNEConnections958myGNEConnections = retrievedConnections;959}960961962void963GNEEdge::clearGNEConnections() {964// Drop all existents connections that aren't referenced anymore965for (const auto& connection : myGNEConnections) {966// check if connection is selected967if (connection->isAttributeCarrierSelected()) {968connection->unselectAttributeCarrier();969}970// Dec reference of connection971connection->decRef("GNEEdge::clearGNEConnections");972// remove it from network973myNet->removeGLObjectFromGrid(connection);974// and from AttributeCarriers975if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {976myNet->getAttributeCarriers()->deleteConnection(connection);977}978// Delete GNEConnectionToErase if is unreferenced979if (connection->unreferenced()) {980delete connection;981}982}983myGNEConnections.clear();984}985986987int988GNEEdge::getRouteProbeRelativePosition(GNERouteProbe* routeProbe) const {989std::vector<GNEAdditional*> routeProbes;990for (const auto& additional : getChildAdditionals()) {991if (additional->getTagProperty()->getTag() == routeProbe->getTagProperty()->getTag()) {992routeProbes.push_back(additional);993}994}995// return index of routeProbe in routeProbes vector996auto it = std::find(routeProbes.begin(), routeProbes.end(), routeProbe);997if (it == routeProbes.end()) {998return -1;999} else {1000return (int)(it - routeProbes.begin());1001}1002}100310041005std::vector<GNECrossing*>1006GNEEdge::getGNECrossings() {1007std::vector<GNECrossing*> crossings;1008for (auto i : getFromJunction()->getGNECrossings()) {1009if (i->checkEdgeBelong(this)) {1010crossings.push_back(i);1011}1012}1013for (auto i : getToJunction()->getGNECrossings()) {1014if (i->checkEdgeBelong(this)) {1015crossings.push_back(i);1016}1017}1018return crossings;1019}102010211022void1023GNEEdge::copyTemplate(const GNEEdgeTemplate* edgeTemplate, GNEUndoList* undoList) {1024// copy edge-specific attributes1025for (const auto& attProperty : myTagProperty->getAttributeProperties()) {1026if (attProperty->isCopyable() && isValid(attProperty->getAttr(), edgeTemplate->getAttribute(attProperty->getAttr()))) {1027setAttribute(attProperty->getAttr(), edgeTemplate->getAttribute(attProperty->getAttr()), undoList);1028}1029}1030// also copy parameters1031setAttribute(GNE_ATTR_PARAMETERS, edgeTemplate->getAttribute(GNE_ATTR_PARAMETERS), undoList);1032// copy lane attributes as well1033for (int i = 0; i < (int)getChildLanes().size(); i++) {1034for (const auto& attProperty : edgeTemplate->getLaneTemplates().at(i)->getTagProperty()->getAttributeProperties()) {1035if (attProperty->isCopyable() && getChildLanes()[i]->isValid(attProperty->getAttr(), edgeTemplate->getLaneTemplates().at(i)->getAttribute(attProperty->getAttr()))) {1036getChildLanes()[i]->setAttribute(attProperty->getAttr(), edgeTemplate->getLaneTemplates().at(i)->getAttribute(attProperty->getAttr()), undoList);1037}1038}1039// also copy parameters1040getChildLanes()[i]->setAttribute(GNE_ATTR_PARAMETERS, edgeTemplate->getLaneTemplates().at(i)->getAttribute(GNE_ATTR_PARAMETERS), undoList);1041}1042}104310441045void1046GNEEdge::copyEdgeType(const GNEEdgeType* edgeType, GNEUndoList* undoList) {1047const auto tagPropertiesDatabase = myNet->getTagPropertiesDatabase();1048// get tag properties1049const auto edgeProperties = tagPropertiesDatabase->getTagProperty(SUMO_TAG_EDGE, true);1050const auto laneProperties = tagPropertiesDatabase->getTagProperty(SUMO_TAG_LANE, true);1051const auto edgeTypeProperties = tagPropertiesDatabase->getTagProperty(SUMO_TAG_TYPE, true);1052const auto laneTypeProperties = tagPropertiesDatabase->getTagProperty(SUMO_TAG_LANETYPE, true);1053// set type (only for info)1054setAttribute(SUMO_ATTR_TYPE, edgeType->getAttribute(SUMO_ATTR_ID), undoList);1055// copy attributes1056for (const auto& attrProperty : edgeTypeProperties->getAttributeProperties()) {1057if (attrProperty->isCopyable() && edgeProperties->hasAttribute(attrProperty->getAttr())) {1058setAttribute(attrProperty->getAttr(), edgeType->getAttribute(attrProperty->getAttr()), undoList);1059}1060}1061setAttribute(GNE_ATTR_PARAMETERS, edgeType->getAttribute(GNE_ATTR_PARAMETERS), undoList);1062// copy lane attributes as well1063for (int i = 0; i < (int)getChildLanes().size(); i++) {1064for (const auto& attrProperty : laneTypeProperties->getAttributeProperties()) {1065if (attrProperty->isCopyable() && laneProperties->hasAttribute(attrProperty->getAttr()) &&1066(edgeType->getLaneTypes().at(i)->getAttribute(attrProperty->getAttr()) != laneTypeProperties->getAttributeProperties(attrProperty->getAttr())->getDefaultStringValue())) {1067getChildLanes()[i]->setAttribute(attrProperty->getAttr(), edgeType->getLaneTypes().at(i)->getAttribute(attrProperty->getAttr()), undoList);1068}1069}1070if (edgeType->getLaneTypes().at(i)->getAttribute(GNE_ATTR_PARAMETERS).size() > 0) {1071getChildLanes()[i]->setAttribute(GNE_ATTR_PARAMETERS, edgeType->getLaneTypes().at(i)->getAttribute(GNE_ATTR_PARAMETERS), undoList);1072}1073}1074}107510761077std::set<GUIGlID>1078GNEEdge::getLaneGlIDs() const {1079std::set<GUIGlID> result;1080for (const auto glID : getChildLanes()) {1081result.insert(glID->getGlID());1082}1083return result;1084}108510861087const std::vector<GNEConnection*>&1088GNEEdge::getGNEConnections() const {1089return myGNEConnections;1090}109110921093bool1094GNEEdge::wasSplit() {1095return myWasSplit;1096}109710981099std::string1100GNEEdge::getAttribute(SumoXMLAttr key) const {1101switch (key) {1102case SUMO_ATTR_ID:1103return getMicrosimID();1104case SUMO_ATTR_FROM:1105case SUMO_ATTR_FROM_JUNCTION:1106return getFromJunction()->getID();1107case SUMO_ATTR_TO:1108case SUMO_ATTR_TO_JUNCTION:1109return getToJunction()->getID();1110case SUMO_ATTR_NUMLANES:1111return toString(myNBEdge->getNumLanes());1112case SUMO_ATTR_PRIORITY:1113return toString(myNBEdge->getPriority());1114case SUMO_ATTR_LENGTH:1115return toString(myNBEdge->getFinalLength());1116case SUMO_ATTR_TYPE:1117return myNBEdge->getTypeID();1118case SUMO_ATTR_SHAPE:1119return toString(myNBEdge->getInnerGeometry());1120case SUMO_ATTR_SPREADTYPE:1121return SUMOXMLDefinitions::LaneSpreadFunctions.getString(myNBEdge->getLaneSpreadFunction());1122case SUMO_ATTR_NAME:1123return myNBEdge->getStreetName();1124case SUMO_ATTR_ALLOW:1125return (getVehicleClassNames(myNBEdge->getPermissions()) + (myNBEdge->hasLaneSpecificPermissions() ? " (combined!)" : ""));1126case SUMO_ATTR_DISALLOW: {1127return (getVehicleClassNames(invertPermissions(myNBEdge->getPermissions())) + (myNBEdge->hasLaneSpecificPermissions() ? " (combined!)" : ""));1128}1129case SUMO_ATTR_SPEED:1130if (myNBEdge->hasLaneSpecificSpeed()) {1131return "lane specific";1132} else {1133return toString(myNBEdge->getSpeed());1134}1135case SUMO_ATTR_FRICTION:1136if (myNBEdge->hasLaneSpecificFriction()) {1137return "lane specific";1138} else {1139return toString(myNBEdge->getFriction());1140}1141case SUMO_ATTR_WIDTH:1142if (myNBEdge->hasLaneSpecificWidth()) {1143return "lane specific";1144} else if (myNBEdge->getLaneWidth() == NBEdge::UNSPECIFIED_WIDTH) {1145return "default";1146} else {1147return toString(myNBEdge->getLaneWidth());1148}1149case SUMO_ATTR_ENDOFFSET:1150if (myNBEdge->hasLaneSpecificEndOffset()) {1151return "lane specific";1152} else {1153return toString(myNBEdge->getEndOffset());1154}1155case SUMO_ATTR_DISTANCE:1156return toString(myNBEdge->getDistance());1157case GNE_ATTR_MODIFICATION_STATUS:1158return myConnectionStatus;1159case GNE_ATTR_SHAPE_START:1160if (getParentJunctions().empty()) {1161return "";1162} else if (myNBEdge->getGeometry().front().distanceSquaredTo2D(getFromJunction()->getNBNode()->getPosition()) <= ENDPOINT_TOLERANCE) {1163return "";1164} else {1165return toString(myNBEdge->getGeometry().front());1166}1167case GNE_ATTR_SHAPE_END:1168if (getParentJunctions().empty()) {1169return "";1170} else if (myNBEdge->getGeometry().back().distanceSquaredTo2D(getToJunction()->getNBNode()->getPosition()) <= ENDPOINT_TOLERANCE) {1171return "";1172} else {1173return toString(myNBEdge->getGeometry().back());1174}1175case GNE_ATTR_BIDIR:1176return toString(myNBEdge->getBidiEdge() != nullptr);1177case GNE_ATTR_STOPOFFSET:1178return toString(myNBEdge->myEdgeStopOffset.getOffset());1179case GNE_ATTR_STOPOEXCEPTION:1180if (myNBEdge->myEdgeStopOffset.isDefined()) {1181return toString(myNBEdge->myEdgeStopOffset.getExceptions());1182} else {1183return "";1184}1185case GNE_ATTR_IS_ROUNDABOUT:1186return myNBEdge->getFromNode()->isRoundabout() && myNBEdge->getToNode()->isRoundabout() ? TRUE_STR : FALSE_STR;1187default:1188return getCommonAttribute(key);1189}1190}119111921193double1194GNEEdge::getAttributeDouble(SumoXMLAttr key) const {1195return getCommonAttributeDouble(key);1196}119711981199Position1200GNEEdge::getAttributePosition(SumoXMLAttr key) const {1201return getCommonAttributePosition(key);1202}120312041205PositionVector1206GNEEdge::getAttributePositionVector(SumoXMLAttr key) const {1207switch (key) {1208case SUMO_ATTR_SHAPE:1209return myNBEdge->getInnerGeometry();1210default:1211return getCommonAttributePositionVector(key);1212}1213}121412151216std::string1217GNEEdge::getAttributeForSelection(SumoXMLAttr key) const {1218std::string result = getAttribute(key);1219if ((key == SUMO_ATTR_ALLOW || key == SUMO_ATTR_DISALLOW) && result.find("all") != std::string::npos) {1220result += " " + getVehicleClassNames(SVCAll, true);1221}1222return result;1223}122412251226void1227GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {1228// get template editor1229GNEInspectorFrame::TemplateEditor* templateEditor = myNet->getViewParent()->getInspectorFrame()->getTemplateEditor();1230// check if we have to update template1231const bool updateTemplate = templateEditor->getEdgeTemplate() ? (templateEditor->getEdgeTemplate()->getID() == getID()) : false;1232switch (key) {1233case SUMO_ATTR_WIDTH:1234case SUMO_ATTR_ENDOFFSET:1235case SUMO_ATTR_SPEED:1236case SUMO_ATTR_FRICTION:1237case SUMO_ATTR_ALLOW:1238case SUMO_ATTR_DISALLOW: {1239undoList->begin(this, "change " + getTagStr() + " attribute");1240const std::string origValue = getChildLanes().at(0)->getAttribute(key); // will have intermediate value of "lane specific"1241// lane specific attributes need to be changed via lanes to allow undo1242for (auto it : getChildLanes()) {1243it->setAttribute(key, value, undoList);1244}1245// ensure that the edge value is also changed. Actually this sets the lane attributes again but it does not matter1246GNEChange_Attribute::changeAttribute(this, key, value, origValue, undoList);1247undoList->end();1248break;1249}1250case SUMO_ATTR_FROM: {1251if (value != getAttribute(key)) {1252undoList->begin(this, "change " + getTagStr() + " attribute");1253// Remove edge from crossings of junction source1254removeEdgeFromCrossings(getFromJunction(), undoList);1255// continue changing from junction1256GNEJunction* originalFirstParentJunction = getFromJunction();1257getFromJunction()->setLogicValid(false, undoList);1258GNEChange_Attribute::changeAttribute(this, key, value, undoList);1259getFromJunction()->setLogicValid(false, undoList);1260myNet->getAttributeCarriers()->retrieveJunction(value)->setLogicValid(false, undoList);1261setAttribute(GNE_ATTR_SHAPE_START, toString(getFromJunction()->getNBNode()->getPosition()), undoList);1262getFromJunction()->invalidateShape();1263undoList->end();1264// update geometries of all implicated junctions1265originalFirstParentJunction->updateGeometry();1266getFromJunction()->updateGeometry();1267getToJunction()->updateGeometry();1268}1269break;1270}1271case SUMO_ATTR_TO: {1272if (value != getAttribute(key)) {1273undoList->begin(this, "change " + getTagStr() + " attribute");1274// Remove edge from crossings of junction destination1275removeEdgeFromCrossings(getToJunction(), undoList);1276// continue changing destination junction1277GNEJunction* originalSecondParentJunction = getToJunction();1278getToJunction()->setLogicValid(false, undoList);1279GNEChange_Attribute::changeAttribute(this, key, value, undoList);1280getToJunction()->setLogicValid(false, undoList);1281myNet->getAttributeCarriers()->retrieveJunction(value)->setLogicValid(false, undoList);1282setAttribute(GNE_ATTR_SHAPE_END, toString(getToJunction()->getNBNode()->getPosition()), undoList);1283getToJunction()->invalidateShape();1284undoList->end();1285// update geometries of all implicated junctions1286originalSecondParentJunction->updateGeometry();1287getToJunction()->updateGeometry();1288getFromJunction()->updateGeometry();1289}1290break;1291}1292case SUMO_ATTR_ID:1293case SUMO_ATTR_PRIORITY:1294case SUMO_ATTR_LENGTH:1295case SUMO_ATTR_TYPE:1296case SUMO_ATTR_SPREADTYPE:1297case SUMO_ATTR_DISTANCE:1298case GNE_ATTR_MODIFICATION_STATUS:1299case GNE_ATTR_STOPOEXCEPTION:1300GNEChange_Attribute::changeAttribute(this, key, value, undoList);1301break;1302case GNE_ATTR_STOPOFFSET:1303// special case for stop offset, because affects to stopOffsetExceptions (#15297)1304if (canParse<double>(value) && (parse<double>(value) == 0)) {1305GNEChange_Attribute::changeAttribute(this, GNE_ATTR_STOPOEXCEPTION, "", undoList);1306}1307GNEChange_Attribute::changeAttribute(this, key, value, undoList);1308break;1309case GNE_ATTR_SHAPE_START:1310case GNE_ATTR_SHAPE_END: {1311// due to ENDPOINT_TOLERANCE, force change1312GNEChange_Attribute::changeAttribute(this, key, value, undoList, true);1313break;1314}1315case SUMO_ATTR_NAME:1316// user cares about street names. Make sure they appear in the output1317OptionsCont::getOptions().resetWritable();1318OptionsCont::getOptions().set("output.street-names", "true");1319GNEChange_Attribute::changeAttribute(this, key, value, undoList);1320break;1321case SUMO_ATTR_NUMLANES:1322if (value != getAttribute(key)) {1323// set num lanes1324setNumLanes(parse<int>(value), undoList);1325}1326break;1327case GNE_ATTR_BIDIR:1328undoList->begin(this, "change " + getTagStr() + " attribute");1329GNEChange_Attribute::changeAttribute(this, key, value, undoList);1330if (myNBEdge->getTurnDestination(true) != nullptr) {1331GNEEdge* bidi = myNet->getAttributeCarriers()->retrieveEdge(myNBEdge->getTurnDestination(true)->getID());1332GNEChange_Attribute::changeAttribute(bidi, key, value, undoList);1333if (myNBEdge->getGeometry() != bidi->getNBEdge()->getGeometry().reverse()1334&& myNBEdge->getGeometry().size() == 21335&& bidi->getNBEdge()->getGeometry().size() == 21336&& myNBEdge->getBidiEdge() == nullptr) {1337// NBEdge::avoidOverlap was already active so we need to reset the1338// geometry to its default1339resetBothEndpoint(undoList);1340bidi->resetBothEndpoint(undoList);1341}1342}1343undoList->end();1344break;1345case SUMO_ATTR_SHAPE:1346// @note: assumes value of inner geometry!1347// actually the geometry is already updated (incrementally1348// during mouse movement). We set the restore point to the end1349// of the last change-set1350GNEChange_Attribute::changeAttribute(this, key, value, undoList);1351break;1352default:1353setCommonAttribute(key, value, undoList);1354break;1355}1356// update template1357if (updateTemplate) {1358templateEditor->setEdgeTemplate(this);1359}1360}136113621363bool1364GNEEdge::isValid(SumoXMLAttr key, const std::string& value) {1365switch (key) {1366case SUMO_ATTR_ID:1367return SUMOXMLDefinitions::isValidNetID(value) && (myNet->getAttributeCarriers()->retrieveEdge(value, false) == nullptr);1368case SUMO_ATTR_FROM: {1369// check that is a valid ID and is different of ID of junction destination1370if (value == getFromJunction()->getID()) {1371return true;1372} else if (SUMOXMLDefinitions::isValidNetID(value) && (value != getToJunction()->getID())) {1373return (myNet->getAttributeCarriers()->retrieveJunction(value, false) != nullptr);1374} else {1375return false;1376}1377}1378case SUMO_ATTR_TO: {1379// check that is a valid ID and is different of ID of junction Source1380if (value == getToJunction()->getID()) {1381return true;1382} else if (SUMOXMLDefinitions::isValidNetID(value) && (value != getFromJunction()->getID())) {1383return (myNet->getAttributeCarriers()->retrieveJunction(value, false) != nullptr);1384} else {1385return false;1386}1387}1388case SUMO_ATTR_SPEED:1389return canParse<double>(value) && (parse<double>(value) > 0);1390case SUMO_ATTR_FRICTION:1391return canParse<double>(value) && (parse<double>(value) > 0);1392case SUMO_ATTR_NUMLANES:1393return canParse<int>(value) && (parse<double>(value) > 0);1394case SUMO_ATTR_PRIORITY:1395return canParse<int>(value);1396case SUMO_ATTR_LENGTH:1397if (value.empty()) {1398return true;1399} else {1400return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_LOADED_LENGTH));1401}1402case SUMO_ATTR_ALLOW:1403case SUMO_ATTR_DISALLOW:1404return canParseVehicleClasses(value);1405case SUMO_ATTR_TYPE:1406return true;1407case SUMO_ATTR_SHAPE:1408// empty shapes are allowed1409return canParse<PositionVector>(value);1410case SUMO_ATTR_SPREADTYPE:1411return SUMOXMLDefinitions::LaneSpreadFunctions.hasString(value);1412case SUMO_ATTR_NAME:1413return true;1414case SUMO_ATTR_WIDTH:1415if (value.empty() || (value == "default")) {1416return true;1417} else {1418return canParse<double>(value) && ((parse<double>(value) >= -1) || (parse<double>(value) == NBEdge::UNSPECIFIED_WIDTH));1419}1420case SUMO_ATTR_ENDOFFSET:1421return canParse<double>(value) && parse<double>(value) >= 0 && parse<double>(value) < myNBEdge->getLoadedLength();1422case SUMO_ATTR_DISTANCE:1423if (value.empty()) {1424return true;1425} else {1426return canParse<double>(value);1427}1428case GNE_ATTR_SHAPE_START: {1429if (value.empty()) {1430return true;1431} else if (canParse<Position>(value)) {1432Position shapeStart = parse<Position>(value);1433return (shapeStart != myNBEdge->getGeometry()[-1]);1434} else {1435return false;1436}1437}1438case GNE_ATTR_SHAPE_END: {1439if (value.empty()) {1440return true;1441} else if (canParse<Position>(value)) {1442Position shapeEnd = parse<Position>(value);1443return (shapeEnd != myNBEdge->getGeometry()[0]);1444} else {1445return false;1446}1447}1448case GNE_ATTR_BIDIR:1449return canParse<bool>(value) && (!parse<bool>(value) || myNBEdge->isBidiEdge(true));1450case GNE_ATTR_STOPOFFSET:1451if (value.empty()) {1452return true;1453} else {1454return canParse<double>(value) && (parse<double>(value) >= 0);1455}1456case GNE_ATTR_STOPOEXCEPTION:1457return canParseVehicleClasses(value);1458default:1459return isCommonAttributeValid(key, value);1460}1461}146214631464bool1465GNEEdge::isAttributeEnabled(SumoXMLAttr key) const {1466switch (key) {1467case GNE_ATTR_BIDIR:1468return myNBEdge->isBidiEdge(true);1469case GNE_ATTR_STOPOEXCEPTION:1470return myNBEdge->myEdgeStopOffset.getOffset() > 0;1471case GNE_ATTR_IS_ROUNDABOUT:1472return false;1473default:1474return true;1475}1476}147714781479bool1480GNEEdge::isAttributeComputed(SumoXMLAttr key) const {1481switch (key) {1482case SUMO_ATTR_LENGTH:1483return !myNBEdge->hasLoadedLength();1484case SUMO_ATTR_WIDTH:1485if (myNBEdge->hasLaneSpecificWidth()) {1486return false;1487} else {1488return (myNBEdge->getLaneWidth() == NBEdge::UNSPECIFIED_WIDTH);1489}1490default:1491return false;1492}1493}149414951496void1497GNEEdge::setResponsible(bool newVal) {1498myAmResponsible = newVal;1499}150015011502GNELane*1503GNEEdge::getLaneByAllowedVClass(const SUMOVehicleClass vClass) const {1504// iterate over all NBEdge lanes1505for (int i = 0; i < (int)myNBEdge->getLanes().size(); i++) {1506// if given VClass is in permissions, return lane1507if (myNBEdge->getLanes().at(i).permissions & vClass) {1508// return GNELane1509return getChildLanes().at(i);1510}1511}1512// return first lane1513return getChildLanes().front();1514}151515161517GNELane*1518GNEEdge::getLaneByDisallowedVClass(const SUMOVehicleClass vClass) const {1519// iterate over all NBEdge lanes1520for (int i = 0; i < (int)myNBEdge->getLanes().size(); i++) {1521// if given VClass isn't in permissions, return lane1522if (~(myNBEdge->getLanes().at(i).permissions) & vClass) {1523// return GNELane1524return getChildLanes().at(i);1525}1526}1527// return first lane1528return getChildLanes().front();1529}153015311532void1533GNEEdge::updateVehicleSpreadGeometries() {1534// get lane vehicles map1535const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneVehiclesMap = getVehiclesOverEdgeMap();1536// iterate over every lane1537for (const auto& laneVehicle : laneVehiclesMap) {1538// obtain total length1539double totalLength = 0;1540for (const auto& vehicle : laneVehicle.second) {1541totalLength += vehicle->getAttributeDouble(SUMO_ATTR_LENGTH) + VEHICLE_GAP;1542}1543// calculate multiplier for vehicle positions1544double multiplier = 1;1545const double laneShapeLength = laneVehicle.first->getLaneShape().length();1546if (laneShapeLength == 0) {1547multiplier = 0;1548} else if (totalLength > laneShapeLength) {1549multiplier = (laneShapeLength / totalLength);1550}1551// declare current length1552double length = 0;1553// iterate over vehicles to calculate position and rotations1554for (const auto& vehicle : laneVehicle.second) {1555vehicle->updateDemandElementSpreadGeometry(laneVehicle.first, length * multiplier);1556// update length1557length += vehicle->getAttributeDouble(SUMO_ATTR_LENGTH) + VEHICLE_GAP;1558}1559}1560}156115621563void1564GNEEdge::updateVehicleStackLabels() {1565// get lane vehicles map1566const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneVehiclesMap = getVehiclesOverEdgeMap();1567// iterate over laneVehiclesMap and obtain a vector with1568for (const auto& vehicleMap : laneVehiclesMap) {1569// declare map for sort vehicles using their departpos+length position (StackPosition)1570std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosVehicles;1571// declare vector of stack demand elements1572std::vector<GNEEdge::StackDemandElements> stackedVehicles;1573// iterate over vehicles1574for (const auto& vehicle : vehicleMap.second) {1575// get vehicle's depart pos and length1576const double departPos = vehicle->getAttributeDouble(SUMO_ATTR_DEPARTPOS);1577const double length = vehicle->getAttributeDouble(SUMO_ATTR_LENGTH);1578double posOverLane = vehicle->getAttributeDouble(SUMO_ATTR_DEPARTPOS);1579// check if we have to adapt posOverLane1580if (posOverLane < 0) {1581posOverLane += vehicleMap.first->getLaneShape().length();1582}1583// make a stack position using departPos and length1584departPosVehicles.push_back(std::make_pair(StackPosition(departPos, length), vehicle));1585// update depart element geometry1586vehicle->updateDemandElementGeometry(vehicleMap.first, posOverLane);1587// reset vehicle stack label1588vehicle->updateDemandElementStackLabel(0);1589}15901591// sort departPosVehicles1592std::sort(departPosVehicles.begin(), departPosVehicles.end());1593// iterate over departPosVehicles1594for (const auto& departPosVehicle : departPosVehicles) {1595// obtain stack position and vehicle1596const GNEEdge::StackPosition& vehicleStackPosition = departPosVehicle.first;1597GNEDemandElement* vehicle = departPosVehicle.second;1598// if stackedVehicles is empty, add a new StackDemandElements1599if (stackedVehicles.empty()) {1600stackedVehicles.push_back(GNEEdge::StackDemandElements(vehicleStackPosition, vehicle));1601} else if (areStackPositionOverlapped(vehicleStackPosition, stackedVehicles.back().getStackPosition())) {1602// add new vehicle to last inserted stackDemandElements1603stackedVehicles[stackedVehicles.size() - 1].addDemandElements(vehicle);1604} else {1605// No overlapping, then add a new StackDemandElements1606stackedVehicles.push_back(GNEEdge::StackDemandElements(vehicleStackPosition, vehicle));1607}1608}1609// iterate over stackedVehicles1610for (const auto& vehicle : stackedVehicles) {1611// only update vehicles with one or more stack1612if (vehicle.getDemandElements().size() > 1) {1613// set stack labels1614vehicle.getDemandElements().front()->updateDemandElementStackLabel((int)vehicle.getDemandElements().size());1615}1616}1617}1618}161916201621void1622GNEEdge::updatePersonStackLabels() {1623// get lane persons map1624const std::map<const GNELane*, std::vector<GNEDemandElement*> > lanePersonsMap = getPersonsOverEdgeMap();1625// iterate over lanePersonsMap and obtain a vector with1626for (const auto& personMap : lanePersonsMap) {1627// declare map for sort persons using their departpos+length position (StackPosition)1628std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosPersons;1629// declare vector of stack demand elements1630std::vector<GNEEdge::StackDemandElements> stackedPersons;1631// iterate over persons1632for (const auto& person : personMap.second) {1633// get person's depart pos and length1634const double departPos = person->getAttributeDouble(SUMO_ATTR_DEPARTPOS);1635// make a stack position using departPos and length1636departPosPersons.push_back(std::make_pair(StackPosition(departPos, 1.8), person));1637// update depart element geometry1638person->updateDemandElementGeometry(personMap.first, departPos);1639// reset person stack label1640person->updateDemandElementStackLabel(0);1641}16421643// sort departPosPersons1644std::sort(departPosPersons.begin(), departPosPersons.end());1645// iterate over departPosPersons1646for (const auto& departPosPerson : departPosPersons) {1647// obtain stack position and person1648const GNEEdge::StackPosition& personStackPosition = departPosPerson.first;1649GNEDemandElement* person = departPosPerson.second;1650// if stackedPersons is empty, add a new StackDemandElements1651if (stackedPersons.empty()) {1652stackedPersons.push_back(GNEEdge::StackDemandElements(personStackPosition, person));1653} else if (areStackPositionOverlapped(personStackPosition, stackedPersons.back().getStackPosition())) {1654// add new person to last inserted stackDemandElements1655stackedPersons[stackedPersons.size() - 1].addDemandElements(person);1656} else {1657// No overlapping, then add a new StackDemandElements1658stackedPersons.push_back(GNEEdge::StackDemandElements(personStackPosition, person));1659}1660}1661// iterate over stackedPersons1662for (const auto& person : stackedPersons) {1663// only update persons with one or more stack1664if (person.getDemandElements().size() > 1) {1665// set stack labels1666person.getDemandElements().front()->updateDemandElementStackLabel((int)person.getDemandElements().size());1667}1668}1669}1670}167116721673void1674GNEEdge::updateContainerStackLabels() {1675// get lane containers map1676const std::map<const GNELane*, std::vector<GNEDemandElement*> > laneContainersMap = getContainersOverEdgeMap();1677// iterate over laneContainersMap and obtain a vector with1678for (const auto& containerMap : laneContainersMap) {1679// declare map for sort containers using their departpos+length position (StackPosition)1680std::vector<std::pair<GNEEdge::StackPosition, GNEDemandElement*> > departPosContainers;1681// declare vector of stack demand elements1682std::vector<GNEEdge::StackDemandElements> stackedContainers;1683// iterate over containers1684for (const auto& container : containerMap.second) {1685// get container's depart pos and length1686const double departPos = container->getAttributeDouble(SUMO_ATTR_DEPARTPOS);1687// make a stack position using departPos and length1688departPosContainers.push_back(std::make_pair(StackPosition(departPos, 1.8), container));1689// update depart element geometry1690container->updateDemandElementGeometry(containerMap.first, departPos);1691// reset container stack label1692container->updateDemandElementStackLabel(0);1693}16941695// sort departPosContainers1696std::sort(departPosContainers.begin(), departPosContainers.end());1697// iterate over departPosContainers1698for (const auto& departPosContainer : departPosContainers) {1699// obtain stack position and container1700const GNEEdge::StackPosition& containerStackPosition = departPosContainer.first;1701GNEDemandElement* container = departPosContainer.second;1702// if stackedContainers is empty, add a new StackDemandElements1703if (stackedContainers.empty()) {1704stackedContainers.push_back(GNEEdge::StackDemandElements(containerStackPosition, container));1705} else if (areStackPositionOverlapped(containerStackPosition, stackedContainers.back().getStackPosition())) {1706// add new container to last inserted stackDemandElements1707stackedContainers[stackedContainers.size() - 1].addDemandElements(container);1708} else {1709// No overlapping, then add a new StackDemandElements1710stackedContainers.push_back(GNEEdge::StackDemandElements(containerStackPosition, container));1711}1712}1713// iterate over stackedContainers1714for (const auto& container : stackedContainers) {1715// only update containers with one or more stack1716if (container.getDemandElements().size() > 1) {1717// set stack labels1718container.getDemandElements().front()->updateDemandElementStackLabel((int)container.getDemandElements().size());1719}1720}1721}1722}172317241725bool1726GNEEdge::isConvexAngle() const {1727// calculate angle between both junction positions1728double edgeAngle = RAD2DEG(getFromJunction()->getPositionInView().angleTo2D(getToJunction()->getPositionInView()));1729// adjust to 360 degrees1730while (edgeAngle < 0) {1731edgeAngle += 360;1732}1733// fmod round towards zero which is not want we want for negative numbers1734edgeAngle = fmod(edgeAngle, 360);1735// check angle1736return edgeAngle >= 0 && edgeAngle < 180;1737}173817391740bool1741GNEEdge::hasPredecessors() const {1742// get incoming edges1743const auto incomingEdges = getFromJunction()->getGNEIncomingEdges();1744// iterate over connections1745for (const auto& incomingEdge : incomingEdges) {1746for (const auto& connection : incomingEdge->getGNEConnections()) {1747if (connection->getEdgeTo() == this) {1748return true;1749}1750}1751}1752return false;1753}175417551756bool1757GNEEdge::hasSuccessors() const {1758return (myGNEConnections.size() > 0);1759}176017611762GNEEdge*1763GNEEdge::getReverseEdge() const {1764for (const auto& outgoingEdge : getParentJunctions().back()->getGNEOutgoingEdges()) {1765if (outgoingEdge->getToJunction() == getFromJunction()) {1766return outgoingEdge;1767}1768}1769return nullptr;1770}17711772// ===========================================================================1773// private1774// ===========================================================================17751776GNEEdge::StackPosition::StackPosition(const double departPos, const double length) :1777pair(departPos, departPos + length) {1778}177917801781double1782GNEEdge::StackPosition::beginPosition() const {1783return first;1784}178517861787double1788GNEEdge::StackPosition::endPosition() const {1789return second;1790}179117921793GNEEdge::StackDemandElements::StackDemandElements(const StackPosition stackedPosition, GNEDemandElement* demandElement) :1794pair(stackedPosition, {1795demandElement1796}) {1797}179817991800void1801GNEEdge::StackDemandElements::addDemandElements(GNEDemandElement* demandElement) {1802second.push_back(demandElement);1803}180418051806const GNEEdge::StackPosition&1807GNEEdge::StackDemandElements::getStackPosition() const {1808return first;1809}181018111812const std::vector<GNEDemandElement*>&1813GNEEdge::StackDemandElements::getDemandElements() const {1814return second;1815}181618171818void1819GNEEdge::setAttribute(SumoXMLAttr key, const std::string& value) {1820switch (key) {1821case SUMO_ATTR_ID:1822myNet->getAttributeCarriers()->updateEdgeID(this, value);1823// enable save demand elements if there are stops1824for (const auto& stop : getChildDemandElements()) {1825if (stop->getTagProperty()->isVehicleStop()) {1826myNet->getSavingStatus()->requireSaveDemandElements();1827}1828}1829// also for lanes1830for (const auto& lane : getChildLanes()) {1831for (const auto& stop : lane->getChildDemandElements()) {1832if (stop->getTagProperty()->isVehicleStop()) {1833myNet->getSavingStatus()->requireSaveDemandElements();1834}1835}1836}1837break;1838case SUMO_ATTR_FROM:1839myNet->changeEdgeEndpoints(this, value, getToJunction()->getID());1840// update this edge of list of outgoings edges of the old first parent junction1841getFromJunction()->removeOutgoingGNEEdge(this);1842// update first parent junction1843updateFirstParentJunction(value);1844// update this edge of list of outgoings edges of the new first parent junction1845getFromJunction()->addOutgoingGNEEdge(this);1846// update centering boundary and grid1847updateCenteringBoundary(true);1848break;1849case SUMO_ATTR_TO:1850myNet->changeEdgeEndpoints(this, getFromJunction()->getID(), value);1851// update this edge of list of incomings edges of the old second parent junction1852getToJunction()->removeIncomingGNEEdge(this);1853// update second parent junction1854updateSecondParentJunction(value);1855// update this edge of list of incomings edges of the new second parent junction1856getToJunction()->addIncomingGNEEdge(this);1857// update centering boundary and grid1858updateCenteringBoundary(true);1859break;1860case SUMO_ATTR_NUMLANES:1861throw InvalidArgument("GNEEdge::setAttribute (private) called for attr SUMO_ATTR_NUMLANES. This should never happen");1862case SUMO_ATTR_PRIORITY:1863myNBEdge->myPriority = parse<int>(value);1864break;1865case SUMO_ATTR_LENGTH:1866if (value.empty()) {1867myNBEdge->setLoadedLength(NBEdge::UNSPECIFIED_LOADED_LENGTH);1868} else {1869myNBEdge->setLoadedLength(parse<double>(value));1870}1871break;1872case SUMO_ATTR_TYPE:1873myNBEdge->myType = value;1874break;1875case SUMO_ATTR_SHAPE:1876// set new geometry1877setGeometry(parse<PositionVector>(value), true);1878// update centering boundary and grid1879updateCenteringBoundary(true);1880break;1881case SUMO_ATTR_SPREADTYPE:1882myNBEdge->setLaneSpreadFunction(SUMOXMLDefinitions::LaneSpreadFunctions.get(value));1883break;1884case SUMO_ATTR_NAME:1885myNBEdge->setStreetName(value);1886break;1887case SUMO_ATTR_SPEED:1888myNBEdge->setSpeed(-1, parse<double>(value));1889break;1890case SUMO_ATTR_FRICTION:1891myNBEdge->setFriction(-1, parse<double>(value));1892break;1893case SUMO_ATTR_WIDTH:1894if (value.empty() || (value == "default")) {1895myNBEdge->setLaneWidth(-1, NBEdge::UNSPECIFIED_WIDTH);1896} else {1897myNBEdge->setLaneWidth(-1, parse<double>(value));1898}1899break;1900case SUMO_ATTR_ENDOFFSET:1901myNBEdge->setEndOffset(-1, parse<double>(value));1902break;1903case SUMO_ATTR_ALLOW:1904break; // no edge value1905case SUMO_ATTR_DISALLOW:1906break; // no edge value1907case SUMO_ATTR_DISTANCE:1908if (value.empty()) {1909myNBEdge->setDistance(0.0);1910} else {1911myNBEdge->setDistance(parse<double>(value));1912}1913break;1914case GNE_ATTR_MODIFICATION_STATUS:1915myConnectionStatus = value;1916if (value == FEATURE_GUESSED) {1917myNBEdge->invalidateConnections(true);1918clearGNEConnections();1919} else if (value != FEATURE_GUESSED) {1920myNBEdge->declareConnectionsAsLoaded();1921}1922break;1923case GNE_ATTR_SHAPE_START: {1924// get geometry of NBEdge, remove FIRST element with the new value (or with the Junction Source position) and set it back to edge1925Position newShapeStart;1926if (value == "") {1927newShapeStart = getFromJunction()->getNBNode()->getPosition();1928} else {1929newShapeStart = parse<Position>(value);1930}1931// set shape start position1932setShapeStartPos(newShapeStart);1933// update centering boundary and grid1934updateCenteringBoundary(true);1935break;1936}1937case GNE_ATTR_SHAPE_END: {1938// get geometry of NBEdge, remove LAST element with the new value (or with the Junction Destination position) and set it back to edge1939Position newShapeEnd;1940if (value == "") {1941newShapeEnd = getToJunction()->getNBNode()->getPosition();1942} else {1943newShapeEnd = parse<Position>(value);1944}1945// set shape end position1946setShapeEndPos(newShapeEnd);1947// update centering boundary and grid1948updateCenteringBoundary(true);1949break;1950}1951case GNE_ATTR_BIDIR:1952myNBEdge->setBidi(parse<bool>(value));1953break;1954case GNE_ATTR_STOPOFFSET:1955if (value.empty()) {1956myNBEdge->myEdgeStopOffset.setOffset(0);1957} else {1958myNBEdge->myEdgeStopOffset.setOffset(parse<double>(value));1959}1960break;1961case GNE_ATTR_STOPOEXCEPTION:1962myNBEdge->myEdgeStopOffset.setExceptions(value);1963break;1964default:1965setCommonAttribute(key, value);1966break;1967}1968// get template editor1969GNEInspectorFrame::TemplateEditor* templateEditor = myNet->getViewParent()->getInspectorFrame()->getTemplateEditor();1970// check if update template (except for modification status)1971if (templateEditor->getEdgeTemplate() && (templateEditor->getEdgeTemplate()->getID() == getID()) &&1972(key != GNE_ATTR_MODIFICATION_STATUS)) {1973myNet->getViewParent()->getInspectorFrame()->getTemplateEditor()->updateEdgeTemplate();1974}1975// invalidate demand path calculator1976myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();1977}197819791980void1981GNEEdge::setNumLanes(int numLanes, GNEUndoList* undoList) {1982// begin undo list1983undoList->begin(this, "change number of " + toString(SUMO_TAG_LANE) + "s");1984// invalidate logic of source/destination edges1985getFromJunction()->setLogicValid(false, undoList);1986getToJunction()->setLogicValid(false, undoList);1987// disable update geometry (see #6336)1988myUpdateGeometry = false;1989// remove edge from grid1990myNet->removeGLObjectFromGrid(this);1991// save old number of lanes1992const int oldNumLanes = (int)getChildLanes().size();1993// get opposite ID1994const auto oppositeID = getChildLanes().back()->getAttribute(GNE_ATTR_OPPOSITE);1995if (oppositeID != "") {1996// we'll have a different leftmost lane after adding/removing lanes1997GNEChange_Attribute::changeAttribute(getChildLanes().back(), GNE_ATTR_OPPOSITE, "", undoList);1998}1999for (int i = oldNumLanes; i < numLanes; i++) {2000// since the GNELane does not exist yet, it cannot have yet been referenced so we only pass a zero-pointer2001undoList->add(new GNEChange_Lane(this, myNBEdge->getLaneStruct(oldNumLanes - 1)), true);2002}2003for (int i = (oldNumLanes - 1); i > (numLanes - 1); i--) {2004myNet->deleteLane(getChildLanes().at(i), undoList, false);2005}2006if (oppositeID != "") {2007GNEChange_Attribute::changeAttribute(getChildLanes().back(), GNE_ATTR_OPPOSITE, oppositeID, undoList);2008}2009// enable updateGeometry again2010myUpdateGeometry = true;2011// update geometry of entire edge2012updateGeometry();2013// end undo list2014undoList->end();2015// update centering boundary (without updating RTREE)2016updateCenteringBoundary(false);2017// insert edge in grid again2018myNet->addGLObjectIntoGrid(this);2019}202020212022void2023GNEEdge::updateFirstParentJunction(const std::string& value) {2024auto newJunction = myNet->getAttributeCarriers()->retrieveJunction(value);2025GNEHierarchicalElement::updateParent(this, 0, newJunction);2026}202720282029void2030GNEEdge::updateSecondParentJunction(const std::string& value) {2031auto newJunction = myNet->getAttributeCarriers()->retrieveJunction(value);2032GNEHierarchicalElement::updateParent(this, 1, newJunction);2033}203420352036void2037GNEEdge::addLane(GNELane* lane, const NBEdge::Lane& laneAttrs, bool recomputeConnections) {2038const int index = lane ? lane->getIndex() : myNBEdge->getNumLanes();2039// the laneStruct must be created first to ensure we have some geometry2040// unless the connections are fully recomputed, existing indices must be shifted2041myNBEdge->addLane(index, true, recomputeConnections, !recomputeConnections);2042if (lane) {2043// restore a previously deleted lane2044GNEHierarchicalContainerChildren<GNELane*> newParentLanes = getChildLanes();2045newParentLanes.insert(newParentLanes.begin() + index, lane);2046while (getChildLanes().size() > 0) {2047removeChild(this, getChildLanes().front());2048}2049for (const auto newParentLane : newParentLanes) {2050insertChild(this, newParentLane);2051}2052} else {2053// create a new lane by copying leftmost lane2054lane = new GNELane(this, index);2055getHierarchicalElement()->addChildElement(lane);2056}2057lane->incRef("GNEEdge::addLane");2058// add in attributeCarriers2059myNet->getAttributeCarriers()->insertLane(lane);2060// check if lane is selected2061if (lane->isAttributeCarrierSelected()) {2062lane->selectAttributeCarrier();2063}2064// we copy all attributes except shape since this is recomputed from edge shape2065myNBEdge->setSpeed(lane->getIndex(), laneAttrs.speed);2066myNBEdge->setFriction(lane->getIndex(), laneAttrs.friction);2067myNBEdge->setPermissions(laneAttrs.permissions, lane->getIndex());2068myNBEdge->setPreferredVehicleClass(laneAttrs.preferred, lane->getIndex());2069myNBEdge->setEndOffset(lane->getIndex(), laneAttrs.endOffset);2070myNBEdge->setLaneWidth(lane->getIndex(), laneAttrs.width);2071// update indices2072for (int i = 0; i < (int)getChildLanes().size(); ++i) {2073getChildLanes()[i]->setIndex(i);2074}2075/* while technically correct, this looks ugly2076getFromJunction()->invalidateShape();2077getToJunction()->invalidateShape();2078*/2079// Remake connections for this edge and all edges that target this lane2080remakeGNEConnections();2081// remake connections of all edges of junction source and destination2082for (const auto& fromEdge : getFromJunction()->getChildEdges()) {2083fromEdge->remakeGNEConnections();2084}2085// remake connections of all edges of junction source and destination2086for (const auto& toEdge : getToJunction()->getChildEdges()) {2087toEdge->remakeGNEConnections();2088}2089// Update geometry with the new lane2090updateGeometry();2091// update boundary and grid2092updateCenteringBoundary(myUpdateGeometry);2093}209420952096void2097GNEEdge::removeLane(GNELane* lane, bool recomputeConnections) {2098if (getChildLanes().size() == 0) {2099throw ProcessError("Should not remove the last " + toString(SUMO_TAG_LANE) + " from an " + getTagStr());2100}2101if (lane == nullptr) {2102lane = getChildLanes().back();2103}2104// check if lane is selected2105if (lane->isAttributeCarrierSelected()) {2106lane->unselectAttributeCarrier();2107}2108// before removing, check that lane isn't being inspected2109myNet->getViewNet()->getInspectedElements().uninspectAC(lane);2110myNet->getViewParent()->getInspectorFrame()->getHierarchicalElementTree()->removeCurrentEditedAttributeCarrier(lane);2111// Delete lane of edge's container2112// unless the connections are fully recomputed, existing indices must be shifted2113myNBEdge->deleteLane(lane->getIndex(), recomputeConnections, !recomputeConnections);2114lane->decRef("GNEEdge::removeLane");2115// delete lane from edge2116GNEHierarchicalElement::removeChild(this, lane);2117// remove from attributeCarriers2118myNet->getAttributeCarriers()->deleteLane(lane);2119// Delete lane if is unreferenced2120if (lane->unreferenced()) {2121delete lane;2122}2123// update indices2124for (int i = 0; i < (int)getChildLanes().size(); ++i) {2125getChildLanes()[i]->setIndex(i);2126}2127/* while technically correct, this looks ugly2128getFromJunction()->invalidateShape();2129getToJunction()->invalidateShape();2130*/2131// Remake connections of this edge2132remakeGNEConnections();2133// remake connections of all edges of junction source and destination2134for (const auto& fromEdge : getFromJunction()->getChildEdges()) {2135fromEdge->remakeGNEConnections();2136}2137// remake connections of all edges of junction source and destination2138for (const auto& toEdge : getToJunction()->getChildEdges()) {2139toEdge->remakeGNEConnections();2140}2141// Update element2142updateGeometry();2143// update boundary and grid2144updateCenteringBoundary(myUpdateGeometry);2145}214621472148void2149GNEEdge::addConnection(NBEdge::Connection nbCon, bool selectAfterCreation) {2150// If a new connection was successfully created2151if (myNBEdge->setConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane, NBEdge::Lane2LaneInfoType::USER, true, nbCon.mayDefinitelyPass,2152nbCon.keepClear, nbCon.contPos, nbCon.visibility,2153nbCon.speed, nbCon.friction, nbCon.customLength, nbCon.customShape, nbCon.uncontrolled)) {2154// Create or retrieve existent GNEConnection2155GNEConnection* con = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane);2156// add it to GNEConnection container2157myGNEConnections.push_back(con);2158// Add reference2159myGNEConnections.back()->incRef("GNEEdge::addConnection");2160// select GNEConnection if needed2161if (selectAfterCreation) {2162con->selectAttributeCarrier();2163}2164// update geometry2165con->updateGeometry();2166}2167// actually we only do this to force a redraw2168updateGeometry();2169}217021712172void2173GNEEdge::removeConnection(NBEdge::Connection nbCon) {2174// check if is a explicit turnaround2175if (nbCon.toEdge == myNBEdge->getTurnDestination()) {2176myNet->removeExplicitTurnaround(getID());2177}2178// remove NBEdge::connection from NBEdge2179myNBEdge->removeFromConnections(nbCon);2180// remove their associated GNEConnection2181GNEConnection* connection = retrieveGNEConnection(nbCon.fromLane, nbCon.toEdge, nbCon.toLane, false);2182if (connection != nullptr) {2183// before removing, check that the connection isn't being inspected2184myNet->getViewNet()->getInspectedElements().uninspectAC(connection);2185myNet->getViewParent()->getInspectorFrame()->getHierarchicalElementTree()->removeCurrentEditedAttributeCarrier(connection);2186connection->decRef("GNEEdge::removeConnection");2187myGNEConnections.erase(std::find(myGNEConnections.begin(), myGNEConnections.end(), connection));2188// check if connection is selected2189if (connection->isAttributeCarrierSelected()) {2190connection->unselectAttributeCarrier();2191}2192// remove it from network2193myNet->removeGLObjectFromGrid(connection);2194// check if remove it from Attribute Carriers2195if (myNet->getAttributeCarriers()->getConnections().count(connection) > 0) {2196myNet->getAttributeCarriers()->deleteConnection(connection);2197}2198if (connection->unreferenced()) {2199// actually we only do this to force a redraw2200updateGeometry();2201}2202}2203}220422052206GNEConnection*2207GNEEdge::retrieveGNEConnection(int fromLane, NBEdge* to, int toLane, bool createIfNoExist) {2208for (const auto& connection : myGNEConnections) {2209if ((connection->getFromLaneIndex() == fromLane) && (connection->getEdgeTo()->getNBEdge() == to) && (connection->getToLaneIndex() == toLane)) {2210return connection;2211}2212}2213if (createIfNoExist) {2214// create new connection. Will be added to the rTree on first geometry computation2215GNEConnection* connection = new GNEConnection(getChildLanes()[fromLane], myNet->getAttributeCarriers()->retrieveEdge(to->getID())->getChildLanes()[toLane]);2216// add it into network2217myNet->addGLObjectIntoGrid(connection);2218// add it in attributeCarriers2219myNet->getAttributeCarriers()->insertConnection(connection);2220return connection;2221} else {2222return nullptr;2223}2224}222522262227void2228GNEEdge::setEdgeID(const std::string& newID) {2229setNetworkElementID(newID);2230for (const auto& lane : getChildLanes()) {2231lane->setNetworkElementID(getNBEdge()->getLaneID(lane->getIndex()));2232}2233}223422352236bool2237GNEEdge::hasRestrictedLane(SUMOVehicleClass vclass) const {2238for (const auto& lane : getChildLanes()) {2239if (lane->isRestricted(vclass)) {2240return true;2241}2242}2243return false;2244}224522462247void2248GNEEdge::removeEdgeFromCrossings(GNEJunction* junction, GNEUndoList* undoList) {2249// Remove all crossings that contain this edge in parameter "edges"2250for (const auto& crossing : junction->getGNECrossings()) {2251if (crossing->checkEdgeBelong(this)) {2252myNet->deleteCrossing(crossing, undoList);2253}2254}2255}225622572258void2259GNEEdge::straightenElevation(GNEUndoList* undoList) {2260PositionVector modifiedShape = myNBEdge->getGeometry().interpolateZ(2261myNBEdge->getFromNode()->getPosition().z(),2262myNBEdge->getToNode()->getPosition().z());2263PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);2264setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);2265}226622672268PositionVector2269GNEEdge::smoothShape(const PositionVector& old, bool forElevation) {2270const auto& neteditOptions = OptionsCont::getOptions();2271// distinguish 3 cases:2272// a) if the edge has exactly 3 or 4 points, use these as control points2273// b) if the edge has more than 4 points, use the first 2 and the last 2 as control points2274// c) if the edge is straight and both nodes are geometry-like nodes, use geometry of the continuation edges as control points2275PositionVector init;2276#ifdef DEBUG_SMOOTH_GEOM2277if (DEBUGCOND(this)) std::cout << getID()2278<< " forElevation=" << forElevation2279<< " fromGeometryLike=" << myNBEdge->getFromNode()->geometryLike()2280<< " toGeometryLike=" << myNBEdge->getToNode()->geometryLike()2281<< " smoothShape old=" << old << "\n";2282#endif2283if (old.size() == 3 || old.size() == 4) {2284init = old;2285} else if (old.size() > 4 && !forElevation) {2286// for elevation, the initial segments are not useful2287init.push_back(old[0]);2288init.push_back(old[1]);2289init.push_back(old[-2]);2290init.push_back(old[-1]);2291} else if (myNBEdge->getFromNode()->geometryLike() && myNBEdge->getToNode()->geometryLike()) {2292PositionVector begShape;2293PositionVector endShape;2294const EdgeVector& incoming = myNBEdge->getFromNode()->getIncomingEdges();2295const EdgeVector& outgoing = myNBEdge->getToNode()->getOutgoingEdges();2296if (incoming.size() == 1) {2297begShape = incoming[0]->getGeometry();2298} else {2299assert(incoming.size() == 2);2300begShape = myNBEdge->isTurningDirectionAt(incoming[0]) ? incoming[1]->getGeometry() : incoming[0]->getGeometry();2301}2302if (outgoing.size() == 1) {2303endShape = outgoing[0]->getGeometry();2304} else {2305assert(outgoing.size() == 2);2306endShape = myNBEdge->isTurningDirectionAt(outgoing[0]) ? outgoing[1]->getGeometry() : outgoing[0]->getGeometry();2307}2308const double dist = MIN2(old.length2D(), MAX2(old.length2D() / 8, fabs(old[0].z() - old[-1].z()) * OptionsCont::getOptions().getFloat("geometry.max-grade") / 3));2309if (forElevation) {2310// initialize control point elevation for smooth continuation2311init.push_back(old[0]);2312init.push_back(old.positionAtOffset2D(dist));2313init.push_back(old.positionAtOffset2D(old.length2D() - dist));2314init.push_back(old[-1]);2315double begZ = begShape.positionAtOffset2D(MAX2(0.0, begShape.length2D() - dist)).z();2316double endZ = endShape.positionAtOffset2D(MIN2(begShape.length2D(), dist)).z();2317// continue incline2318init[1].setz(2 * init[0].z() - begZ);2319init[2].setz(2 * init[-1].z() - endZ);2320} else {2321bool ok = true;2322const double straightThresh = DEG2RAD(neteditOptions.getFloat("opendrive-output.straight-threshold"));2323init = NBNode::bezierControlPoints(begShape, endShape, false, dist, dist, ok, nullptr, straightThresh);2324}2325#ifdef DEBUG_SMOOTH_GEOM2326if (DEBUGCOND(this)) {2327std::cout << " begShape=" << begShape << " endShape=" << endShape << " forElevation=" << forElevation << " dist=" << dist << " ok=" << ok << " init=" << init << "\n";2328}2329#endif2330}2331if (init.size() == 0) {2332return PositionVector::EMPTY;2333} else {2334const int numPoints = MAX2(neteditOptions.getInt("junctions.internal-link-detail"),2335int(old.length2D() / neteditOptions.getFloat("opendrive.curve-resolution")));2336return init.bezier(numPoints);2337}2338}233923402341void2342GNEEdge::smooth(GNEUndoList* undoList) {2343PositionVector modifiedShape = smoothShape(myNBEdge->getGeometry(), false);2344if (modifiedShape.size() < 2) {2345WRITE_WARNINGF(TL("Could not compute smooth shape for edge '%'"), getID());2346} else {2347PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);2348setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);2349}2350}235123522353void2354GNEEdge::smoothElevation(GNEUndoList* undoList) {2355PositionVector elevationBase;2356for (const Position& pos : myNBEdge->getGeometry()) {2357if (elevationBase.size() == 0 || elevationBase[-1].z() != pos.z()) {2358elevationBase.push_back(pos);2359}2360}2361PositionVector elevation = smoothShape(elevationBase, true);2362if (elevation.size() <= 2) {2363WRITE_WARNINGF(TL("Could not compute smooth elevation for edge '%'"), getID());2364} else {2365PositionVector modifiedShape = myNBEdge->getGeometry();2366if (modifiedShape.size() < 5) {2367modifiedShape = modifiedShape.resample(OptionsCont::getOptions().getFloat("opendrive.curve-resolution"), false);2368}2369const double scale = elevation.length2D() / modifiedShape.length2D();2370//std::cout << " elevation=" << elevation << "\n mod1=" << modifiedShape << " scale=" << scale << "\n";2371double seen = 0;2372for (int i = 1; i < (int)modifiedShape.size(); ++i) {2373seen += modifiedShape[i - 1].distanceTo2D(modifiedShape[i]);2374modifiedShape[i].setz(elevation.positionAtOffset2D(seen * scale).z());2375}2376//std::cout << " mod2=" << modifiedShape << "\n";2377PositionVector innerShape(modifiedShape.begin() + 1, modifiedShape.end() - 1);2378setAttribute(SUMO_ATTR_SHAPE, toString(innerShape), undoList);2379}2380}238123822383void2384GNEEdge::setShapeStartPos(const Position& pos) {2385// remove start position and add it the new position2386PositionVector geom = myNBEdge->getGeometry();2387geom.pop_front();2388geom.push_front(pos);2389// restore modified shape2390setGeometry(geom, false);2391}239223932394void2395GNEEdge::setShapeEndPos(const Position& pos) {2396// remove end position and add it the new position2397PositionVector geom = myNBEdge->getGeometry();2398geom.pop_back();2399geom.push_back(pos);2400// restore modified shape2401setGeometry(geom, false);2402}240324042405const std::map<const GNELane*, std::vector<GNEDemandElement*> >2406GNEEdge::getVehiclesOverEdgeMap() const {2407// declare vehicles over edge vector2408std::vector<GNEDemandElement*> vehiclesOverEdge;2409// declare solution map2410std::map<const GNELane*, std::vector<GNEDemandElement*> > vehiclesOverEdgeMap;2411// declare a set of vehicles (to avoid duplicates)2412std::set<std::pair<double, GNEDemandElement*> > vehicles;2413// first obtain all vehicles of this edge2414for (const auto& edgeChild : getChildDemandElements()) {2415if (((edgeChild->getTagProperty()->getTag() == SUMO_TAG_TRIP) || (edgeChild->getTagProperty()->getTag() == SUMO_TAG_FLOW)) &&2416(edgeChild->getParentEdges().front() == this)) {2417vehicles.insert(std::make_pair(edgeChild->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild));2418vehicles.insert(std::make_pair(edgeChild->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild));2419} else if ((edgeChild->getTagProperty()->getTag() == SUMO_TAG_ROUTE) && (edgeChild->getParentEdges().front() == this)) {2420for (const auto& routeChild : edgeChild->getChildDemandElements()) {2421if (routeChild->getTagProperty()->vehicleRoute()) {2422vehicles.insert(std::make_pair(routeChild->getAttributeDouble(SUMO_ATTR_DEPART), routeChild));2423}2424}2425} else if ((edgeChild->getTagProperty()->getTag() == GNE_TAG_ROUTE_EMBEDDED) && (edgeChild->getParentEdges().front() == this) && (edgeChild->getParentDemandElements().size() > 0)) {2426vehicles.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPART), edgeChild->getParentDemandElements().front()));2427}2428}2429// reserve2430vehiclesOverEdge.reserve(vehicles.size());2431// iterate over vehicles2432for (const auto& vehicle : vehicles) {2433// add it over vehiclesOverEdge;2434vehiclesOverEdge.push_back(vehicle.second);2435}2436// now split vehicles by lanes2437for (const auto& vehicle : vehiclesOverEdge) {2438const GNELane* vehicleLane = vehicle->getFirstPathLane();2439if (vehicleLane) {2440vehiclesOverEdgeMap[vehicleLane].push_back(vehicle);2441}2442}2443return vehiclesOverEdgeMap;2444}244524462447const std::map<const GNELane*, std::vector<GNEDemandElement*> >2448GNEEdge::getPersonsOverEdgeMap() const {2449// declare persons over edge vector2450std::vector<GNEDemandElement*> personsOverEdge;2451// declare solution map2452std::map<const GNELane*, std::vector<GNEDemandElement*> > personsOverEdgeMap;2453// declare a set of persons (to avoid duplicates)2454std::set<std::pair<double, GNEDemandElement*> > persons;2455// first obtain all persons of this edge2456for (const auto& edgeChild : getChildDemandElements()) {2457if (edgeChild->getTagProperty()->isPlanPerson() && (edgeChild->getParentDemandElements().size() > 0)) {2458persons.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPARTPOS), edgeChild->getParentDemandElements().front()));2459}2460}2461// reserve2462personsOverEdge.reserve(persons.size());2463// iterate over persons2464for (const auto& person : persons) {2465// add it over personsOverEdge;2466personsOverEdge.push_back(person.second);2467}2468// now split persons by lanes2469for (const auto& person : personsOverEdge) {2470const GNELane* personLane = person->getFirstPathLane();2471if (personLane) {2472personsOverEdgeMap[personLane].push_back(person);2473}2474}2475return personsOverEdgeMap;2476}2477247824792480const std::map<const GNELane*, std::vector<GNEDemandElement*> >2481GNEEdge::getContainersOverEdgeMap() const {2482// declare containers over edge vector2483std::vector<GNEDemandElement*> containersOverEdge;2484// declare solution map2485std::map<const GNELane*, std::vector<GNEDemandElement*> > containersOverEdgeMap;2486// declare a set of containers (to avoid duplicates)2487std::set<std::pair<double, GNEDemandElement*> > containers;2488// first obtain all containers of this edge2489for (const auto& edgeChild : getChildDemandElements()) {2490if (edgeChild->getTagProperty()->isPlanContainer() && (edgeChild->getParentDemandElements().size() > 0)) {2491containers.insert(std::make_pair(edgeChild->getParentDemandElements().front()->getAttributeDouble(SUMO_ATTR_DEPARTPOS), edgeChild->getParentDemandElements().front()));2492}2493}2494// reserve2495containersOverEdge.reserve(containers.size());2496// iterate over containers2497for (const auto& container : containers) {2498// add it over containersOverEdge;2499containersOverEdge.push_back(container.second);2500}2501// now split containers by lanes2502for (const auto& container : containersOverEdge) {2503const GNELane* containerLane = container->getFirstPathLane();2504if (containerLane) {2505containersOverEdgeMap[containerLane].push_back(container);2506}2507}2508return containersOverEdgeMap;2509}251025112512void2513GNEEdge::drawEdgeGeometryPoints(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double layer) const {2514// first check conditions2515if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() && (d <= GUIVisualizationSettings::Detail::GeometryPoint)) {2516// check if draw geometry points2517const bool bigGeometryPoints = drawBigGeometryPoints();2518// Obtain exaggeration of the draw2519const double exaggeration = getExaggeration(s);2520// get geometry point radius2521const double geometryPointRadius = getGeometryPointRadius();2522// obtain geometry point color2523RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);2524if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {2525// override with special colors (unless the color scheme is based on selection)2526geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);2527}2528// draw geometry points except initial and final2529for (int i = 1; i < (int)myNBEdge->getGeometry().size() - 1; i++) {2530// obtain geometry point2531const auto geometryPointPos = myNBEdge->getGeometry()[i];2532// push geometry point drawing matrix2533GLHelper::pushMatrix();2534// set color2535GLHelper::setColor(geometryPointColor);2536// move geometry point geometryPointPos2537glTranslated(geometryPointPos.x(), geometryPointPos.y(), bigGeometryPoints ? GLO_GEOMETRYPOINT : GLO_LANE + 1);2538// draw filled circle (resolution of drawn circle depending of the zoom, to improve smoothness)2539GLHelper::drawFilledCircleDetailled(d, geometryPointRadius);2540// draw elevation or special symbols (Start, End and Block)2541if ((d <= GUIVisualizationSettings::Detail::Text) && myNet->getViewNet()->getNetworkViewOptions().editingElevation()) {2542// Translate to top2543glTranslated(0, 0, 0.2);2544// draw Z value2545GLHelper::drawText(toString(geometryPointPos.z()), Position(), 0, s.edgeValue.scaledSize(s.scale) / 2, s.edgeValue.color);2546}2547// pop geometry point drawing matrix2548GLHelper::popMatrix();2549}2550// draw start and end points2551if (bigGeometryPoints) {2552drawStartGeometryPoint(s, d, geometryPointRadius, layer, exaggeration);2553drawEndGeometryPoint(s, d, geometryPointRadius, layer, exaggeration);2554}2555// draw dotted contour geometry points2556myNetworkElementContour.drawDottedContourGeometryPoints(s, d, this, myNBEdge->getGeometry(), geometryPointRadius,2557exaggeration, s.dottedContourSettings.segmentWidthSmall);2558}2559}256025612562void2563GNEEdge::drawStartGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,2564const double geometryPointRadius, const double layer, const double exaggeration) const {2565// check detail level2566if (d <= GUIVisualizationSettings::Detail::GeometryPoint) {2567// get first geometry point2568const auto& startGeometryPointPos = myNBEdge->getGeometry().front();2569// get flags2570const bool startPosEdited = (startGeometryPointPos != getParentJunctions().front()->getPositionInView());2571const bool forceDraw = myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getForceDrawGeometryPoints();2572// check drawing conditions2573if (startPosEdited || forceDraw) {2574// calculate angle betwen first and second geometry point2575const double angle = RAD2DEG(startGeometryPointPos.angleTo2D(myNBEdge->getGeometry()[1])) * -1;2576// get selected geometry points2577const auto selectedGeometryPoints = gViewObjectsHandler.getSelectedGeometryPoints(this);2578// override with special colors (unless the color scheme is based on selection)2579if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {2580GLHelper::setColor(s.colorSettings.selectedEdgeColor.changedBrightness(-20));2581} else {2582GLHelper::setColor(s.junctionColorer.getSchemes()[0].getColor(2));2583}2584// push drawing matrix2585GLHelper::pushMatrix();2586// move to point position2587glTranslated(startGeometryPointPos.x(), startGeometryPointPos.y(), GLO_GEOMETRYPOINT);2588// resolution of drawn circle depending of detail2589GLHelper::drawFilledCircleDetailled(d, geometryPointRadius, angle + 90, angle + 270);2590// pop drawing matrix2591GLHelper::popMatrix();2592// draw a "s" over last point depending of detail level2593if (d <= GUIVisualizationSettings::Detail::Text) {2594// push drawing matrix2595GLHelper::pushMatrix();2596// move top2597glTranslated(0, 0, GLO_GEOMETRYPOINT + 0.1);2598// draw S2599GLHelper::drawText("S", startGeometryPointPos, 0, geometryPointRadius, RGBColor(0, 50, 255));2600// pop drawing matrix2601GLHelper::popMatrix();2602// check if draw line between junctions2603if ((selectedGeometryPoints.size() > 0) && (selectedGeometryPoints.front() == 0)) {2604// set base color2605GLHelper::setColor(RGBColor::ORANGE);2606// push drawing matrix2607GLHelper::pushMatrix();2608// draw line between geometry point and from junction2609const PositionVector lineA = {startGeometryPointPos, getFromJunction()->getNBNode()->getPosition()};2610GLHelper::drawBoxLine(lineA[1], RAD2DEG(lineA[0].angleTo2D(lineA[1])) - 90, lineA[0].distanceTo2D(lineA[1]), .1);2611// draw line between begin point of last lane shape and the first edge shape point2612const PositionVector lineB = {startGeometryPointPos, myNBEdge->getLanes().back().shape.front()};2613GLHelper::drawBoxLine(lineB[1], RAD2DEG(lineB[0].angleTo2D(lineB[1])) - 90, lineB[0].distanceTo2D(lineB[1]), .1);2614// pop drawing matrix2615GLHelper::popMatrix();2616}2617}2618// draw dotted contour geometry points2619myNetworkElementContour.calculateContourFirstGeometryPoint(s, d, this, myNBEdge->getInnerGeometry(), layer,2620geometryPointRadius, exaggeration);2621}2622}2623}262426252626void2627GNEEdge::drawEndGeometryPoint(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,2628const double geometryPointRadius, const double layer, const double exaggeration) const {2629// check detail level2630if (d <= GUIVisualizationSettings::Detail::GeometryPoint) {2631// get first geometry point2632const auto& geometryPointPos = myNBEdge->getGeometry().back();2633// get flags2634const bool endPosEdited = (geometryPointPos != getParentJunctions().back()->getPositionInView());2635const bool forceDraw = myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getForceDrawGeometryPoints();2636// check drawing conditions2637if (endPosEdited || forceDraw) {2638// calculate angle last and previous geometry point2639const double angle = RAD2DEG(geometryPointPos.angleTo2D(myNBEdge->getGeometry()[-2])) * -1;2640// get selected geometry points2641const auto selectedGeometryPoints = gViewObjectsHandler.getSelectedGeometryPoints(this);2642// override with special colors (unless the color scheme is based on selection)2643if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {2644GLHelper::setColor(s.colorSettings.selectedEdgeColor.changedBrightness(-20));2645} else {2646GLHelper::setColor(s.junctionColorer.getSchemes()[0].getColor(2));2647}2648// push drawing matrix2649GLHelper::pushMatrix();2650// move to point position2651glTranslated(geometryPointPos.x(), geometryPointPos.y(), GLO_GEOMETRYPOINT);2652// resolution of drawn circle depending of detail2653GLHelper::drawFilledCircleDetailled(d, geometryPointRadius, angle + 90, angle + 270);2654// pop drawing matrix2655GLHelper::popMatrix();2656// draw a "s" over last point depending of detail level2657if (d <= GUIVisualizationSettings::Detail::Text) {2658// push drawing matrix2659GLHelper::pushMatrix();2660// move top2661glTranslated(0, 0, GLO_GEOMETRYPOINT + 0.1);2662// draw S2663GLHelper::drawText("E", geometryPointPos, 0, geometryPointRadius, RGBColor(0, 50, 255));2664// pop drawing matrix2665GLHelper::popMatrix();2666// check if draw line between junctions2667if ((selectedGeometryPoints.size() > 0) && (selectedGeometryPoints.back() == ((int)myNBEdge->getGeometry().size() - 1))) {2668// set base color2669GLHelper::setColor(RGBColor::ORANGE);2670// push drawing matrix2671GLHelper::pushMatrix();2672// draw line between geometry point and from junction2673const PositionVector lineA = {geometryPointPos, getToJunction()->getNBNode()->getPosition()};2674GLHelper::drawBoxLine(lineA[1], RAD2DEG(lineA[0].angleTo2D(lineA[1])) - 90, lineA[0].distanceTo2D(lineA[1]), .1);2675// draw line between begin point of last lane shape and the first edge shape point2676const PositionVector lineB = {geometryPointPos, myNBEdge->getLanes().back().shape.back()};2677GLHelper::drawBoxLine(lineB[1], RAD2DEG(lineB[0].angleTo2D(lineB[1])) - 90, lineB[0].distanceTo2D(lineB[1]), .1);2678// pop drawing matrix2679GLHelper::popMatrix();2680}2681}2682// draw dotted contour geometry points2683myNetworkElementContour.calculateContourFirstGeometryPoint(s, d, this, myNBEdge->getInnerGeometry(), layer,2684geometryPointRadius, exaggeration);2685}2686}2687}268826892690void2691GNEEdge::drawEdgeName(const GUIVisualizationSettings& s) const {2692// draw the name and/or the street name2693const bool drawStreetName = s.streetName.show(this) && (myNBEdge->getStreetName() != "");2694const bool spreadSuperposed = s.spreadSuperposed && myNBEdge->getBidiEdge() != nullptr;2695// check conditions2696if (s.edgeName.show(this) || drawStreetName || s.edgeValue.show(this)) {2697// get first and last lanes2698const GNELane* firstLane = getChildLanes()[0];2699const GNELane* lastLane = getChildLanes()[getChildLanes().size() - 1];2700// calculate draw position2701Position drawPosition = firstLane->getLaneShape().positionAtOffset(firstLane->getLaneShape().length() / (double) 2.);2702drawPosition.add(lastLane->getLaneShape().positionAtOffset(lastLane->getLaneShape().length() / (double) 2.));2703drawPosition.mul(.5);2704if (spreadSuperposed) {2705// move name to the right of the edge and towards its beginning2706const double dist = 0.6 * s.edgeName.scaledSize(s.scale);2707const double shiftA = firstLane->getLaneShape().rotationAtOffset(firstLane->getLaneShape().length() / (double) 2.) - DEG2RAD(135);2708const Position shift(dist * cos(shiftA), dist * sin(shiftA));2709drawPosition.add(shift);2710}2711// calculate drawing angle2712double drawAngle = firstLane->getLaneShape().rotationDegreeAtOffset(firstLane->getLaneShape().length() / (double) 2.);2713drawAngle += 90;2714// avoid draw inverted text2715if (drawAngle > 90 && drawAngle < 270) {2716drawAngle -= 180;2717}2718// draw edge name2719if (s.edgeName.show(this)) {2720drawName(drawPosition, s.scale, s.edgeName, drawAngle);2721}2722// draw street name2723if (drawStreetName) {2724GLHelper::drawTextSettings(s.streetName, myNBEdge->getStreetName(), drawPosition, s.scale, drawAngle);2725}2726// draw edge values2727if (s.edgeValue.show(this)) {2728// get current scheme2729const int activeScheme = s.laneColorer.getActive();2730// calculate value depending of active scheme2731std::string value;2732if (activeScheme == 12) {2733// edge param, could be non-numerical2734value = getNBEdge()->getParameter(s.edgeParam, "");2735} else if (activeScheme == 13) {2736// lane param, could be non-numerical2737value = getNBEdge()->getLaneStruct(lastLane->getIndex()).getParameter(s.laneParam, "");2738} else {2739// use numerical value value of leftmost lane to hopefully avoid sidewalks, bikelanes etc2740const double doubleValue = lastLane->getColorValue(s, activeScheme);2741const RGBColor color = s.laneColorer.getScheme().getColor(doubleValue);2742value = color.alpha() == 0 ? "" : toString(doubleValue);2743}2744// check if value is empty2745if (value != "") {2746GLHelper::drawTextSettings(s.edgeValue, value, drawPosition, s.scale, drawAngle);2747}2748}2749}2750}275127522753void2754GNEEdge::drawLaneStopOffset(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double layer) const {2755// draw geometry only if we'rent in drawForObjectUnderCursor mode2756if (d <= GUIVisualizationSettings::Detail::LaneDetails) {2757// Push stopOffset matrix2758GLHelper::pushMatrix();2759// translate to front (note: Special case)2760drawInLayer(layer + 1);2761if (myNBEdge->myEdgeStopOffset.isDefined() && (myNBEdge->myEdgeStopOffset.getPermissions() & SVC_PASSENGER) != 0) {2762for (const auto& lane : getChildLanes()) {2763lane->drawLaneStopOffset(s);2764}2765}2766// Push stopOffset matrix2767GLHelper::popMatrix();2768}2769}277027712772void2773GNEEdge::drawChildrens(const GUIVisualizationSettings& s) const {2774// draw child additional2775for (const auto& additional : getChildAdditionals()) {2776if (!additional->getTagProperty()->isListedElement()) {2777additional->drawGL(s);2778}2779}2780// draw person stops2781if (myNet->getViewNet()->getNetworkViewOptions().showDemandElements() && myNet->getViewNet()->getDataViewOptions().showDemandElements()) {2782for (const auto& stopEdge : getChildDemandElements()) {2783if ((stopEdge->getTagProperty()->getTag() == GNE_TAG_STOPPERSON_EDGE) || (stopEdge->getTagProperty()->getTag() == GNE_TAG_STOPCONTAINER_EDGE)) {2784stopEdge->drawGL(s);2785}2786}2787}2788// draw vehicles2789const std::map<const GNELane*, std::vector<GNEDemandElement*> > vehiclesMap = getVehiclesOverEdgeMap();2790for (const auto& vehicleMap : vehiclesMap) {2791for (const auto& vehicle : vehicleMap.second) {2792vehicle->drawGL(s);2793}2794}2795// draw TAZ elements2796drawTAZElements(s);2797}279827992800void2801GNEEdge::calculateEdgeContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double layer) const {2802// if we're selecting using a boundary, first don't calculate contour bt check if edge boundary is within selection boundary2803if (gViewObjectsHandler.selectingUsingRectangle() && gViewObjectsHandler.getSelectionTriangle().isBoundaryFullWithin(myEdgeBoundary)) {2804// simply add object in ViewObjectsHandler with full boundary2805gViewObjectsHandler.selectObject(this, layer, false, nullptr);2806} else {2807// get geometry point radius2808const auto geometryPointRadius = getGeometryPointRadius();2809// check if edit extrems2810const bool forceDrawExtrems = myNet->getViewParent()->getMoveFrame()->getNetworkMoveOptions()->getForceDrawGeometryPoints();2811const bool firstExtrem = forceDrawExtrems || (myNBEdge->getGeometry().front() != getParentJunctions().front()->getPositionInView());2812const bool lastExtrem = forceDrawExtrems || (myNBEdge->getGeometry().back() != getParentJunctions().back()->getPositionInView());2813// check if we're in move mode2814const bool moveMode = (myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_MOVE);2815// calculate contour2816myNetworkElementContour.calculateContourEdge(s, d, this, this, layer, true, true);2817// calculate edge geometry points2818myNetworkElementContour.calculateContourEdgeGeometryPoints(s, d, this, geometryPointRadius, moveMode, firstExtrem, lastExtrem);2819}2820}282128222823void2824GNEEdge::drawTAZElements(const GUIVisualizationSettings& s) const {2825// first check if draw TAZ Elements is enabled2826if (myNet->getViewNet()->getNetworkViewOptions().showTAZElements()) {2827if (getChildTAZSourceSinks().size() > 0) {2828// check if TAZ Source/sink is selected2829bool selected = false;2830for (const auto& TAZSourceSink : getChildTAZSourceSinks()) {2831if (TAZSourceSink->isAttributeCarrierSelected()) {2832selected = true;2833}2834}2835// iterate over lanes2836for (const auto& lane : getChildLanes()) {2837// Push layer matrix2838GLHelper::pushMatrix();2839// translate to front (note: Special case)2840if (myDrawInFront) {2841glTranslated(0, 0, GLO_FRONTELEMENT);2842} else if (lane->getLaneShape().length2D() <= (s.neteditSizeSettings.junctionBubbleRadius * 2)) {2843drawInLayer(GLO_JUNCTION + 0.5);2844} else {2845drawInLayer(GLO_LANE);2846}2847// move to front2848glTranslated(0, 0, 0.1);2849// set color2850if (selected) {2851GLHelper::setColor(RGBColor::BLUE);2852} else {2853GLHelper::setColor(RGBColor::CYAN);2854}2855// draw as box lines2856GUIGeometry::drawGeometry(GUIVisualizationSettings::Detail::Level0, lane->getLaneGeometry(),2857lane->getDrawingConstants()->getDrawingWidth());2858// Pop layer matrix2859GLHelper::popMatrix();2860}2861/*2862// check if curently we're inspecting a TAZ Source/Sink2863for (const auto& TAZSourceSink : TAZSourceSinks) {2864if (myNet->getViewNet()->isAttributeCarrierInspected(TAZSourceSink)) {2865drawDottedContourEdge(s, GUIDottedGeometry::DottedContourType::INSPECT, this, true, true);2866} else if (TAZSourceSink == markAC) {2867drawDottedContourEdge(s, GUIDottedGeometry::DottedContourType::FRONT, this, true, true);2868}2869}2870*/2871}2872}2873}287428752876void2877GNEEdge::drawEdgeShape(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d, const double layer) const {2878// avoid draw for railways2879if ((d <= GUIVisualizationSettings::Detail::LaneDetails) && !getChildLanes().front()->getDrawingConstants()->drawAsRailway() &&2880(gViewObjectsHandler.markedFirstGeometryPoint == this)) {2881// push draw matrix2882GLHelper::pushMatrix();2883// translate to front depending of big points2884if (drawBigGeometryPoints()) {2885glTranslated(0, 0, GLO_GEOMETRYPOINT - 1);2886} else {2887glTranslated(0, 0, layer);2888}2889// obtain color2890RGBColor geometryPointColor = s.junctionColorer.getSchemes()[0].getColor(2);2891if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {2892// override with special colors (unless the color scheme is based on selection)2893geometryPointColor = s.colorSettings.selectedEdgeColor.changedBrightness(-20);2894}2895// set color2896GLHelper::setColor(geometryPointColor);2897// iterate over NBEdge geometry2898for (int i = 1; i < (int)myNBEdge->getGeometry().size(); i++) {2899// calculate line between previous and current geometry point2900PositionVector line = {myNBEdge->getGeometry()[i - 1], myNBEdge->getGeometry()[i]};2901line.move2side(0.2);2902// draw box line2903GLHelper::drawBoxLine(line[1], RAD2DEG(line[0].angleTo2D(line[1])) - 90, line[0].distanceTo2D(line[1]), .1);2904}2905// pop draw matrix2906GLHelper::popMatrix();2907}2908}290929102911bool2912GNEEdge::drawBigGeometryPoints() const {2913// get edit modes2914const auto& editModes = myNet->getViewNet()->getEditModes();2915// continue depending of conditions2916if (!editModes.isCurrentSupermodeNetwork()) {2917return false;2918} else if (editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) {2919return true;2920} else if ((editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE) &&2921(myNet->getViewParent()->getDeleteFrame()->getDeleteOptions()->deleteOnlyGeometryPoints())) {2922return true;2923} else {2924return false;2925}2926}292729282929bool2930GNEEdge::areStackPositionOverlapped(const GNEEdge::StackPosition& vehicleA, const GNEEdge::StackPosition& vehicleB) const {2931if ((vehicleA.beginPosition() == vehicleB.beginPosition()) && (vehicleA.endPosition() == vehicleB.endPosition())) {2932return true;2933} else if ((vehicleA.beginPosition() < vehicleB.beginPosition()) && (vehicleA.endPosition() > vehicleB.endPosition())) {2934return true;2935} else if ((vehicleA.beginPosition() < vehicleB.beginPosition()) && (vehicleA.endPosition() > vehicleB.beginPosition())) {2936return true;2937} else if ((vehicleA.beginPosition() < vehicleB.endPosition()) && (vehicleA.endPosition() > vehicleB.endPosition())) {2938return true;2939} else {2940return false;2941}2942}294329442945double2946GNEEdge::getGeometryPointRadius() const {2947return drawBigGeometryPoints() ? SNAP_RADIUS * MIN2(1.0, myNet->getViewNet()->getVisualisationSettings().laneWidthExaggeration) : 0.5;2948}29492950/****************************************************************************/295129522953