Path: blob/main/src/netedit/elements/network/GNELane.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 GNELane.cpp14/// @author Jakob Erdmann15/// @author Mirko Barthauer16/// @date Feb 201117///18// A class for visualizing Lane geometry (adapted from GNELaneWrapper)19/****************************************************************************/2021#include <netbuild/NBEdgeCont.h>22#include <netedit/changes/GNEChange_Attribute.h>23#include <netedit/elements/moving/GNEMoveElementLane.h>24#include <netedit/frames/common/GNEDeleteFrame.h>25#include <netedit/frames/common/GNEInspectorFrame.h>26#include <netedit/frames/demand/GNERouteFrame.h>27#include <netedit/frames/GNEPathCreator.h>28#include <netedit/frames/GNEPlanCreator.h>29#include <netedit/frames/GNEViewObjectSelector.h>30#include <netedit/frames/network/GNEAdditionalFrame.h>31#include <netedit/frames/network/GNETLSEditorFrame.h>32#include <netedit/GNENet.h>33#include <netedit/GNEViewParent.h>34#include <utils/gui/div/GLHelper.h>35#include <utils/gui/div/GUIDesigns.h>36#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>37#include <utils/gui/images/GUITextureSubSys.h>38#include <utils/gui/images/VClassIcons.h>39#include <utils/gui/windows/GUIAppEnum.h>4041#include "GNELane.h"42#include "GNEInternalLane.h"43#include "GNEConnection.h"44#include "GNEEdgeTemplate.h"4546// ===========================================================================47// FOX callback mapping48// ===========================================================================4950// Object implementation51FXIMPLEMENT(GNELane, FXDelegator, 0, 0)5253// ===========================================================================54// method definitions55// ===========================================================================5657// ---------------------------------------------------------------------------58// GNELane::LaneDrawingConstants - methods59// ---------------------------------------------------------------------------6061GNELane::DrawingConstants::DrawingConstants(const GNELane* lane) :62myLane(lane) {63}646566void67GNELane::DrawingConstants::update(const GUIVisualizationSettings& s) {68// get NBEdge69const auto& NBEdge = myLane->getParentEdge()->getNBEdge();70// get lane struct71const auto& laneStruct = myLane->getParentEdges().front()->getNBEdge()->getLaneStruct(myLane->getIndex());72// get selection scale73const double selectionScale = myLane->isAttributeCarrierSelected() || myLane->getParentEdges().front()->isAttributeCarrierSelected() ? s.selectorFrameScale : 1;74// get lane width75const double laneWidth = (laneStruct.width == -1 ? SUMO_const_laneWidth : laneStruct.width);76// calculate exaggeration77myExaggeration = selectionScale * s.laneWidthExaggeration;78// get detail level79myDetail = s.getDetailLevel(myExaggeration);80// check if draw lane as railway81myDrawAsRailway = isRailway(laneStruct.permissions) && ((laneStruct.permissions & SVC_BUS) == 0) && s.showRails;82// adjust rest of parameters depending if draw as railway83if (myDrawAsRailway) {84// draw as railway: assume standard gauge of 1435mm when lane width is not set85myDrawingWidth = (laneWidth == SUMO_const_laneWidth ? 1.4350 : laneWidth) * myExaggeration;86// calculate internal drawing width87myInternalDrawingWidth = myDrawingWidth * 0.8;88} else {89// calculate exaggerated drawing width90myDrawingWidth = laneWidth * myExaggeration * 0.5;91// calculate internal drawing width92myInternalDrawingWidth = myDrawingWidth - (2 * SUMO_const_laneMarkWidth);93}94// check if draw superposed95myDrawSuperposed = (s.spreadSuperposed && (NBEdge->isBidiRail() || NBEdge->isBidiEdge()));96// adjust parameters depending of superposing97if (myDrawSuperposed) {98// apply offset99myOffset = myDrawingWidth * 0.5;100// reduce width101myDrawingWidth *= 0.4;102myInternalDrawingWidth *= 0.4;103} else {104// restore offset105myOffset = 0;106}107}108109110double111GNELane::DrawingConstants::getExaggeration() const {112return myExaggeration;113}114115116double117GNELane::DrawingConstants::getDrawingWidth() const {118return myDrawingWidth;119}120121122double123GNELane::DrawingConstants::getInternalDrawingWidth() const {124return myInternalDrawingWidth;125}126127128double129GNELane::DrawingConstants::getOffset() const {130return myOffset;131}132133134GUIVisualizationSettings::Detail135GNELane::DrawingConstants::getDetail() const {136return myDetail;137}138139140bool141GNELane::DrawingConstants::drawAsRailway() const {142return myDrawAsRailway;143}144145146bool147GNELane::DrawingConstants::drawSuperposed() const {148return myDrawSuperposed;149}150151152// ---------------------------------------------------------------------------153// GNELane - methods154// ---------------------------------------------------------------------------155#ifdef _MSC_VER156#pragma warning(push)157#pragma warning(disable: 4355) // mask warning about "this" in initializers158#endif159GNELane::GNELane(GNEEdge* edge, const int index) :160GNENetworkElement(edge->getNet(), edge->getNBEdge()->getLaneID(index), SUMO_TAG_LANE),161myMoveElementLane(new GNEMoveElementLane(this)),162myIndex(index),163myDrawingConstants(new DrawingConstants(this)),164mySpecialColor(nullptr),165mySpecialColorValue(-1),166myLane2laneConnections(this) {167// set parents168setParent<GNEEdge*>(edge);169// update centering boundary without updating grid170updateCenteringBoundary(false);171}172173174GNELane::GNELane() :175GNENetworkElement(nullptr, "dummyConstructorGNELane", SUMO_TAG_LANE),176myMoveElementLane(new GNEMoveElementLane(this)),177myIndex(-1),178myDrawingConstants(nullptr),179mySpecialColor(nullptr),180mySpecialColorValue(-1),181myLane2laneConnections(this) {182}183#ifdef _MSC_VER184#pragma warning(pop)185#endif186187GNELane::~GNELane() {188if (myDrawingConstants) {189delete myDrawingConstants;190}191}192193194GNEMoveElement*195GNELane::getMoveElement() const {196return myMoveElementLane;197}198199200Parameterised*201GNELane::getParameters() {202return &(getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex));203}204205206const Parameterised*207GNELane::getParameters() const {208return &(getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex));209}210211212GNEEdge*213GNELane::getParentEdge() const {214return getParentEdges().front();215}216217218bool219GNELane::allowPedestrians() const {220return (getParentEdges().front()->getNBEdge()->getPermissions(myIndex) & SVC_PEDESTRIAN) > 0;221}222223224const GUIGeometry&225GNELane::getLaneGeometry() const {226return myLaneGeometry;227}228229230const PositionVector&231GNELane::getLaneShape() const {232if (getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape.size() > 0) {233return getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape;234} else {235return getParentEdges().front()->getNBEdge()->getLaneShape(myIndex);236}237}238239240const std::vector<double>&241GNELane::getShapeRotations() const {242return myLaneGeometry.getShapeRotations();243}244245246const std::vector<double>&247GNELane::getShapeLengths() const {248return myLaneGeometry.getShapeLengths();249}250251252const GNELane::DrawingConstants*253GNELane::getDrawingConstants() const {254return myDrawingConstants;255}256257258void259GNELane::updateGeometry() {260// Clear texture containers261myLaneRestrictedTexturePositions.clear();262myLaneRestrictedTextureRotations.clear();263// get lane shape and extend if is too short264auto laneShape = getLaneShape();265if (laneShape.length2D() < 1) {266laneShape.extrapolate2D(1 - laneShape.length2D());267}268// Obtain lane shape of NBEdge269myLaneGeometry.updateGeometry(laneShape);270// update connections271myLane2laneConnections.updateLane2laneConnection();272// update additionals children associated with this lane273for (const auto& additional : getParentAdditionals()) {274additional->updateGeometry();275}276// update additionals parents associated with this lane277for (const auto& additional : getChildAdditionals()) {278additional->updateGeometry();279}280// update partial demand elements parents associated with this lane281for (const auto& demandElement : getParentDemandElements()) {282demandElement->updateGeometry();283}284// update partial demand elements children associated with this lane285for (const auto& demandElement : getChildDemandElements()) {286demandElement->updateGeometry();287}288// Update geometry of parent generic datas that have this edge as parent289for (const auto& additionalParent : getParentGenericDatas()) {290additionalParent->updateGeometry();291}292// Update geometry of additionals generic datas vinculated to this edge293for (const auto& childAdditionals : getChildGenericDatas()) {294childAdditionals->updateGeometry();295}296// compute geometry of path elements elements vinculated with this lane (depending of showDemandElements)297if (myNet->getViewNet() && myNet->getViewNet()->getNetworkViewOptions().showDemandElements()) {298for (const auto& childAdditional : getChildAdditionals()) {299childAdditional->computePathElement();300}301for (const auto& childDemandElement : getChildDemandElements()) {302childDemandElement->computePathElement();303}304for (const auto& childGenericData : getChildGenericDatas()) {305childGenericData->computePathElement();306}307}308// in Move mode, connections aren't updated309if (myNet->getViewNet() && myNet->getViewNet()->getEditModes().networkEditMode != NetworkEditMode::NETWORK_MOVE) {310// Update incoming connections of this lane311const auto incomingConnections = getGNEIncomingConnections();312for (const auto& connection : incomingConnections) {313connection->updateGeometry();314}315// Update outgoings connections of this lane316const auto outGoingConnections = getGNEOutcomingConnections();317for (const auto& connection : outGoingConnections) {318connection->updateGeometry();319}320}321// if lane has enought length for show textures of restricted lanes322if ((getLaneShapeLength() > 4)) {323// if lane is restricted324if (isRestricted(SVC_PEDESTRIAN) || isRestricted(SVC_BICYCLE) || isRestricted(SVC_BUS)) {325// get values for position and rotation of icons326for (int i = 2; i < getLaneShapeLength() - 1; i += 15) {327myLaneRestrictedTexturePositions.push_back(myLaneGeometry.getShape().positionAtOffset(i));328myLaneRestrictedTextureRotations.push_back(myLaneGeometry.getShape().rotationDegreeAtOffset(i));329}330}331}332}333334335Position336GNELane::getPositionInView() const {337return getLaneShape().positionAtOffset2D(getLaneShape().length2D() * 0.5);338}339340341bool342GNELane::checkDrawFromContour() const {343const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();344// check if we're inspecting a connection345if (inspectedElements.isInspectingSingleElement() && (inspectedElements.getFirstAC()->getTagProperty()->getTag() == SUMO_TAG_CONNECTION) &&346inspectedElements.getFirstAC()->getAttribute(GNE_ATTR_FROM_LANEID) == getID()) {347return true;348} else {349return false;350}351}352353354bool355GNELane::checkDrawToContour() const {356const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();357// check if we're inspecting a connection358if (inspectedElements.isInspectingSingleElement() && (inspectedElements.getFirstAC()->getTagProperty()->getTag() == SUMO_TAG_CONNECTION) &&359inspectedElements.getFirstAC()->getAttribute(GNE_ATTR_TO_LANEID) == getID()) {360return true;361} else {362return false;363}364}365366367bool368GNELane::checkDrawRelatedContour() const {369// check opened popup370if (myNet->getViewNet()->getPopup()) {371return myNet->getViewNet()->getPopup()->getGLObject() == this;372}373return false;374}375376377bool378GNELane::checkDrawOverContour() const {379// get modes and viewParent (for code legibility)380const auto& modes = myNet->getViewNet()->getEditModes();381const auto& viewParent = myNet->getViewParent();382// check if we're selecting edges in additional mode383if (modes.isCurrentSupermodeNetwork() && (modes.networkEditMode == NetworkEditMode::NETWORK_ADDITIONAL)) {384if (viewParent->getAdditionalFrame()->getViewObjetsSelector()->isNetworkElementSelected(this)) {385return true;386} else if (viewParent->getAdditionalFrame()->getViewObjetsSelector()->getTag() == myTagProperty->getTag()) {387return myNet->getViewNet()->getViewObjectsSelector().getLaneFront() == this;388} else {389return false;390}391} else {392return false;393}394}395396397bool398GNELane::checkDrawDeleteContour() const {399// first check if we're selecting edges or lanes400if (myNet->getViewNet()->checkSelectEdges()) {401return false;402} else {403// get edit modes404const auto& editModes = myNet->getViewNet()->getEditModes();405// check if we're in delete mode406if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_DELETE)) {407return myNet->getViewNet()->checkOverLockedElement(this, mySelected);408} else {409return false;410}411}412}413414415bool416GNELane::checkDrawDeleteContourSmall() const {417return false;418}419420421bool422GNELane::checkDrawSelectContour() const {423// first check if we're selecting edges or lanes424if (myNet->getViewNet()->checkSelectEdges()) {425return false;426} else {427// get edit modes428const auto& editModes = myNet->getViewNet()->getEditModes();429// check if we're in select mode430if (editModes.isCurrentSupermodeNetwork() && (editModes.networkEditMode == NetworkEditMode::NETWORK_SELECT)) {431return myNet->getViewNet()->checkOverLockedElement(this, mySelected);432} else {433return false;434}435}436}437438439bool440GNELane::checkDrawMoveContour() const {441// check if we're editing this network element442const GNENetworkElement* editedNetworkElement = myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement();443if (editedNetworkElement) {444return editedNetworkElement == this;445} else {446return false;447}448}449450451void452GNELane::drawGL(const GUIVisualizationSettings& s) const {453// update lane drawing constan454myDrawingConstants->update(s);455// calculate layer456double layer = GLO_LANE;457if (getParentEdges().front()->isMarkedForDrawingFront()) {458layer = GLO_FRONTELEMENT;459} else if (myLaneGeometry.getShape().length2D() <= (s.neteditSizeSettings.junctionBubbleRadius * 2)) {460layer = GLO_JUNCTION + 2;461}462// check drawing conditions463if (!s.drawForViewObjectsHandler) {464// draw lane465drawLane(s, layer);466// draw lock icon467GNEViewNetHelper::LockIcon::drawLockIcon(myDrawingConstants->getDetail(), this, getType(), getPositionInView(), 1);468// draw dotted contour469myNetworkElementContour.drawDottedContours(s, myDrawingConstants->getDetail(), this, s.dottedContourSettings.segmentWidth, true);470}471// calculate contour (always before children)472calculateLaneContour(s, layer);473// draw children474drawChildren(s);475}476477478void479GNELane::deleteGLObject() {480// Check if edge can be deleted481if (GNEDeleteFrame::SubordinatedElements(this).checkElements(myNet->getViewParent()->getDeleteFrame()->getProtectElements())) {482myNet->deleteLane(this, myNet->getUndoList(), false);483}484}485486487void488GNELane::updateGLObject() {489updateGeometry();490}491492493494GUIGLObjectPopupMenu*495GNELane::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {496// first obtain edit mode (needed because certain Commands depend of current edit mode)497const NetworkEditMode editMode = myNet->getViewNet()->getEditModes().networkEditMode;498// get mouse position499const auto mousePosition = myNet->getViewNet()->getPositionInformation();500GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);501buildPopupHeader(ret, app);502buildCenterPopupEntry(ret);503// build copy names entry504if (editMode != NetworkEditMode::NETWORK_TLS) {505GUIDesigns::buildFXMenuCommand(ret, TL("Copy parent edge name to clipboard"), nullptr, ret, MID_COPY_EDGE_NAME);506buildNameCopyPopupEntry(ret);507}508// stop if we're in data mode509if (myNet->getViewNet()->getEditModes().isCurrentSupermodeData()) {510return ret;511}512// build lane selection513if (isAttributeCarrierSelected()) {514GUIDesigns::buildFXMenuCommand(ret, TL("Remove Lane From Selected"), GUIIconSubSys::getIcon(GUIIcon::FLAG_MINUS), myNet->getViewNet(), MID_REMOVESELECT);515} else {516GUIDesigns::buildFXMenuCommand(ret, TL("Add Lane To Selected"), GUIIconSubSys::getIcon(GUIIcon::FLAG_PLUS), myNet->getViewNet(), MID_ADDSELECT);517}518// build edge selection519if (getParentEdges().front()->isAttributeCarrierSelected()) {520GUIDesigns::buildFXMenuCommand(ret, TL("Remove Edge From Selected"), GUIIconSubSys::getIcon(GUIIcon::FLAG_MINUS), myNet->getViewNet(), MID_GNE_REMOVESELECT_EDGE);521} else {522GUIDesigns::buildFXMenuCommand(ret, TL("Add Edge To Selected"), GUIIconSubSys::getIcon(GUIIcon::FLAG_PLUS), myNet->getViewNet(), MID_GNE_ADDSELECT_EDGE);523}524// stop if we're in data mode525if (myNet->getViewNet()->getEditModes().isCurrentSupermodeDemand()) {526return ret;527}528// add separator529new FXMenuSeparator(ret);530if (editMode != NetworkEditMode::NETWORK_TLS) {531// build show parameters menu532buildShowParamsPopupEntry(ret);533// build position copy entry534buildPositionCopyEntry(ret, app);535}536// check if we're in supermode network537if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork()) {538// create end point539FXMenuCommand* resetEndPoints = GUIDesigns::buildFXMenuCommand(ret, TL("Reset edge end points"), nullptr, &parent, MID_GNE_RESET_GEOMETRYPOINT);540// enable or disable reset end points541if (getParentEdges().front()->hasCustomEndPoints()) {542resetEndPoints->enable();543} else {544resetEndPoints->disable();545}546// check if we clicked over a geometry point547if ((editMode == NetworkEditMode::NETWORK_MOVE) && getParentEdges().front()->clickedOverGeometryPoint(mousePosition)) {548GUIDesigns::buildFXMenuCommand(ret, TL("Set custom Geometry Point"), nullptr, &parent, MID_GNE_CUSTOM_GEOMETRYPOINT);549}550// add separator551new FXMenuSeparator(ret);552//build operations553if ((editMode != NetworkEditMode::NETWORK_CONNECT) && (editMode != NetworkEditMode::NETWORK_TLS)) {554// build edge operations555buildEdgeOperations(parent, ret);556// build lane operations557buildLaneOperations(parent, ret);558// build template operations559buildTemplateOperations(parent, ret);560// add separator561new FXMenuSeparator(ret);562// build rechable operations563buildRechableOperations(parent, ret);564} else if (editMode == NetworkEditMode::NETWORK_TLS) {565if (myNet->getViewParent()->getTLSEditorFrame()->controlsEdge(getParentEdges().front())) {566GUIDesigns::buildFXMenuCommand(ret, TL("Select state for all links from this edge:"), nullptr, nullptr, 0);567const std::vector<std::string> names = GNEInternalLane::LinkStateNames.getStrings();568for (auto it : names) {569FXuint state = GNEInternalLane::LinkStateNames.get(it);570FXMenuRadio* mc = new FXMenuRadio(ret, it.c_str(), this, FXDataTarget::ID_OPTION + state);571mc->setSelBackColor(MFXUtils::getFXColor(GNEInternalLane::colorForLinksState(state)));572mc->setBackColor(MFXUtils::getFXColor(GNEInternalLane::colorForLinksState(state)));573}574}575} else {576FXMenuCommand* mc = GUIDesigns::buildFXMenuCommand(ret, TL("Additional options available in 'Inspect Mode'"), nullptr, nullptr, 0);577mc->handle(&parent, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);578}579// build shape positions menu580if (editMode != NetworkEditMode::NETWORK_TLS) {581new FXMenuSeparator(ret);582// get lane shape583const auto& laneShape = myLaneGeometry.getShape();584// get variables585const double pos = laneShape.nearest_offset_to_point2D(mousePosition);586const Position firstAnglePos = laneShape.positionAtOffset2D(pos - 0.001);587const Position secondAnglePos = laneShape.positionAtOffset2D(pos);588const double angle = firstAnglePos.angleTo2D(secondAnglePos);589590// build menu commands591GUIDesigns::buildFXMenuCommand(ret, TL("Shape pos: ") + toString(pos), nullptr, nullptr, 0);592GUIDesigns::buildFXMenuCommand(ret, TL("Length pos: ") + toString(pos * getLaneParametricLength() / getLaneShapeLength()), nullptr, nullptr, 0);593if (getParentEdges().front()->getNBEdge()->getDistance() != 0) {594GUIDesigns::buildFXMenuCommand(ret, TL("Distance: ") + toString(getParentEdges().front()->getNBEdge()->getDistancAt(pos)), nullptr, nullptr, 0);595}596GUIDesigns::buildFXMenuCommand(ret, TL("Height: ") + toString(firstAnglePos.z()), nullptr, nullptr, 0);597GUIDesigns::buildFXMenuCommand(ret, TL("Angle: ") + toString((GeomHelper::naviDegree(angle))), nullptr, nullptr, 0);598}599}600return ret;601}602603604double605GNELane::getExaggeration(const GUIVisualizationSettings& s) const {606return s.addSize.getExaggeration(s, this);607}608609610Boundary611GNELane::getCenteringBoundary() const {612return myNetworkElementContour.getContourBoundary();613}614615616void617GNELane::updateCenteringBoundary(const bool /*updateGrid*/) {618// nothing to update619}620621622int623GNELane::getIndex() const {624return myIndex;625}626627628void629GNELane::setIndex(int index) {630myIndex = index;631setNetworkElementID(getParentEdges().front()->getNBEdge()->getLaneID(index));632}633634635double636GNELane::getSpeed() const {637return getParentEdges().front()->getNBEdge()->getLaneSpeed(myIndex);638}639640641double642GNELane::getLaneParametricLength() const {643double laneParametricLength = getParentEdges().front()->getNBEdge()->getLoadedLength();644if (laneParametricLength > 0) {645return laneParametricLength;646} else {647throw ProcessError(TL("Lane Parametric Length cannot be never 0"));648}649}650651652double653GNELane::getLaneShapeLength() const {654return myLaneGeometry.getShape().length();655}656657658bool659GNELane::isRestricted(SUMOVehicleClass vclass) const {660return getParentEdges().front()->getNBEdge()->getPermissions(myIndex) == vclass;661}662663664const GNELane2laneConnection&665GNELane::getLane2laneConnections() const {666return myLane2laneConnections;667}668669670std::string671GNELane::getAttribute(SumoXMLAttr key) const {672const NBEdge* edge = getParentEdges().front()->getNBEdge();673switch (key) {674case SUMO_ATTR_ID:675return getMicrosimID();676case SUMO_ATTR_FROM_JUNCTION:677return getParentEdges().front()->getFromJunction()->getID();678case SUMO_ATTR_TO_JUNCTION:679return getParentEdges().front()->getToJunction()->getID();680case SUMO_ATTR_SPEED:681return toString(edge->getLaneSpeed(myIndex));682case SUMO_ATTR_ALLOW:683return getVehicleClassNames(edge->getPermissions(myIndex));684case SUMO_ATTR_DISALLOW:685return getVehicleClassNames(invertPermissions(edge->getPermissions(myIndex)));686case SUMO_ATTR_CHANGE_LEFT:687return getVehicleClassNames(edge->getLaneStruct(myIndex).changeLeft);688case SUMO_ATTR_CHANGE_RIGHT:689return getVehicleClassNames(edge->getLaneStruct(myIndex).changeRight);690case SUMO_ATTR_WIDTH:691if (edge->getLaneStruct(myIndex).width == NBEdge::UNSPECIFIED_WIDTH) {692return "default";693} else {694return toString(edge->getLaneStruct(myIndex).width);695}696case SUMO_ATTR_FRICTION:697return toString(edge->getLaneStruct(myIndex).friction);698case SUMO_ATTR_ENDOFFSET:699return toString(edge->getLaneStruct(myIndex).endOffset);700case SUMO_ATTR_ACCELERATION:701return toString(edge->getLaneStruct(myIndex).accelRamp);702case SUMO_ATTR_SHAPE:703case SUMO_ATTR_CUSTOMSHAPE:704return toString(edge->getLaneStruct(myIndex).customShape);705case GNE_ATTR_OPPOSITE:706return toString(edge->getLaneStruct(myIndex).oppositeID);707case SUMO_ATTR_TYPE:708return edge->getLaneStruct(myIndex).type;709case SUMO_ATTR_INDEX:710return toString(myIndex);711case GNE_ATTR_STOPOFFSET:712return toString(edge->getLaneStruct(myIndex).laneStopOffset.getOffset());713case GNE_ATTR_STOPOEXCEPTION:714if (edge->getLaneStruct(myIndex).laneStopOffset.isDefined()) {715return toString(edge->getLaneStruct(myIndex).laneStopOffset.getExceptions());716} else {717return "";718}719case GNE_ATTR_PARENT:720return getParentEdges().front()->getID();721default:722return getCommonAttribute(key);723}724}725726727double728GNELane::getAttributeDouble(SumoXMLAttr key) const {729return getCommonAttributeDouble(key);730}731732733Position734GNELane::getAttributePosition(SumoXMLAttr key) const {735return getCommonAttributePosition(key);736}737738739PositionVector740GNELane::getAttributePositionVector(SumoXMLAttr key) const {741switch (key) {742case SUMO_ATTR_SHAPE:743case SUMO_ATTR_CUSTOMSHAPE:744return getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape;745default:746return getCommonAttributePositionVector(key);747}748}749750751std::string752GNELane::getAttributeForSelection(SumoXMLAttr key) const {753std::string result = getAttribute(key);754if ((key == SUMO_ATTR_ALLOW || key == SUMO_ATTR_DISALLOW) && result.find("all") != std::string::npos) {755result += " " + getVehicleClassNames(SVCAll, true);756}757return result;758}759760761void762GNELane::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {763switch (key) {764case SUMO_ATTR_ID:765throw InvalidArgument("Modifying attribute '" + toString(key) + "' of " + getTagStr() + " isn't allowed");766case SUMO_ATTR_SPEED:767case SUMO_ATTR_ALLOW:768case SUMO_ATTR_DISALLOW:769case SUMO_ATTR_CHANGE_LEFT:770case SUMO_ATTR_CHANGE_RIGHT:771case SUMO_ATTR_WIDTH:772case SUMO_ATTR_FRICTION:773case SUMO_ATTR_ENDOFFSET:774case SUMO_ATTR_ACCELERATION:775case SUMO_ATTR_SHAPE:776case SUMO_ATTR_CUSTOMSHAPE:777case GNE_ATTR_OPPOSITE:778case SUMO_ATTR_TYPE:779case SUMO_ATTR_INDEX:780case GNE_ATTR_STOPOFFSET:781// special case for stop offset, because affects to stopOffsetExceptions (#15297)782if (canParse<double>(value) && (parse<double>(value) == 0)) {783GNEChange_Attribute::changeAttribute(this, GNE_ATTR_STOPOEXCEPTION, "", undoList);784}785GNEChange_Attribute::changeAttribute(this, key, value, undoList);786break;787case GNE_ATTR_STOPOEXCEPTION:788GNEChange_Attribute::changeAttribute(this, key, value, undoList);789break;790default:791setCommonAttribute(key, value, undoList);792break;793}794}795796797bool798GNELane::isValid(SumoXMLAttr key, const std::string& value) {799switch (key) {800case SUMO_ATTR_ID:801case SUMO_ATTR_INDEX:802return false;803case SUMO_ATTR_SPEED:804return canParse<double>(value);805case SUMO_ATTR_ALLOW:806case SUMO_ATTR_DISALLOW:807case SUMO_ATTR_CHANGE_LEFT:808case SUMO_ATTR_CHANGE_RIGHT:809return canParseVehicleClasses(value);810case SUMO_ATTR_WIDTH:811if (value.empty() || (value == "default")) {812return true;813} else {814return canParse<double>(value) && ((parse<double>(value) > 0) || (parse<double>(value) == NBEdge::UNSPECIFIED_WIDTH));815}816case SUMO_ATTR_FRICTION:817case SUMO_ATTR_ENDOFFSET:818return canParse<double>(value) && (parse<double>(value) >= 0);819case SUMO_ATTR_ACCELERATION:820return canParse<bool>(value);821case SUMO_ATTR_SHAPE:822case SUMO_ATTR_CUSTOMSHAPE:823// A lane shape can either be empty or have more than 1 element824if (value.empty()) {825return true;826} else if (canParse<PositionVector>(value)) {827return parse<PositionVector>(value).size() > 1;828}829return false;830case GNE_ATTR_OPPOSITE: {831if (value.empty()) {832return true;833} else {834NBEdge* oppEdge = myNet->getEdgeCont().retrieve(value.substr(0, value.rfind("_")));835if (oppEdge == nullptr || oppEdge->getLaneID(oppEdge->getNumLanes() - 1) != value) {836return false;837}838NBEdge* edge = getParentEdges().front()->getNBEdge();839if (oppEdge->getFromNode() != edge->getToNode() || oppEdge->getToNode() != edge->getFromNode()) {840WRITE_WARNINGF(TL("Opposite lane '%' does not connect the same nodes as edge '%'!"), value, edge->getID());841return false;842}843return true;844}845}846case SUMO_ATTR_TYPE:847return true;848case GNE_ATTR_STOPOFFSET:849return canParse<double>(value) && (parse<double>(value) >= 0);850case GNE_ATTR_STOPOEXCEPTION:851return canParseVehicleClasses(value);852default:853return isCommonAttributeValid(key, value);854}855}856857858bool859GNELane::isAttributeEnabled(SumoXMLAttr key) const {860switch (key) {861case SUMO_ATTR_ID:862case SUMO_ATTR_INDEX:863return false;864case GNE_ATTR_STOPOEXCEPTION:865return getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).laneStopOffset.getOffset() > 0;866default:867return true;868}869}870871872bool873GNELane::isAttributeComputed(SumoXMLAttr key) const {874const NBEdge* edge = getParentEdges().front()->getNBEdge();875switch (key) {876case SUMO_ATTR_WIDTH:877return (edge->getLaneStruct(myIndex).width == NBEdge::UNSPECIFIED_WIDTH);878default:879return false;880}881}882883884void885GNELane::setSpecialColor(const RGBColor* color, double colorValue) {886mySpecialColor = color;887mySpecialColorValue = colorValue;888}889890// ===========================================================================891// private892// ===========================================================================893894void895GNELane::setAttribute(SumoXMLAttr key, const std::string& value) {896// get parent edge897NBEdge* edge = getParentEdges().front()->getNBEdge();898// get template editor899GNEInspectorFrame::TemplateEditor* templateEditor = myNet->getViewParent()->getInspectorFrame()->getTemplateEditor();900// check if we have to update template901const bool updateTemplate = templateEditor->getEdgeTemplate() ? (templateEditor->getEdgeTemplate()->getID() == getParentEdges().front()->getID()) : false;902switch (key) {903case SUMO_ATTR_ID:904case SUMO_ATTR_INDEX:905throw InvalidArgument("Modifying attribute '" + toString(key) + "' of " + getTagStr() + " isn't allowed");906case SUMO_ATTR_SPEED:907edge->setSpeed(myIndex, parse<double>(value));908break;909case SUMO_ATTR_ALLOW:910edge->setPermissions(parseVehicleClasses(value), myIndex);911break;912case SUMO_ATTR_DISALLOW:913edge->setPermissions(invertPermissions(parseVehicleClasses(value)), myIndex);914break;915case SUMO_ATTR_CHANGE_LEFT:916edge->setPermittedChanging(myIndex, parseVehicleClasses(value), edge->getLaneStruct(myIndex).changeRight);917break;918case SUMO_ATTR_CHANGE_RIGHT:919edge->setPermittedChanging(myIndex, edge->getLaneStruct(myIndex).changeLeft, parseVehicleClasses(value));920break;921case SUMO_ATTR_WIDTH:922if (value.empty() || (value == "default")) {923edge->setLaneWidth(myIndex, NBEdge::UNSPECIFIED_WIDTH);924} else {925edge->setLaneWidth(myIndex, parse<double>(value));926}927// update edge parent boundary928getParentEdges().front()->updateCenteringBoundary(true);929break;930case SUMO_ATTR_FRICTION:931edge->setFriction(myIndex, parse<double>(value));932break;933case SUMO_ATTR_ENDOFFSET:934edge->setEndOffset(myIndex, parse<double>(value));935break;936case SUMO_ATTR_ACCELERATION:937edge->setAcceleration(myIndex, parse<bool>(value));938break;939case SUMO_ATTR_SHAPE:940case SUMO_ATTR_CUSTOMSHAPE:941// set new shape942edge->setLaneShape(myIndex, parse<PositionVector>(value));943// update edge parent boundary944getParentEdges().front()->updateCenteringBoundary(true);945break;946case GNE_ATTR_OPPOSITE: {947if (value != "") {948NBEdge* oppEdge = myNet->getEdgeCont().retrieve(value.substr(0, value.rfind("_")));949oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1).oppositeID = getID();950} else {951// reset prior oppEdge if existing952const std::string oldValue = getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).oppositeID;953NBEdge* oppEdge = myNet->getEdgeCont().retrieve(oldValue.substr(0, oldValue.rfind("_")));954if (oppEdge != nullptr) {955oppEdge->getLaneStruct(oppEdge->getNumLanes() - 1).oppositeID = "";956}957}958getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).oppositeID = value;959break;960}961case SUMO_ATTR_TYPE:962edge->getLaneStruct(myIndex).type = value;963break;964case GNE_ATTR_STOPOFFSET:965if (value.empty()) {966edge->getLaneStruct(myIndex).laneStopOffset.setOffset(0);967} else {968edge->getLaneStruct(myIndex).laneStopOffset.setOffset(parse<double>(value));969}970break;971case GNE_ATTR_STOPOEXCEPTION:972edge->getLaneStruct(myIndex).laneStopOffset.setExceptions(value);973break;974default:975setCommonAttribute(key, value);976break;977}978// update template979if (updateTemplate) {980templateEditor->setEdgeTemplate(getParentEdges().front());981}982// invalidate demand path calculator983myNet->getDemandPathManager()->getPathCalculator()->invalidatePathCalculator();984}985986987void988GNELane::drawLane(const GUIVisualizationSettings& s, const double layer) const {989// Push layer matrix990GLHelper::pushMatrix();991// translate to layer992drawInLayer(layer);993// set lane colors994setLaneColor(s);995// Check if lane has to be draw as railway and if isn't being drawn for selecting996if (myDrawingConstants->drawAsRailway()) {997// draw as railway998drawLaneAsRailway();999} else if (myShapeColors.size() > 0) {1000// draw geometry with own colors1001GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, myShapeColors,1002myDrawingConstants->getDrawingWidth(), myDrawingConstants->getOffset());1003} else {1004// draw geometry with current color1005const GUIVisualizationSettings::Detail d = myDrawingConstants->getDetail();1006double drawingWidth = myDrawingConstants->getDrawingWidth();1007if (d > GUIVisualizationSettings::Detail::GeometryBoxLines && d < GUIVisualizationSettings::Detail::GeometryBoxSimpleLine) {1008drawingWidth = myNet->getViewNet()->m2p(drawingWidth);1009}1010GUIGeometry::drawGeometry(d, myLaneGeometry, drawingWidth,1011myDrawingConstants->getOffset());1012}1013// if lane is selected, draw a second lane over it1014drawSelectedLane(s);1015// draw start end shape points1016drawStartEndGeometryPoints(s);1017// check if draw details1018if (myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::LaneDetails) {1019// draw markings1020drawMarkingsAndBoundings(s);1021// Draw direction indicators1022drawDirectionIndicators(s);1023// draw lane textures1024drawTextures(s);1025// draw lane arrows1026drawArrows(s);1027// draw link numbers1028drawLinkNo(s);1029// draw TLS link numbers1030drawTLSLinkNo(s);1031// draw stopOffsets1032drawLaneStopOffset(s);1033}1034// draw shape edited1035drawShapeEdited(s);1036// Pop layer matrix1037GLHelper::popMatrix();1038}103910401041void1042GNELane::drawSelectedLane(const GUIVisualizationSettings& s) const {1043// only draw if lane is selected1044if (drawUsingSelectColor()) {1045// Push matrix1046GLHelper::pushMatrix();1047// move back1048glTranslated(0, 0, 0.1);1049// set selected edge color1050GLHelper::setColor(s.colorSettings.selectedLaneColor);1051// draw geometry with current color1052GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, myDrawingConstants->getDrawingWidth(),1053myDrawingConstants->getOffset());1054// Pop matrix1055GLHelper::popMatrix();1056}1057}105810591060void1061GNELane::drawShapeEdited(const GUIVisualizationSettings& s) const {1062// if shape is being edited, draw point and green line1063if (myShapeEdited) {1064// push shape edited matrix1065GLHelper::pushMatrix();1066// translate1067drawInLayer(GLO_JUNCTION + 1);1068// set selected edge color1069GLHelper::setColor(s.colorSettings.editShapeColor);1070// draw shape around1071GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, 0.25,1072myDrawingConstants->getOffset());1073// move front1074glTranslated(0, 0, 1);1075// draw geometry points1076GUIGeometry::drawGeometryPoints(myDrawingConstants->getDetail(), myLaneGeometry.getShape(),1077s.colorSettings.editShapeColor.changedBrightness(-32),1078s.neteditSizeSettings.laneGeometryPointRadius, 1,1079myNet->getViewNet()->getNetworkViewOptions().editingElevation());1080// Pop shape edited matrix1081GLHelper::popMatrix();1082}1083}108410851086void1087GNELane::drawChildren(const GUIVisualizationSettings& s) const {1088// draw additional children1089for (const auto& additional : getChildAdditionals()) {1090if (!additional->getTagProperty()->isListedElement()) {1091additional->drawGL(s);1092}1093}1094// draw demand element children1095for (const auto& demandElement : getChildDemandElements()) {1096if (!demandElement->getTagProperty()->isPlacedInRTree()) {1097demandElement->drawGL(s);1098}1099}1100// draw path additional elements1101myNet->getNetworkPathManager()->drawLanePathElements(s, this);1102myNet->getDemandPathManager()->drawLanePathElements(s, this);1103myNet->getDataPathManager()->drawLanePathElements(s, this);1104}110511061107void1108GNELane::drawMarkingsAndBoundings(const GUIVisualizationSettings& s) const {1109// check conditions1110if (s.laneShowBorders && !myDrawingConstants->drawAsRailway()) {1111// check if this is the last lane (note: First lane is the lane more far of the edge's center)1112const bool firstlane = (myIndex == 0);1113const bool lastLane = (myIndex == (getParentEdges().front()->getNBEdge()->getNumLanes() - 1));1114// declare separator width1115const auto separatorWidth = SUMO_const_laneMarkWidth * 0.5;1116// get passengers change left and right for previous, current and next lane1117const bool changeRightTop = lastLane ? true : getParentEdges().front()->getNBEdge()->allowsChangingRight(myIndex + 1, SVC_PASSENGER);1118const bool changeLeftCurrent = lastLane ? true : getParentEdges().front()->getNBEdge()->allowsChangingLeft(myIndex, SVC_PASSENGER);1119const bool changeRightCurrent = firstlane ? true : getParentEdges().front()->getNBEdge()->allowsChangingRight(myIndex, SVC_PASSENGER);1120const bool changeLeftBot = firstlane ? true : getParentEdges().front()->getNBEdge()->allowsChangingLeft(myIndex - 1, SVC_PASSENGER);1121// save current color1122const auto currentColor = GLHelper::getColor();1123// separator offsets1124const double topSeparatorOffset = myDrawingConstants->getOffset() + (myDrawingConstants->getDrawingWidth() * -1) + separatorWidth;1125const double botSeparatorOffset = myDrawingConstants->getOffset() + myDrawingConstants->getDrawingWidth() - separatorWidth;1126// push matrix1127GLHelper::pushMatrix();1128// translate1129glTranslated(0, 0, 0.1);1130// continue depending of lanes1131if (myDrawingConstants->drawSuperposed() || (firstlane && lastLane)) {1132// draw top and bot separator only1133GLHelper::setColor(RGBColor::WHITE);1134GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, topSeparatorOffset);1135GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, botSeparatorOffset);1136} else if (firstlane) {1137// draw top separator1138GLHelper::setColor((changeLeftCurrent && changeRightTop) ? RGBColor::WHITE : RGBColor::ORANGE);1139GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, topSeparatorOffset);1140// check if draw inverse marking1141if (changeLeftCurrent) {1142GLHelper::setColor(currentColor);1143GLHelper::drawInverseMarkings(myLaneGeometry.getShape(), myLaneGeometry.getShapeRotations(), myLaneGeometry.getShapeLengths(),11443, 6, topSeparatorOffset, true, true, s.lefthand, 1);1145}1146// draw bot separator1147GLHelper::setColor(RGBColor::WHITE);1148GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, botSeparatorOffset);1149} else if (lastLane) {1150// draw top separator1151GLHelper::setColor(RGBColor::WHITE);1152GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, topSeparatorOffset);1153// draw bot separator1154GLHelper::setColor((changeRightCurrent && changeLeftBot) ? RGBColor::WHITE : RGBColor::ORANGE);1155GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, botSeparatorOffset);1156// check if draw inverse marking1157if (changeRightCurrent) {1158GLHelper::setColor(currentColor);1159GLHelper::drawInverseMarkings(myLaneGeometry.getShape(), myLaneGeometry.getShapeRotations(), myLaneGeometry.getShapeLengths(),11603, 6, botSeparatorOffset, true, true, s.lefthand, 1);1161}1162} else {1163// draw top separator1164GLHelper::setColor((changeLeftCurrent && changeRightTop) ? RGBColor::WHITE : RGBColor::ORANGE);1165GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, topSeparatorOffset);1166// check if draw inverse marking1167if (changeLeftCurrent) {1168GLHelper::setColor(currentColor);1169GLHelper::drawInverseMarkings(myLaneGeometry.getShape(), myLaneGeometry.getShapeRotations(), myLaneGeometry.getShapeLengths(),11703, 6, topSeparatorOffset, true, true, s.lefthand, 1);1171}1172// draw bot separator1173GLHelper::setColor((changeRightCurrent && changeLeftBot) ? RGBColor::WHITE : RGBColor::ORANGE);1174GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, separatorWidth, botSeparatorOffset);1175// check if draw inverse marking1176if (changeRightCurrent) {1177GLHelper::setColor(currentColor);1178GLHelper::drawInverseMarkings(myLaneGeometry.getShape(), myLaneGeometry.getShapeRotations(), myLaneGeometry.getShapeLengths(),11793, 6, botSeparatorOffset, true, true, s.lefthand, 1);1180}1181}1182// pop matrix1183GLHelper::popMatrix();1184}1185}118611871188void1189GNELane::drawLinkNo(const GUIVisualizationSettings& s) const {1190// check draw conditions1191if (s.drawLinkJunctionIndex.show(getParentEdges().front()->getToJunction())) {1192// get connections1193const auto& connections = getParentEdges().front()->getNBEdge()->getConnectionsFromLane(myIndex);1194// get number of links1195const int noLinks = (int)connections.size();1196// only continue if there is links1197if (noLinks > 0) {1198// push link matrix1199GLHelper::pushMatrix();1200// move front1201glTranslated(0, 0, GLO_TEXTNAME);1202// calculate width1203const double width = getParentEdges().front()->getNBEdge()->getLaneWidth(myIndex) / (double) noLinks;1204// get X11205double x1 = getParentEdges().front()->getNBEdge()->getLaneWidth(myIndex) / 2;1206// iterate over links1207for (int i = noLinks - 1; i >= 0; i--) {1208// calculate x21209const double x2 = x1 - (double)(width / 2.);1210// get link index1211const int linkIndex = getParentEdges().front()->getNBEdge()->getToNode()->getConnectionIndex(getParentEdges().front()->getNBEdge(),1212connections[s.lefthand ? noLinks - 1 - i : i]);1213// draw link index1214GLHelper::drawTextAtEnd(toString(linkIndex), myLaneGeometry.getShape(), x2, s.drawLinkJunctionIndex, s.scale);1215// update x11216x1 -= width;1217}1218// pop link matrix1219GLHelper::popMatrix();1220}1221}1222}122312241225void1226GNELane::drawTLSLinkNo(const GUIVisualizationSettings& s) const {1227// check conditions1228if ((myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::LaneDetails) && s.drawLinkTLIndex.show(getParentEdges().front()->getToJunction()) &&1229(getParentEdges().front()->getToJunction()->getNBNode()->getControllingTLS().size() > 0)) {1230// get connections1231const auto& connections = getParentEdges().front()->getNBEdge()->getConnectionsFromLane(myIndex);1232// get numer of links1233const int noLinks = (int)connections.size();1234// only continue if there are links1235if (noLinks > 0) {1236// push link matrix1237GLHelper::pushMatrix();1238// move t front1239glTranslated(0, 0, GLO_TEXTNAME);1240// calculate width1241const double w = getParentEdges().front()->getNBEdge()->getLaneWidth(myIndex) / (double) noLinks;1242// calculate x11243double x1 = getParentEdges().front()->getNBEdge()->getLaneWidth(myIndex) / 2;1244// iterate over links1245for (int i = noLinks - 1; i >= 0; --i) {1246// calculate x21247const double x2 = x1 - (double)(w / 2.);1248// get link number1249const int linkNo = connections[s.lefthand ? noLinks - 1 - i : i].tlLinkIndex;1250// draw link number1251GLHelper::drawTextAtEnd(toString(linkNo), myLaneGeometry.getShape(), x2, s.drawLinkTLIndex, s.scale);1252// update x11253x1 -= w;1254}1255// pop link matrix1256GLHelper::popMatrix();1257}1258}1259}126012611262void1263GNELane::drawArrows(const GUIVisualizationSettings& s) const {1264if (s.showLinkDecals && getParentEdges().front()->getToJunction()->isLogicValid()) {1265// calculate begin, end and rotation1266const Position& begin = myLaneGeometry.getShape()[-2];1267const Position& end = myLaneGeometry.getShape().back();1268const double rot = GUIGeometry::calculateRotation(begin, end);1269// push arrow matrix1270GLHelper::pushMatrix();1271// move front (note: must draw on top of junction shape?1272glTranslated(0, 0, 3);1273// change color depending of spreadSuperposed1274if (myDrawingConstants->drawSuperposed()) {1275GLHelper::setColor(RGBColor::CYAN);1276} else {1277GLHelper::setColor(RGBColor::WHITE);1278}1279// move to end1280glTranslated(end.x(), end.y(), 0);1281// rotate1282glRotated(rot, 0, 0, 1);1283const double width = getParentEdges().front()->getNBEdge()->getLaneWidth(myIndex);1284if (width < SUMO_const_laneWidth) {1285glScaled(myDrawingConstants->getDrawingWidth() / SUMO_const_laneWidth, 1, 1);1286}1287// apply offset1288glTranslated(myDrawingConstants->getOffset() * -1, 0, 0);1289// get destination node1290const NBNode* dest = getParentEdges().front()->getNBEdge()->myTo;1291// draw all links iterating over connections1292for (const auto& connection : getParentEdges().front()->getNBEdge()->myConnections) {1293if (connection.fromLane == myIndex) {1294// get link direction1295LinkDirection dir = dest->getDirection(getParentEdges().front()->getNBEdge(), connection.toEdge, s.lefthand);1296// draw depending of link direction1297switch (dir) {1298case LinkDirection::STRAIGHT:1299GLHelper::drawBoxLine(Position(0, 4), 0, 2, .05);1300GLHelper::drawTriangleAtEnd(Position(0, 4), Position(0, 1), (double) 1, (double) .25);1301break;1302case LinkDirection::LEFT:1303GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1304GLHelper::drawBoxLine(Position(0, 2.5), 90, 1, .05);1305GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.5, 2.5), (double) 1, (double) .25);1306break;1307case LinkDirection::RIGHT:1308GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1309GLHelper::drawBoxLine(Position(0, 2.5), -90, 1, .05);1310GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.5, 2.5), (double) 1, (double) .25);1311break;1312case LinkDirection::TURN:1313GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1314GLHelper::drawBoxLine(Position(0, 2.5), 90, .5, .05);1315GLHelper::drawBoxLine(Position(0.5, 2.5), 180, 1, .05);1316GLHelper::drawTriangleAtEnd(Position(0.5, 2.5), Position(0.5, 4), (double) 1, (double) .25);1317break;1318case LinkDirection::TURN_LEFTHAND:1319GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1320GLHelper::drawBoxLine(Position(0, 2.5), -90, 1, .05);1321GLHelper::drawBoxLine(Position(-0.5, 2.5), -180, 1, .05);1322GLHelper::drawTriangleAtEnd(Position(-0.5, 2.5), Position(-0.5, 4), (double) 1, (double) .25);1323break;1324case LinkDirection::PARTLEFT:1325GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1326GLHelper::drawBoxLine(Position(0, 2.5), 45, .7, .05);1327GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(1.2, 1.3), (double) 1, (double) .25);1328break;1329case LinkDirection::PARTRIGHT:1330GLHelper::drawBoxLine(Position(0, 4), 0, 1.5, .05);1331GLHelper::drawBoxLine(Position(0, 2.5), -45, .7, .05);1332GLHelper::drawTriangleAtEnd(Position(0, 2.5), Position(-1.2, 1.3), (double) 1, (double) .25);1333break;1334case LinkDirection::NODIR:1335GLHelper::drawBoxLine(Position(1, 5.8), 245, 2, .05);1336GLHelper::drawBoxLine(Position(-1, 5.8), 115, 2, .05);1337glTranslated(0, 5, 0);1338GLHelper::drawOutlineCircle(0.9, 0.8, 32);1339glTranslated(0, -5, 0);1340break;1341default:1342break;1343}1344}1345}1346// pop arrow matrix1347GLHelper::popMatrix();1348}1349}135013511352void1353GNELane::drawLane2LaneConnections() const {1354GLHelper::pushMatrix();1355glTranslated(0, 0, 0.1); // must draw on top of junction shape1356std::vector<NBEdge::Connection> connections = getParentEdges().front()->getNBEdge()->getConnectionsFromLane(myIndex);1357NBNode* node = getParentEdges().front()->getNBEdge()->getToNode();1358const Position& startPos = myLaneGeometry.getShape()[-1];1359for (auto it : connections) {1360const LinkState state = node->getLinkState(getParentEdges().front()->getNBEdge(), it.toEdge, it.fromLane, it.toLane, it.mayDefinitelyPass, it.tlID);1361switch (state) {1362case LINKSTATE_TL_OFF_NOSIGNAL:1363glColor3d(1, 1, 0);1364break;1365case LINKSTATE_TL_OFF_BLINKING:1366glColor3d(0, 1, 1);1367break;1368case LINKSTATE_MAJOR:1369glColor3d(1, 1, 1);1370break;1371case LINKSTATE_MINOR:1372glColor3d(.4, .4, .4);1373break;1374case LINKSTATE_STOP:1375glColor3d(.7, .4, .4);1376break;1377case LINKSTATE_EQUAL:1378glColor3d(.7, .7, .7);1379break;1380case LINKSTATE_ALLWAY_STOP:1381glColor3d(.7, .7, 1);1382break;1383case LINKSTATE_ZIPPER:1384glColor3d(.75, .5, 0.25);1385break;1386default:1387throw ProcessError(TLF("Unexpected LinkState '%'", toString(state)));1388}1389const Position& endPos = it.toEdge->getLaneShape(it.toLane)[0];1390glBegin(GL_LINES);1391glVertex2d(startPos.x(), startPos.y());1392glVertex2d(endPos.x(), endPos.y());1393glEnd();1394GLHelper::drawTriangleAtEnd(startPos, endPos, (double) 1.5, (double) .2);1395}1396GLHelper::popMatrix();1397}139813991400void1401GNELane::calculateLaneContour(const GUIVisualizationSettings& s, const double layer) const {1402// first check if edge parent was inserted with full boundary1403if (!gViewObjectsHandler.checkBoundaryParentObject(this, layer, getParentEdges().front())) {1404// calculate contour1405myNetworkElementContour.calculateContourExtrudedShape(s, myDrawingConstants->getDetail(),1406this, myLaneGeometry.getShape(), layer, myDrawingConstants->getDrawingWidth(), 1,1407true, true, myDrawingConstants->getOffset(), nullptr, getParentEdges().front());1408// calculate geometry points contour if we're editing shape1409if (myShapeEdited) {1410myNetworkElementContour.calculateContourAllGeometryPoints(s, myDrawingConstants->getDetail(),1411this, myLaneGeometry.getShape(), layer, s.neteditSizeSettings.laneGeometryPointRadius,1412myDrawingConstants->getExaggeration(), true);1413}1414}1415}141614171418RGBColor1419GNELane::setLaneColor(const GUIVisualizationSettings& s) const {1420const auto& inspectedElements = myNet->getViewNet()->getInspectedElements();1421// declare a RGBColor variable1422RGBColor color;1423// we need to draw lanes with a special color if we're inspecting a Trip or Flow and this lane belongs to a via's edge.1424if (inspectedElements.getFirstAC() &&1425!inspectedElements.getFirstAC()->isAttributeCarrierSelected() &&1426inspectedElements.getFirstAC()->getTagProperty()->vehicleEdges()) {1427// obtain attribute "via"1428std::vector<std::string> viaEdges = parse<std::vector<std::string> >(inspectedElements.getFirstAC()->getAttribute(SUMO_ATTR_VIA));1429// iterate over viaEdges1430for (const auto& edge : viaEdges) {1431// check if parent edge is in the via edges1432if (getParentEdges().front()->getID() == edge) {1433// set green color in GLHelper and return it1434color = RGBColor::GREEN;1435}1436}1437}1438if (mySpecialColor != nullptr) {1439// If special color is enabled, set it1440color = *mySpecialColor;1441} else if (getParentEdges().front()->drawUsingSelectColor() && s.laneColorer.getActive() != 1) {1442// override with special colors (unless the color scheme is based on selection)1443color = s.colorSettings.selectedEdgeColor;1444} else {1445// Get normal lane color1446const GUIColorer& c = s.laneColorer;1447if (!setFunctionalColor(c.getActive(), color) && !setMultiColor(s, c, color)) {1448color = c.getScheme().getColor(getColorValue(s, c.getActive()));1449}1450}1451// special color for conflicted candidate edges1452if (getParentEdges().front()->isConflictedCandidate()) {1453// extra check for route frame1454if (myNet->getViewParent()->getRouteFrame()->getPathCreator()->drawCandidateEdgesWithSpecialColor()) {1455color = s.candidateColorSettings.conflict;1456}1457}1458// special color for special candidate edges1459if (getParentEdges().front()->isSpecialCandidate()) {1460// extra check for route frame1461if (myNet->getViewParent()->getRouteFrame()->getPathCreator()->drawCandidateEdgesWithSpecialColor()) {1462color = s.candidateColorSettings.special;1463}1464}1465// special color for candidate edges1466if (getParentEdges().front()->isPossibleCandidate()) {1467// extra check for route frame1468if (myNet->getViewParent()->getRouteFrame()->getPathCreator()->drawCandidateEdgesWithSpecialColor()) {1469color = s.candidateColorSettings.possible;1470}1471}1472// special color for source candidate edges1473if (getParentEdges().front()->isSourceCandidate()) {1474color = s.candidateColorSettings.source;1475}1476// special color for target candidate edges1477if (getParentEdges().front()->isTargetCandidate()) {1478color = s.candidateColorSettings.target;1479}1480// special color for invalid candidate edges1481if (getParentEdges().front()->isInvalidCandidate()) {1482color = s.candidateColorSettings.invalid;1483}1484// special color for source candidate lanes1485if (mySourceCandidate) {1486color = s.candidateColorSettings.source;1487}1488// special color for target candidate lanes1489if (myTargetCandidate) {1490color = s.candidateColorSettings.target;1491}1492// special color for special candidate lanes1493if (mySpecialCandidate) {1494color = s.candidateColorSettings.special;1495}1496// special color for possible candidate lanes1497if (myPossibleCandidate) {1498color = s.candidateColorSettings.possible;1499}1500// special color for conflicted candidate lanes1501if (myConflictedCandidate) {1502color = s.candidateColorSettings.conflict;1503}1504// special color for invalid candidate lanes1505if (myInvalidCandidate) {1506color = s.candidateColorSettings.invalid;1507}1508// set color in GLHelper1509GLHelper::setColor(color);1510return color;1511}151215131514bool1515GNELane::setFunctionalColor(int activeScheme, RGBColor& col) const {1516switch (activeScheme) {1517case 6: {1518double hue = GeomHelper::naviDegree(myLaneGeometry.getShape().beginEndAngle()); // [0-360]1519col = RGBColor::fromHSV(hue, 1., 1.);1520return true;1521}1522default:1523return false;1524}1525}152615271528bool1529GNELane::setMultiColor(const GUIVisualizationSettings& s, const GUIColorer& c, RGBColor& col) const {1530const int activeScheme = c.getActive();1531myShapeColors.clear();1532switch (activeScheme) {1533case 9: // color by height at segment start1534for (PositionVector::const_iterator ii = myLaneGeometry.getShape().begin(); ii != myLaneGeometry.getShape().end() - 1; ++ii) {1535myShapeColors.push_back(c.getScheme().getColor(ii->z()));1536}1537col = c.getScheme().getColor(getColorValue(s, 8));1538return true;1539case 11: // color by inclination at segment start1540for (int ii = 1; ii < (int)myLaneGeometry.getShape().size(); ++ii) {1541const double inc = (myLaneGeometry.getShape()[ii].z() - myLaneGeometry.getShape()[ii - 1].z()) / MAX2(POSITION_EPS, myLaneGeometry.getShape()[ii].distanceTo2D(myLaneGeometry.getShape()[ii - 1]));1542myShapeColors.push_back(c.getScheme().getColor(inc));1543}1544col = c.getScheme().getColor(getColorValue(s, 10));1545return true;1546default:1547return false;1548}1549}155015511552double1553GNELane::getColorValue(const GUIVisualizationSettings& s, int activeScheme) const {1554const SVCPermissions myPermissions = getParentEdges().front()->getNBEdge()->getPermissions(myIndex);1555if (mySpecialColor != nullptr && mySpecialColorValue != std::numeric_limits<double>::max()) {1556return mySpecialColorValue;1557}1558switch (activeScheme) {1559case 0:1560switch (myPermissions) {1561case SVC_PEDESTRIAN:1562return 1;1563case SVC_BICYCLE:1564return 2;1565case 0:1566// forbidden road or green verge1567return getParentEdges().front()->getNBEdge()->getPermissions() == 0 ? 10 : 3;1568case SVC_SHIP:1569return 4;1570case SVC_AUTHORITY:1571return 8;1572case SVC_AIRCRAFT:1573case SVC_DRONE:1574return 12;1575default:1576break;1577}1578if (getParentEdges().front()->getNBEdge()->isMacroscopicConnector()) {1579return 9;1580} else if (isRailway(myPermissions)) {1581return 5;1582} else if ((myPermissions & SVC_PASSENGER) != 0) {1583if ((myPermissions & (SVC_RAIL_CLASSES & ~SVC_RAIL_FAST)) != 0 && (myPermissions & SVC_SHIP) == 0) {1584return 6;1585} else {1586return 0;1587}1588} else {1589if ((myPermissions & SVC_RAIL_CLASSES) != 0 && (myPermissions & SVC_SHIP) == 0) {1590return 6;1591} else {1592return 7;1593}1594}1595case 1:1596return isAttributeCarrierSelected() || getParentEdges().front()->isAttributeCarrierSelected();1597case 2:1598return (double)myPermissions;1599case 3:1600return getParentEdges().front()->getNBEdge()->getLaneSpeed(myIndex);1601case 4:1602return getParentEdges().front()->getNBEdge()->getNumLanes();1603case 5: {1604return getParentEdges().front()->getNBEdge()->getLoadedLength() / getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).shape.length();1605}1606// case 6: by angle (functional)1607case 7: {1608return getParentEdges().front()->getNBEdge()->getPriority();1609}1610case 8: {1611// color by z of first shape point1612return myLaneGeometry.getShape()[0].z();1613}1614// case 9: by segment height1615case 10: {1616// color by incline1617return (myLaneGeometry.getShape()[-1].z() - myLaneGeometry.getShape()[0].z()) / getParentEdges().front()->getNBEdge()->getLength();1618}1619// case 11: by segment incline16201621case 12: {1622// by numerical edge param value1623if (getParentEdges().front()->getNBEdge()->hasParameter(s.edgeParam)) {1624try {1625return StringUtils::toDouble(getParentEdges().front()->getNBEdge()->getParameter(s.edgeParam, "0"));1626} catch (NumberFormatException&) {1627try {1628return StringUtils::toBool(getParentEdges().front()->getNBEdge()->getParameter(s.edgeParam, "0"));1629} catch (BoolFormatException&) {1630return -1;1631}1632}1633} else {1634return GUIVisualizationSettings::MISSING_DATA;1635}1636}1637case 13: {1638// by numerical lane param value1639if (getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).hasParameter(s.laneParam)) {1640try {1641return StringUtils::toDouble(getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).getParameter(s.laneParam, "0"));1642} catch (NumberFormatException&) {1643try {1644return StringUtils::toBool(getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).getParameter(s.laneParam, "0"));1645} catch (BoolFormatException&) {1646return -1;1647}1648}1649} else {1650return GUIVisualizationSettings::MISSING_DATA;1651}1652}1653case 14: {1654return getParentEdges().front()->getNBEdge()->getDistance();1655}1656case 15: {1657return fabs(getParentEdges().front()->getNBEdge()->getDistance());1658}1659}1660return 0;1661}166216631664void1665GNELane::drawOverlappedRoutes(const int numRoutes) const {1666// get middle point and angle1667const Position center = myLaneGeometry.getShape().positionAtOffset2D(myLaneGeometry.getShape().length2D() * 0.5);1668const double angle = myLaneGeometry.getShape().rotationDegreeAtOffset(myLaneGeometry.getShape().length2D() * 0.5);1669// Push route matrix1670GLHelper::pushMatrix();1671// translate to front1672glTranslated(0, 0, GLO_ROUTE + 1);1673// get middle1674GLHelper::drawText(toString(numRoutes) + " routes", center, 0, 1.8, RGBColor::BLACK, angle + 90);1675// pop route matrix1676GLHelper::popMatrix();16771678}167916801681void1682GNELane::drawLaneStopOffset(const GUIVisualizationSettings& s) const {1683const auto& laneStopOffset = getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).laneStopOffset;1684// check conditions1685if (laneStopOffset.isDefined() && (laneStopOffset.getPermissions() & SVC_PASSENGER) != 0) {1686const Position& end = getLaneShape().back();1687const Position& f = getLaneShape()[-2];1688const double rot = RAD2DEG(atan2((end.x() - f.x()), (f.y() - end.y())));1689GLHelper::setColor(s.getLinkColor(LINKSTATE_MAJOR));1690GLHelper::pushMatrix();1691glTranslated(end.x(), end.y(), 1);1692glRotated(rot, 0, 0, 1);1693glTranslated(0, laneStopOffset.getOffset(), 0);1694glBegin(GL_QUADS);1695glVertex2d(-myDrawingConstants->getDrawingWidth(), 0.0);1696glVertex2d(-myDrawingConstants->getDrawingWidth(), 0.2);1697glVertex2d(myDrawingConstants->getDrawingWidth(), 0.2);1698glVertex2d(myDrawingConstants->getDrawingWidth(), 0.0);1699glEnd();1700GLHelper::popMatrix();1701}1702}170317041705bool1706GNELane::drawAsWaterway(const GUIVisualizationSettings& s) const {1707return isWaterway(getParentEdges().front()->getNBEdge()->getPermissions(myIndex)) && s.showRails; // reusing the showRails setting1708}170917101711void1712GNELane::drawDirectionIndicators(const GUIVisualizationSettings& s) const {1713// Draw direction indicators if the correspondient option is enabled1714if (s.showLaneDirection) {1715// improve visibility of superposed rail edges1716if (!myDrawingConstants->drawAsRailway()) {1717glColor3d(0.3, 0.3, 0.3);1718}1719// get width and sideOffset1720const double width = MAX2(NUMERICAL_EPS, (myDrawingConstants->getDrawingWidth() * 2 * myDrawingConstants->getExaggeration()));1721// push direction indicator matrix1722GLHelper::pushMatrix();1723// move to front1724glTranslated(0, 0, 0.1);1725// iterate over shape1726for (int i = 0; i < (int) myLaneGeometry.getShape().size() - 1; ++i) {1727// push triangle matrix1728GLHelper::pushMatrix();1729// move front1730glTranslated(myLaneGeometry.getShape()[i].x(), myLaneGeometry.getShape()[i].y(), 0.1);1731// rotate1732glRotated(myLaneGeometry.getShapeRotations()[i], 0, 0, 1);1733// calculate subwidth1734for (double subWidth = 0; subWidth < myLaneGeometry.getShapeLengths()[i]; subWidth += width) {1735// calculate length1736const double length = MIN2(width * 0.5, myLaneGeometry.getShapeLengths()[i] - subWidth);1737// draw triangle1738glBegin(GL_TRIANGLES);1739glVertex2d(-myDrawingConstants->getOffset(), -subWidth - length);1740glVertex2d(-myDrawingConstants->getOffset() - width * 0.25, -subWidth);1741glVertex2d(-myDrawingConstants->getOffset() + width * 0.25, -subWidth);1742glEnd();1743}1744// pop triangle matrix1745GLHelper::popMatrix();1746}1747// pop direction indicator matrix1748GLHelper::popMatrix();1749}1750}175117521753void1754GNELane::drawLaneAsRailway() const {1755// draw foot width 150mm, assume that distance between rail feet inner sides is reduced on both sides by 39mm with regard to the gauge1756// assume crosstie length of 181% gauge (2600mm for standard gauge)1757// first save current color (obtained from view configuration)1758const auto currentLaneColor = GLHelper::getColor();1759// Set current color1760GLHelper::setColor(currentLaneColor);1761// continue depending of detail1762if (myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::LaneDetails) {1763// move1764glTranslated(0, 0, 0.1);1765// draw external crossbar1766const double crossbarWidth = 0.2 * myDrawingConstants->getExaggeration();1767// draw geometry1768GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry,1769myDrawingConstants->getDrawingWidth() * 0.8,1770myDrawingConstants->getOffset());1771// move1772glTranslated(0, 0, 0.01);1773// Set color gray1774glColor3d(0.8, 0.8, 0.8);1775// draw geometry1776GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry,1777myDrawingConstants->getDrawingWidth() * 0.6,1778myDrawingConstants->getOffset());1779// move1780glTranslated(0, 0, 0.01);1781// Set current color1782GLHelper::setColor(currentLaneColor);1783// Draw crossties1784GLHelper::drawCrossTies(myLaneGeometry.getShape(), myLaneGeometry.getShapeRotations(), myLaneGeometry.getShapeLengths(),1785crossbarWidth, 0.6 * myDrawingConstants->getExaggeration(), myDrawingConstants->getDrawingWidth(),1786myDrawingConstants->getOffset(), false);1787} else if (myShapeColors.size() > 0) {1788// draw colored box lines1789GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, myShapeColors,1790myDrawingConstants->getDrawingWidth(), myDrawingConstants->getOffset());1791} else {1792// draw geometry with current color1793GUIGeometry::drawGeometry(myDrawingConstants->getDetail(), myLaneGeometry, myDrawingConstants->getDrawingWidth(),1794myDrawingConstants->getOffset());1795}1796}179717981799void1800GNELane::drawTextures(const GUIVisualizationSettings& s) const {1801// check all conditions for drawing textures1802if (!s.disableLaneIcons && (myLaneRestrictedTexturePositions.size() > 0)) {1803// Declare default width of icon (3)1804const double iconWidth = myDrawingConstants->getDrawingWidth() * 0.6;1805// Draw list of icons1806for (int i = 0; i < (int)myLaneRestrictedTexturePositions.size(); i++) {1807// Push draw matrix 21808GLHelper::pushMatrix();1809// Set white color1810glColor3d(1, 1, 1);1811// Translate matrix 21812glTranslated(myLaneRestrictedTexturePositions.at(i).x(), myLaneRestrictedTexturePositions.at(i).y(), 0.1);1813// Rotate matrix 21814glRotated(myLaneRestrictedTextureRotations.at(i), 0, 0, -1);1815glRotated(90, 0, 0, 1);1816// draw texture box depending of type of restriction1817if (isRestricted(SVC_PEDESTRIAN)) {1818GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GUITexture::LANE_PEDESTRIAN), iconWidth);1819} else if (isRestricted(SVC_BICYCLE)) {1820GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GUITexture::LANE_BIKE), iconWidth);1821} else if (isRestricted(SVC_BUS)) {1822GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getTexture(GUITexture::LANE_BUS), iconWidth);1823}1824// Pop draw matrix 21825GLHelper::popMatrix();1826}1827}1828}182918301831void1832GNELane::drawStartEndGeometryPoints(const GUIVisualizationSettings& s) const {1833// draw a Start/endPoints if lane has a custom shape1834if ((myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::GeometryPoint) && (getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape.size() > 1)) {1835// obtain circle width and resolution1836const double circleWidth = GNEEdge::SNAP_RADIUS * MIN2((double)1, s.laneWidthExaggeration) / 2;1837// obtain custom shape1838const PositionVector& customShape = getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape;1839// set color (override with special colors unless the color scheme is based on selection)1840if (drawUsingSelectColor() && s.laneColorer.getActive() != 1) {1841GLHelper::setColor(s.colorSettings.selectedEdgeColor.changedBrightness(-20));1842} else {1843GLHelper::setColor(s.junctionColorer.getSchemes()[0].getColor(2));1844}1845// push start matrix1846GLHelper::pushMatrix();1847// move to shape start position1848glTranslated(customShape.front().x(), customShape.front().y(), 0.1);1849// draw circle1850GLHelper::drawFilledCircleDetailled(myDrawingConstants->getDetail(), circleWidth);1851// draw s depending of detail1852if (myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::Text) {1853// move top1854glTranslated(0, 0, 0.1);1855// draw "S"1856GLHelper::drawText("S", Position(), 0.1, circleWidth, RGBColor::WHITE);1857}1858// pop start matrix1859GLHelper::popMatrix();1860// draw line between junction and start position1861GLHelper::pushMatrix();1862// move top1863glTranslated(0, 0, 0.1);1864// set line width1865glLineWidth(4);1866// draw line1867GLHelper::drawLine(customShape.front(), getParentEdges().front()->getFromJunction()->getPositionInView());1868// pop line matrix1869GLHelper::popMatrix();1870// push start matrix1871GLHelper::pushMatrix();1872// move to end position1873glTranslated(customShape.back().x(), customShape.back().y(), 0.1);1874// draw filled circle1875GLHelper::drawFilledCircleDetailled(myDrawingConstants->getDetail(), circleWidth);1876// draw "e" depending of detail1877if (myDrawingConstants->getDetail() <= GUIVisualizationSettings::Detail::Text) {1878// move top1879glTranslated(0, 0, 0.1);1880// draw "E"1881GLHelper::drawText("E", Position(), 0, circleWidth, RGBColor::WHITE);1882}1883// pop start matrix1884GLHelper::popMatrix();1885// draw line between Junction and end position1886GLHelper::pushMatrix();1887// move top1888glTranslated(0, 0, 0.1);1889// set line width1890glLineWidth(4);1891// draw line1892GLHelper::drawLine(customShape.back(), getParentEdges().front()->getToJunction()->getPositionInView());1893// pop line matrix1894GLHelper::popMatrix();1895}1896}189718981899std::string1900GNELane::getParentName() const {1901return getParentEdges().front()->getID();1902}190319041905long1906GNELane::onDefault(FXObject* obj, FXSelector sel, void* data) {1907myNet->getViewParent()->getTLSEditorFrame()->handleMultiChange(this, obj, sel, data);1908return 1;1909}191019111912std::vector<GNEConnection*>1913GNELane::getGNEIncomingConnections() {1914// Declare a vector to save incoming connections1915std::vector<GNEConnection*> incomingConnections;1916// Obtain incoming edges if junction source was already created1917GNEJunction* junctionSource = getParentEdges().front()->getFromJunction();1918if (junctionSource) {1919// Iterate over incoming GNEEdges of junction1920for (const auto& incomingEdge : junctionSource->getGNEIncomingEdges()) {1921// Iterate over connection of incoming edges1922for (const auto& connection : incomingEdge->getGNEConnections()) {1923if (connection->getLaneTo()->getIndex() == getIndex()) {1924incomingConnections.push_back(connection);1925}1926}1927}1928}1929return incomingConnections;1930}193119321933std::vector<GNEConnection*>1934GNELane::getGNEOutcomingConnections() {1935// Obtain GNEConnection of parent edge1936const std::vector<GNEConnection*>& edgeConnections = getParentEdges().front()->getGNEConnections();1937std::vector<GNEConnection*> outcomingConnections;1938// Obtain outgoing connections1939for (const auto& connection : edgeConnections) {1940if (connection->getLaneFrom()->getIndex() == getIndex()) {1941outcomingConnections.push_back(connection);1942}1943}1944return outcomingConnections;1945}194619471948void1949GNELane::updateConnectionIDs() {1950// update incoming connections of lane1951std::vector<GNEConnection*> incomingConnections = getGNEIncomingConnections();1952for (const auto& incomingConnection : incomingConnections) {1953incomingConnection->updateConnectionID();1954}1955// update outcoming connections of lane1956std::vector<GNEConnection*> outcomingConnections = getGNEOutcomingConnections();1957for (const auto& outcomingConnection : outcomingConnections) {1958outcomingConnection->updateConnectionID();1959}1960}196119621963double1964GNELane::getLengthGeometryFactor() const {1965// factor should not be 01966if (getParentEdges().front()->getNBEdge()->getFinalLength() > 0) {1967return MAX2(POSITION_EPS, (getLaneShape().length() / getParentEdges().front()->getNBEdge()->getFinalLength()));1968} else {1969return POSITION_EPS;1970};1971}197219731974void1975GNELane::buildEdgeOperations(GUISUMOAbstractView& parent, GUIGLObjectPopupMenu* ret) {1976// Create basic commands1977std::string edgeDescPossibleMulti = toString(SUMO_TAG_EDGE);1978const int edgeSelSize = getParentEdges().front()->isAttributeCarrierSelected() ? myNet->getAttributeCarriers()->getNumberOfSelectedEdges() : 0;1979if (edgeSelSize && getParentEdges().front()->isAttributeCarrierSelected() && (edgeSelSize > 1)) {1980edgeDescPossibleMulti = toString(edgeSelSize) + " " + toString(SUMO_TAG_EDGE) + "s";1981}1982// create menu pane for edge operations1983FXMenuPane* edgeOperations = new FXMenuPane(ret);1984ret->insertMenuPaneChild(edgeOperations);1985if (edgeSelSize > 0) {1986new FXMenuCascade(ret, TLF("Edge operations (% selected)", toString(edgeSelSize)).c_str(), nullptr, edgeOperations);1987} else {1988new FXMenuCascade(ret, TL("Edge operations"), nullptr, edgeOperations);1989}1990// create menu commands for all edge operations1991GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Split edge here"), nullptr, &parent, MID_GNE_EDGE_SPLIT);1992auto splitBothDirections = GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Split edge in both directions here (no symmetric opposite edge)"), nullptr, &parent, MID_GNE_EDGE_SPLIT_BIDI);1993// check if allow split edge in both directions1994splitBothDirections->disable();1995const auto oppositeEdges = getParentEdges().front()->getOppositeEdges();1996if (oppositeEdges.size() == 0) {1997splitBothDirections->setText(TL("Split edge in both directions here (no opposite edge)"));1998} else {1999for (const auto& oppositeEdge : oppositeEdges) {2000// get reverse inner geometry2001const auto reverseGeometry = oppositeEdge->getNBEdge()->getInnerGeometry().reverse();2002if (reverseGeometry == getParentEdges().front()->getNBEdge()->getInnerGeometry()) {2003splitBothDirections->enable();2004splitBothDirections->setText(TL("Split edge in both directions here"));2005}2006}2007}2008GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Set geometry endpoint here (shift-click)"), nullptr, &parent, MID_GNE_EDGE_EDIT_ENDPOINT);2009// restore geometry points depending of selection status2010if (getParentEdges().front()->isAttributeCarrierSelected()) {2011if (edgeSelSize == 1) {2012GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Restore both geometry endpoints"), nullptr, &parent, MID_GNE_EDGE_RESET_ENDPOINT);2013} else {2014GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Restore geometry endpoints of all selected edges"), nullptr, &parent, MID_GNE_EDGE_RESET_ENDPOINT);2015}2016} else {2017GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Restore geometry endpoint (shift-click)"), nullptr, &parent, MID_GNE_EDGE_RESET_ENDPOINT);2018}2019GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Reverse %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_REVERSE);2020auto reverse = GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Add reverse direction for %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_ADD_REVERSE);2021if (getParentEdges().front()->getReverseEdge() != nullptr) {2022reverse->disable();2023}2024GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Add reverse disconnected direction for %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_ADD_REVERSE_DISCONNECTED);2025GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Reset lengths for %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_RESET_LENGTH);2026GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Straighten %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_STRAIGHTEN);2027GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Smooth %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_SMOOTH);2028GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Straighten elevation of %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_STRAIGHTEN_ELEVATION);2029GUIDesigns::buildFXMenuCommand(edgeOperations, TLF("Smooth elevation of %", edgeDescPossibleMulti), nullptr, &parent, MID_GNE_EDGE_SMOOTH_ELEVATION);2030}203120322033void2034GNELane::buildLaneOperations(GUISUMOAbstractView& parent, GUIGLObjectPopupMenu* ret) {2035// Get icons2036FXIcon* pedestrianIcon = GUIIconSubSys::getIcon(GUIIcon::LANE_PEDESTRIAN);2037FXIcon* bikeIcon = GUIIconSubSys::getIcon(GUIIcon::LANE_BIKE);2038FXIcon* busIcon = GUIIconSubSys::getIcon(GUIIcon::LANE_BUS);2039FXIcon* greenVergeIcon = GUIIconSubSys::getIcon(GUIIcon::LANEGREENVERGE);2040// declare number of selected lanes2041int numSelectedLanes = 0;2042// if lane is selected, calculate number of restricted lanes2043bool edgeHasSidewalk = false;2044bool edgeHasBikelane = false;2045bool edgeHasBuslane = false;2046bool differentLaneShapes = false;2047if (isAttributeCarrierSelected()) {2048const auto selectedLanes = myNet->getAttributeCarriers()->getSelectedLanes();2049// update numSelectedLanes2050numSelectedLanes = (int)selectedLanes.size();2051// iterate over selected lanes2052for (const auto& selectedLane : selectedLanes) {2053if (selectedLane->getParentEdges().front()->hasRestrictedLane(SVC_PEDESTRIAN)) {2054edgeHasSidewalk = true;2055}2056if (selectedLane->getParentEdges().front()->hasRestrictedLane(SVC_BICYCLE)) {2057edgeHasBikelane = true;2058}2059if (selectedLane->getParentEdges().front()->hasRestrictedLane(SVC_BUS)) {2060edgeHasBuslane = true;2061}2062if (selectedLane->getParentEdges().front()->getNBEdge()->getLaneStruct(selectedLane->getIndex()).customShape.size() != 0) {2063differentLaneShapes = true;2064}2065}2066} else {2067edgeHasSidewalk = getParentEdges().front()->hasRestrictedLane(SVC_PEDESTRIAN);2068edgeHasBikelane = getParentEdges().front()->hasRestrictedLane(SVC_BICYCLE);2069edgeHasBuslane = getParentEdges().front()->hasRestrictedLane(SVC_BUS);2070differentLaneShapes = getParentEdges().front()->getNBEdge()->getLaneStruct(myIndex).customShape.size() != 0;2071}2072// create menu pane for lane operations2073FXMenuPane* laneOperations = new FXMenuPane(ret);2074ret->insertMenuPaneChild(laneOperations);2075if (numSelectedLanes > 0) {2076new FXMenuCascade(ret, TLF("Lane operations (% selected)", toString(numSelectedLanes)).c_str(), nullptr, laneOperations);2077} else {2078new FXMenuCascade(ret, TL("Lane operations"), nullptr, laneOperations);2079}2080GUIDesigns::buildFXMenuCommand(laneOperations, TL("Duplicate lane"), nullptr, &parent, MID_GNE_LANE_DUPLICATE);2081GUIDesigns::buildFXMenuCommand(laneOperations, TL("Set custom lane shape"), nullptr, &parent, MID_GNE_LANE_EDIT_SHAPE);2082FXMenuCommand* resetCustomShape = GUIDesigns::buildFXMenuCommand(laneOperations, TL("Reset custom shape"), nullptr, &parent, MID_GNE_LANE_RESET_CUSTOMSHAPE);2083if (!differentLaneShapes) {2084resetCustomShape->disable();2085}2086FXMenuCommand* resetOppositeLane = GUIDesigns::buildFXMenuCommand(laneOperations, TL("Reset opposite lane"), nullptr, &parent, MID_GNE_LANE_RESET_OPPOSITELANE);2087if (getAttribute(GNE_ATTR_OPPOSITE).empty()) {2088resetOppositeLane->disable();2089}2090// Create panel for lane operations and insert it in ret2091FXMenuPane* addSpecialLanes = new FXMenuPane(laneOperations);2092ret->insertMenuPaneChild(addSpecialLanes);2093FXMenuPane* removeSpecialLanes = new FXMenuPane(laneOperations);2094ret->insertMenuPaneChild(removeSpecialLanes);2095FXMenuPane* transformSlanes = new FXMenuPane(laneOperations);2096ret->insertMenuPaneChild(transformSlanes);2097// Create menu comands for all add special lanes2098FXMenuCommand* addSidewalk = GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Sidewalk"), pedestrianIcon, &parent, MID_GNE_LANE_ADD_SIDEWALK);2099FXMenuCommand* addBikelane = GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Bike lane"), bikeIcon, &parent, MID_GNE_LANE_ADD_BIKE);2100FXMenuCommand* addBuslane = GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Bus lane"), busIcon, &parent, MID_GNE_LANE_ADD_BUS);2101// if parent edge is selected, always add greenverge in front2102if (getParentEdges().front()->isAttributeCarrierSelected()) {2103GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Green verge"), greenVergeIcon, &parent, MID_GNE_LANE_ADD_GREENVERGE_FRONT);2104} else {2105GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Green verge (front)"), greenVergeIcon, &parent, MID_GNE_LANE_ADD_GREENVERGE_FRONT);2106GUIDesigns::buildFXMenuCommand(addSpecialLanes, TL("Green verge (back)"), greenVergeIcon, &parent, MID_GNE_LANE_ADD_GREENVERGE_BACK);2107}2108// Create menu comands for all remove special lanes and disable it2109FXMenuCommand* removeSidewalk = GUIDesigns::buildFXMenuCommand(removeSpecialLanes, TL("Sidewalk"), pedestrianIcon, &parent, MID_GNE_LANE_REMOVE_SIDEWALK);2110removeSidewalk->disable();2111FXMenuCommand* removeBikelane = GUIDesigns::buildFXMenuCommand(removeSpecialLanes, TL("Bike lane"), bikeIcon, &parent, MID_GNE_LANE_REMOVE_BIKE);2112removeBikelane->disable();2113FXMenuCommand* removeBuslane = GUIDesigns::buildFXMenuCommand(removeSpecialLanes, TL("Bus lane"), busIcon, &parent, MID_GNE_LANE_REMOVE_BUS);2114removeBuslane->disable();2115FXMenuCommand* removeGreenVerge = GUIDesigns::buildFXMenuCommand(removeSpecialLanes, TL("Green verge"), greenVergeIcon, &parent, MID_GNE_LANE_REMOVE_GREENVERGE);2116removeGreenVerge->disable();2117// Create menu comands for all transform special lanes and disable it2118FXMenuCommand* transformLaneToSidewalk = GUIDesigns::buildFXMenuCommand(transformSlanes, TL("Sidewalk"), pedestrianIcon, &parent, MID_GNE_LANE_TRANSFORM_SIDEWALK);2119FXMenuCommand* transformLaneToBikelane = GUIDesigns::buildFXMenuCommand(transformSlanes, TL("Bike lane"), bikeIcon, &parent, MID_GNE_LANE_TRANSFORM_BIKE);2120FXMenuCommand* transformLaneToBuslane = GUIDesigns::buildFXMenuCommand(transformSlanes, TL("Bus lane"), busIcon, &parent, MID_GNE_LANE_TRANSFORM_BUS);2121FXMenuCommand* transformLaneToGreenVerge = GUIDesigns::buildFXMenuCommand(transformSlanes, TL("Green verge"), greenVergeIcon, &parent, MID_GNE_LANE_TRANSFORM_GREENVERGE);2122// add menuCascade for lane operations2123new FXMenuCascade(laneOperations, TLF("Add restricted %", toString(SUMO_TAG_LANE)).c_str(), nullptr, addSpecialLanes);2124FXMenuCascade* cascadeRemoveSpecialLane = new FXMenuCascade(laneOperations, TLF("Remove restricted %", toString(SUMO_TAG_LANE)).c_str(), nullptr, removeSpecialLanes);2125new FXMenuCascade(laneOperations, TLF("Transform to restricted %", toString(SUMO_TAG_LANE)).c_str(), nullptr, transformSlanes);2126// Enable and disable options depending of current transform of the lane2127if (edgeHasSidewalk) {2128transformLaneToSidewalk->disable();2129addSidewalk->disable();2130removeSidewalk->enable();2131}2132if (edgeHasBikelane) {2133transformLaneToBikelane->disable();2134addBikelane->disable();2135removeBikelane->enable();2136}2137if (edgeHasBuslane) {2138transformLaneToBuslane->disable();2139addBuslane->disable();2140removeBuslane->enable();2141}2142if (isRestricted(SVC_IGNORING)) {2143transformLaneToGreenVerge->disable();2144removeGreenVerge->enable();2145}2146// Check if cascade menu must be disabled2147if (!edgeHasSidewalk && !edgeHasBikelane && !edgeHasBuslane && !isRestricted(SVC_IGNORING)) {2148cascadeRemoveSpecialLane->disable();2149}2150// for whatever reason, sonar complains in the next line that cascadeRemoveSpecialLane may leak, but fox does the cleanup2151} // NOSONAR215221532154void2155GNELane::buildTemplateOperations(GUISUMOAbstractView& parent, GUIGLObjectPopupMenu* ret) {2156// Create basic commands2157std::string edgeDescPossibleMulti = toString(SUMO_TAG_EDGE);2158const int numSelectedEdges = getParentEdges().front()->isAttributeCarrierSelected() ? myNet->getAttributeCarriers()->getNumberOfSelectedEdges() : 0;2159if ((numSelectedEdges > 0) && getParentEdges().front()->isAttributeCarrierSelected() && (numSelectedEdges > 1)) {2160edgeDescPossibleMulti = toString(numSelectedEdges) + " " + toString(SUMO_TAG_EDGE) + "s";2161}2162// create menu pane for edge operations2163FXMenuPane* edgeOperations = new FXMenuPane(ret);2164ret->insertMenuPaneChild(edgeOperations);2165if (numSelectedEdges > 0) {2166new FXMenuCascade(ret, TLF("Template operations (% selected)", toString(numSelectedEdges)).c_str(), nullptr, edgeOperations);2167} else {2168new FXMenuCascade(ret, TL("Template operations"), nullptr, edgeOperations);2169}2170// create menu commands for all edge operations2171GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Use edge as template"), nullptr, &parent, MID_GNE_EDGE_USEASTEMPLATE);2172auto applyTemplate = GUIDesigns::buildFXMenuCommand(edgeOperations, TL("Apply template"), nullptr, &parent, MID_GNE_EDGE_APPLYTEMPLATE);2173// check if disable apply template2174if (myNet->getViewParent()->getInspectorFrame()->getTemplateEditor()->getEdgeTemplate() == nullptr) {2175applyTemplate->disable();2176}2177}217821792180void2181GNELane::buildRechableOperations(GUISUMOAbstractView& parent, GUIGLObjectPopupMenu* ret) {2182// addreachability menu2183FXMenuPane* reachableByClass = new FXMenuPane(ret);2184ret->insertMenuPaneChild(reachableByClass);2185if (myNet->isNetRecomputed()) {2186new FXMenuCascade(ret, TL("Select reachable"), GUIIconSubSys::getIcon(GUIIcon::MODEVEHICLE), reachableByClass);2187for (const auto& vClass : SumoVehicleClassStrings.getStrings()) {2188GUIDesigns::buildFXMenuCommand(reachableByClass, vClass.c_str(), VClassIcons::getVClassIcon(SumoVehicleClassStrings.get(vClass)), &parent, MID_REACHABILITY);2189}2190} else {2191FXMenuCommand* menuCommand = GUIDesigns::buildFXMenuCommand(ret, TL("Select reachable (compute junctions)"), nullptr, nullptr, 0);2192menuCommand->handle(&parent, FXSEL(SEL_COMMAND, FXWindow::ID_DISABLE), nullptr);2193}2194}21952196/****************************************************************************/219721982199