Path: blob/main/src/netedit/elements/moving/GNEMoveElementLaneSingle.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 GNEMoveElementLaneSingle.cpp14/// @author Pablo Alvarez Lopez15/// @date Sep 202516///17// Class used for elements that can be moved over a lane with only one position18/****************************************************************************/19#include <config.h>2021#include <netedit/changes/GNEChange_Attribute.h>22#include <netedit/frames/common/GNEMoveFrame.h>23#include <netedit/GNENet.h>24#include <netedit/GNEUndoList.h>25#include <netedit/GNEViewParent.h>2627#include "GNEMoveElementLaneSingle.h"2829// ===========================================================================30// static members31// ===========================================================================3233const std::string GNEMoveElementLaneSingle::PositionType::SINGLE = "single";34const std::string GNEMoveElementLaneSingle::PositionType::STARPOS = TL("lane start");35const std::string GNEMoveElementLaneSingle::PositionType::ENDPOS = TL("lane end");3637// ===========================================================================38// Method definitions39// ===========================================================================4041GNEMoveElementLaneSingle::GNEMoveElementLaneSingle(GNEAttributeCarrier* element,42SumoXMLAttr posAttr, double& position, bool& friendlyPos, const std::string& defaultBehavior) :43GNEMoveElement(element),44myPosAttr(posAttr),45myPosOverLane(position),46myFriendlyPos(friendlyPos),47myPositionType(defaultBehavior) {48}495051GNEMoveElementLaneSingle::~GNEMoveElementLaneSingle() {}525354GNEMoveOperation*55GNEMoveElementLaneSingle::getMoveOperation() {56// check if allow change lane is enabled57const bool allowChangeLane = myMovedElement->getNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane();58// continue depending if we're moving the start or the end position59if (myPositionType == PositionType::ENDPOS) {60return new GNEMoveOperation(this, myMovedElement->getHierarchicalElement()->getParentLanes().front(), INVALID_DOUBLE,61myMovedElement->getHierarchicalElement()->getParentLanes().back(), myPosOverLane, false, allowChangeLane);62} else {63return new GNEMoveOperation(this, myMovedElement->getHierarchicalElement()->getParentLanes().front(), myPosOverLane,64myMovedElement->getHierarchicalElement()->getParentLanes().back(), INVALID_DOUBLE, true, allowChangeLane);65}66}676869std::string70GNEMoveElementLaneSingle::getMovingAttribute(SumoXMLAttr key) const {71if (key == myPosAttr) {72if ((myPosOverLane == INVALID_DOUBLE) && (myPositionType != PositionType::SINGLE)) {73return myPositionType;74} else {75return toString(myPosOverLane);76}77} else {78switch (key) {79case SUMO_ATTR_LANE:80return myMovedElement->getHierarchicalElement()->getParentLanes().front()->getID();81case SUMO_ATTR_FRIENDLY_POS:82return toString(myFriendlyPos);83default:84return myMovedElement->getCommonAttribute(key);85}86}87}888990double91GNEMoveElementLaneSingle::getMovingAttributeDouble(SumoXMLAttr key) const {92if (key == myPosAttr) {93return myPosOverLane;94} else {95return myMovedElement->getCommonAttributeDouble(key);96}97}9899100Position101GNEMoveElementLaneSingle::getMovingAttributePosition(SumoXMLAttr key) const {102return myMovedElement->getCommonAttributePosition(key);103}104105106PositionVector107GNEMoveElementLaneSingle::getMovingAttributePositionVector(SumoXMLAttr key) const {108return myMovedElement->getCommonAttributePositionVector(key);109}110111112void113GNEMoveElementLaneSingle::setMovingAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {114if (key == myPosAttr) {115GNEChange_Attribute::changeAttribute(myMovedElement, key, value, undoList);116} else {117switch (key) {118case SUMO_ATTR_LANE:119case SUMO_ATTR_FRIENDLY_POS:120GNEChange_Attribute::changeAttribute(myMovedElement, key, value, undoList);121break;122default:123myMovedElement->setCommonAttribute(key, value, undoList);124break;125}126}127}128129130bool131GNEMoveElementLaneSingle::isMovingAttributeValid(SumoXMLAttr key, const std::string& value) const {132if (key == myPosAttr) {133if ((myPositionType != PositionType::SINGLE) && (value.empty() || (value == myPositionType))) {134return true;135} else {136return GNEAttributeCarrier::canParse<double>(value);137}138} else {139switch (key) {140case SUMO_ATTR_LANE:141if (myMovedElement->getNet()->getAttributeCarriers()->retrieveLane(value, false) != nullptr) {142return true;143} else {144return false;145}146case SUMO_ATTR_FRIENDLY_POS:147return GNEAttributeCarrier::canParse<bool>(value);148default:149return myMovedElement->isCommonAttributeValid(key, value);150}151}152}153154155void156GNEMoveElementLaneSingle::setMovingAttribute(SumoXMLAttr key, const std::string& value) {157if (key == myPosAttr) {158if (value.empty()) {159myPosOverLane = INVALID_DOUBLE;160} else if ((value == PositionType::STARPOS) && (myPositionType == PositionType::STARPOS)) {161myPosOverLane = INVALID_DOUBLE;162} else if ((value == PositionType::ENDPOS) && (myPositionType == PositionType::ENDPOS)) {163myPosOverLane = INVALID_DOUBLE;164} else {165myPosOverLane = GNEAttributeCarrier::parse<double>(value);166}167} else {168switch (key) {169case SUMO_ATTR_FRIENDLY_POS:170myFriendlyPos = GNEAttributeCarrier::parse<bool>(value);171break;172default:173myMovedElement->setCommonAttribute(key, value);174break;175}176}177}178179180void181GNEMoveElementLaneSingle::removeGeometryPoint(const Position /*clickedPosition*/, GNEUndoList* /*undoList*/) {182// nothing to do here183}184185186bool187GNEMoveElementLaneSingle::isMoveElementValid() const {188// obtain lane final length189const double laneLenght = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength() :190myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();191// adjust position (negative means start counting from backward)192const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;193// check conditions194if (myFriendlyPos) {195return true;196} else if (adjustedPosition < 0) {197return false;198} else if (adjustedPosition > laneLenght) {199return false;200} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {201return false;202} else {203return true;204}205}206207208std::string209GNEMoveElementLaneSingle::getMovingProblem() const {210// obtain lane final length211const double laneLenght = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back()->getParentEdge()->getNBEdge()->getFinalLength() :212myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();213// adjust position (negative means start counting from backward)214const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;215// check conditions216if (myFriendlyPos) {217return "";218} else if (adjustedPosition < 0) {219return TLF("% < 0", toString(myPosAttr));220} else if (adjustedPosition > laneLenght) {221return TLF("% > length of lane", toString(myPosAttr));222} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {223return TLF("% > (length of lane - EPS)", toString(myPosAttr));224} else {225return "";226}227}228229230void231GNEMoveElementLaneSingle::fixMovingProblem() {232// obtain lane final length233const double laneLenght = myMovedElement->getHierarchicalElement()->getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();234// adjust position (negative means start counting from backward)235const double adjustedPosition = (myPosOverLane == INVALID_DOUBLE) ? 0 : (myPosOverLane < 0) ? (myPosOverLane + laneLenght) : myPosOverLane;236// check conditions237if (adjustedPosition < 0) {238myMovedElement->setAttribute(myPosAttr, "0", myMovedElement->getNet()->getUndoList());239} else if (adjustedPosition > laneLenght) {240myMovedElement->setAttribute(myPosAttr, toString(laneLenght), myMovedElement->getNet()->getUndoList());241} else if ((myPositionType == PositionType::STARPOS) && (adjustedPosition > (laneLenght - POSITION_EPS))) {242myMovedElement->setAttribute(myPosAttr, toString(laneLenght - POSITION_EPS), myMovedElement->getNet()->getUndoList());243}244}245246247void248GNEMoveElementLaneSingle::writeMoveAttributes(OutputDevice& device) const {249// lane250device.writeAttr(SUMO_ATTR_LANE, myMovedElement->getAttribute(SUMO_ATTR_LANE));251// position (don't write if is an invalid double, except in no default)252if ((myPositionType == PositionType::SINGLE) || (myPosOverLane != INVALID_DOUBLE)) {253device.writeAttr(myPosAttr, myPosOverLane);254}255// friendly position (only if true)256if (myFriendlyPos) {257device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myFriendlyPos);258}259}260261262double263GNEMoveElementLaneSingle::getFixedPositionOverLane(const bool adjustGeometryFactor) const {264// get lane depending of type265const auto& lane = (myPositionType == PositionType::ENDPOS) ? myMovedElement->getHierarchicalElement()->getParentLanes().back() : myMovedElement->getHierarchicalElement()->getParentLanes().front();266const double laneLength = lane->getParentEdge()->getNBEdge()->getFinalLength();267// continue depending if we defined a end position268if (myPosOverLane == INVALID_DOUBLE) {269if (myPositionType == PositionType::ENDPOS) {270return adjustGeometryFactor ? (laneLength * lane->getLengthGeometryFactor()) : laneLength;271} else {272return 0;273}274} else {275// fix position276double fixedPos = myPosOverLane;277// adjust fixedPos278if (fixedPos < 0) {279fixedPos += laneLength;280}281// set length geometry factor282if (adjustGeometryFactor) {283// adjust geometry factor284fixedPos *= lane->getLengthGeometryFactor();285// return depending of fixedPos286if (fixedPos < 0) {287return 0;288} else if (fixedPos > lane->getLaneShapeLength()) {289return lane->getLaneShapeLength();290} else {291return fixedPos;292}293} else {294// return depending of fixedPos295if (fixedPos < 0) {296return 0;297} else if (fixedPos > laneLength) {298return laneLength;299} else {300return fixedPos;301}302}303}304}305306307void308GNEMoveElementLaneSingle::setMoveShape(const GNEMoveResult& moveResult) {309if (myPositionType == PositionType::ENDPOS) {310// change position311myPosOverLane = moveResult.newLastPos;312// set lateral offset313myMovingLateralOffset = moveResult.lastLaneOffset;314} else {315// change position316myPosOverLane = moveResult.newFirstPos;317// set lateral offset318myMovingLateralOffset = moveResult.firstLaneOffset;319}320// update geometry321myMovedElement->updateGeometry();322}323324325void326GNEMoveElementLaneSingle::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {327// reset lateral offset328myMovingLateralOffset = 0;329// begin change attribute330undoList->begin(myMovedElement, TLF("position of %", myMovedElement->getTagStr()));331// set position332if (myPositionType == PositionType::ENDPOS) {333myMovedElement->setAttribute(myPosAttr, toString(moveResult.newLastPos), undoList);334// check if lane has to be changed335if (moveResult.newLastLane && (moveResult.newLastLane != myMovedElement->getHierarchicalElement()->getParentLanes().back())) {336// set new lane337myMovedElement->setAttribute(SUMO_ATTR_LANE, moveResult.newLastLane->getID(), undoList);338}339} else {340myMovedElement->setAttribute(myPosAttr, toString(moveResult.newFirstPos), undoList);341// check if lane has to be changed342if (moveResult.newFirstLane && (moveResult.newFirstLane != myMovedElement->getHierarchicalElement()->getParentLanes().front())) {343// set new lane344myMovedElement->setAttribute(SUMO_ATTR_LANE, moveResult.newFirstLane->getID(), undoList);345}346}347// end change attribute348undoList->end();349}350351/****************************************************************************/352353354