Path: blob/main/src/netedit/elements/additional/GNEAccess.cpp
169684 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 GNEAccess.cpp14/// @author Pablo Alvarez Lopez15/// @date Jun 201816///17//18/****************************************************************************/19#include <config.h>2021#include <netedit/GNENet.h>22#include <netedit/GNETagProperties.h>23#include <netedit/GNEUndoList.h>24#include <netedit/GNEViewNet.h>25#include <netedit/GNEViewParent.h>26#include <netedit/changes/GNEChange_Attribute.h>27#include <netedit/frames/common/GNEMoveFrame.h>28#include <utils/gui/div/GLHelper.h>29#include <utils/gui/globjects/GLIncludes.h>30#include <utils/gui/div/GUIGlobalViewObjectsHandler.h>31#include <utils/xml/NamespaceIDs.h>3233#include "GNEAccess.h"34#include "GNEAdditionalHandler.h"3536// ===========================================================================37// member method definitions38// ===========================================================================3940GNEAccess::GNEAccess(GNENet* net) :41GNEAdditional("", net, "", SUMO_TAG_ACCESS, ""),42myPositionOverLane(0),43myLength(0),44myFriendlyPosition(false) {45}464748GNEAccess::GNEAccess(GNEAdditional* busStop, GNELane* lane, const double pos, const std::string& specialPos,49const bool friendlyPos, const double length, const Parameterised::Map& parameters) :50GNEAdditional(busStop, SUMO_TAG_ACCESS, ""),51Parameterised(parameters),52myPositionOverLane(pos),53mySpecialPosition(specialPos),54myLength(length),55myFriendlyPosition(friendlyPos) {56// set parents57setParent<GNELane*>(lane);58setParent<GNEAdditional*>(busStop);59// update centering boundary without updating grid60updateCenteringBoundary(false);61}626364GNEAccess::~GNEAccess() {65}666768GNEMoveOperation*69GNEAccess::getMoveOperation() {70// return move operation for additional placed over shape71return new GNEMoveOperation(this, getParentLanes().front(), myPositionOverLane,72myNet->getViewNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane());73}747576void77GNEAccess::updateGeometry() {78// set start position79double fixedPositionOverLane;80if (myPositionOverLane < 0) {81fixedPositionOverLane = 0;82} else if (myPositionOverLane > getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength()) {83fixedPositionOverLane = getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();84} else {85fixedPositionOverLane = myPositionOverLane;86}87// update geometry88myAdditionalGeometry.updateGeometry(getParentLanes().front()->getLaneShape(), fixedPositionOverLane * getParentLanes().front()->getLengthGeometryFactor(), myMoveElementLateralOffset);89}909192Position93GNEAccess::getPositionInView() const {94return myAdditionalGeometry.getShape().getPolygonCenter();95}969798void99GNEAccess::updateCenteringBoundary(const bool /*updateGrid*/) {100// nothing to update101}102103104void105GNEAccess::splitEdgeGeometry(const double splitPosition, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* newElement, GNEUndoList* undoList) {106if (splitPosition < myPositionOverLane) {107// change lane108setAttribute(SUMO_ATTR_LANE, newElement->getID(), undoList);109// now adjust start position110setAttribute(SUMO_ATTR_POSITION, toString(myPositionOverLane - splitPosition), undoList);111}112}113114115bool116GNEAccess::isAccessPositionFixed() const {117// with friendly position enabled position are "always fixed"118if (myFriendlyPosition) {119return true;120} else {121if (myPositionOverLane != INVALID_DOUBLE) {122return (myPositionOverLane >= 0) && (myPositionOverLane <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength());123} else {124return false;125}126}127}128129130void131GNEAccess::writeAdditional(OutputDevice& device) const {132device.openTag(SUMO_TAG_ACCESS);133device.writeAttr(SUMO_ATTR_LANE, getParentLanes().front()->getID());134device.writeAttr(SUMO_ATTR_POSITION, getAttribute(SUMO_ATTR_POSITION));135if (myLength != -1) {136device.writeAttr(SUMO_ATTR_LENGTH, myLength);137}138if (myFriendlyPosition) {139device.writeAttr(SUMO_ATTR_FRIENDLY_POS, myFriendlyPosition);140}141device.closeTag();142}143144145bool146GNEAccess::isAdditionalValid() const {147// with friendly position enabled position is "always fixed"148if (myFriendlyPosition) {149return true;150} else if (myPositionOverLane == INVALID_DOUBLE) {151return true;152} else {153return fabs(myPositionOverLane) <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();154}155}156157158std::string GNEAccess::getAdditionalProblem() const {159// obtain final length160const double len = getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();161// check if detector has a problem162if (GNEAdditionalHandler::checkLanePosition(myPositionOverLane, 0, len, myFriendlyPosition)) {163return "";164} else {165// declare variable for error position166std::string errorPosition;167// check positions over lane168if (myPositionOverLane < 0) {169errorPosition = (toString(SUMO_ATTR_POSITION) + " < 0");170}171if (myPositionOverLane > len) {172errorPosition = (toString(SUMO_ATTR_POSITION) + TL(" > lanes's length"));173}174return errorPosition;175}176}177178179void GNEAccess::fixAdditionalProblem() {180// declare new position181double newPositionOverLane = myPositionOverLane;182// declare new length (but unsed in this context)183double length = 0;184// fix pos and length with fixLanePosition185GNEAdditionalHandler::fixLanePosition(newPositionOverLane, length, getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength());186// set new position187setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myNet->getViewNet()->getUndoList());188}189190191bool192GNEAccess::checkDrawMoveContour() const {193// get edit modes194const auto& editModes = myNet->getViewNet()->getEditModes();195// check if we're in move mode196if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&197!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&198(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {199// only move the first element200return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;201} else {202return false;203}204}205206207GNEEdge*208GNEAccess::getEdge() const {209return getParentLanes().front()->getParentEdge();210}211212213std::string214GNEAccess::getParentName() const {215return getParentAdditionals().at(0)->getID();216}217218219void220GNEAccess::drawGL(const GUIVisualizationSettings& s) const {221// first check if additional has to be drawn222if (myNet->getViewNet()->getDataViewOptions().showAdditionals()) {223// Obtain exaggeration224const double accessExaggeration = getExaggeration(s);225// adjust radius depending of mode and distance to mouse position226double radius = 0.5;227if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&228myNet->getViewNet()->getPositionInformation().distanceSquaredTo2D(myAdditionalGeometry.getShape().front()) < 1) {229radius = 1;230}231// get detail level232const auto d = s.getDetailLevel(1);233// draw geometry only if we'rent in drawForObjectUnderCursor mode234if (s.checkDrawAdditional(d, isAttributeCarrierSelected())) {235// get color236RGBColor accessColor;237if (drawUsingSelectColor()) {238accessColor = s.colorSettings.selectedAdditionalColor;239} else if (!getParentAdditionals().front()->getAttribute(SUMO_ATTR_COLOR).empty()) {240accessColor = parse<RGBColor>(getParentAdditionals().front()->getAttribute(SUMO_ATTR_COLOR));241} else if (getParentAdditionals().front()->getTagProperty()->getTag() == SUMO_TAG_CONTAINER_STOP) {242accessColor = s.colorSettings.containerStopColor;243} else {244accessColor = s.colorSettings.busStopColor;245}246// draw parent and child lines247drawParentChildLines(s, accessColor);248// push layer matrix249GLHelper::pushMatrix();250// translate to front251drawInLayer(GLO_ACCESS);252// set color253GLHelper::setColor(accessColor);254// translate to geometry position255glTranslated(myAdditionalGeometry.getShape().front().x(), myAdditionalGeometry.getShape().front().y(), 0);256// draw circle257GLHelper::drawFilledCircleDetailled(d, radius * accessExaggeration);258// pop layer matrix259GLHelper::popMatrix();260// draw lock icon261GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), myAdditionalGeometry.getShape().front(), accessExaggeration, 0.3);262// draw dotted contour263myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);264}265// calculate contour266myAdditionalContour.calculateContourCircleShape(s, d, this, myAdditionalGeometry.getShape().front(), radius, getType(),267accessExaggeration, getParentLanes().front()->getParentEdge());268}269}270271272std::string273GNEAccess::getAttribute(SumoXMLAttr key) const {274switch (key) {275case SUMO_ATTR_ID:276return getParentAdditionals().front()->getID();277case SUMO_ATTR_LANE:278return getParentLanes().front()->getID();279case SUMO_ATTR_POSITION:280if (myPositionOverLane == INVALID_DOUBLE) {281return mySpecialPosition;282} else {283return toString(myPositionOverLane);284}285case SUMO_ATTR_LENGTH:286if (myLength == -1) {287return "";288} else {289return toString(myLength);290}291case SUMO_ATTR_FRIENDLY_POS:292return toString(myFriendlyPosition);293case GNE_ATTR_PARENT:294if (isTemplate()) {295return "";296} else {297return getParentAdditionals().at(0)->getID();298}299case GNE_ATTR_SHIFTLANEINDEX:300return "";301default:302return getCommonAttribute(this, key);303}304}305306307double308GNEAccess::getAttributeDouble(SumoXMLAttr key) const {309throw InvalidArgument(getTagStr() + " doesn't have a double attribute of type '" + toString(key) + "'");310}311312313const Parameterised::Map&314GNEAccess::getACParametersMap() const {315return getParametersMap();316}317318319void320GNEAccess::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {321switch (key) {322case SUMO_ATTR_LANE:323case SUMO_ATTR_POSITION:324case SUMO_ATTR_LENGTH:325case SUMO_ATTR_FRIENDLY_POS:326case GNE_ATTR_PARENT:327case GNE_ATTR_SHIFTLANEINDEX:328GNEChange_Attribute::changeAttribute(this, key, value, undoList);329break;330default:331setCommonAttribute(key, value, undoList);332break;333}334}335336337bool338GNEAccess::isValid(SumoXMLAttr key, const std::string& value) {339switch (key) {340case SUMO_ATTR_LANE: {341GNELane* lane = myNet->getAttributeCarriers()->retrieveLane(value, false);342if (lane != nullptr) {343if (getParentLanes().front()->getParentEdge()->getID() != lane->getParentEdge()->getID()) {344return GNEAdditionalHandler::accessCanBeCreated(getParentAdditionals().at(0), lane->getParentEdge());345} else {346return true;347}348} else {349return false;350}351}352case SUMO_ATTR_POSITION:353if (value.empty() || value == "random" || value == "doors" || value == "carriage") {354return true;355} else {356return canParse<double>(value);357}358case SUMO_ATTR_LENGTH:359if (canParse<double>(value)) {360const double valueDouble = parse<double>(value);361return (valueDouble == -1) || (valueDouble >= 0);362} else {363return false;364}365case SUMO_ATTR_FRIENDLY_POS:366return canParse<bool>(value);367case GNE_ATTR_PARENT:368return (myNet->getAttributeCarriers()->retrieveAdditionals(NamespaceIDs::busStops, value, false) != nullptr);369default:370return isCommonValid(key, value);371}372}373374375std::string376GNEAccess::getPopUpID() const {377return getTagStr();378}379380381std::string382GNEAccess::getHierarchyName() const {383return getTagStr() + ": " + getParentLanes().front()->getParentEdge()->getID();384}385386// ===========================================================================387// private388// ===========================================================================389390void391GNEAccess::setAttribute(SumoXMLAttr key, const std::string& value) {392switch (key) {393case SUMO_ATTR_LANE:394replaceAdditionalParentLanes(value);395break;396case SUMO_ATTR_POSITION:397if (value.empty()) {398myPositionOverLane = 0;399} else if (value == "random" || value == "doors" || value == "carriage") {400myPositionOverLane = INVALID_DOUBLE;401mySpecialPosition = value;402} else {403myPositionOverLane = parse<double>(value);404}405break;406case SUMO_ATTR_LENGTH:407if (value.empty()) {408myLength = myTagProperty->getDefaultDoubleValue(key);409} else {410myLength = parse<double>(value);411}412break;413case SUMO_ATTR_FRIENDLY_POS:414myFriendlyPosition = parse<bool>(value);415break;416case GNE_ATTR_PARENT:417if (myNet->getAttributeCarriers()->retrieveAdditional(SUMO_TAG_BUS_STOP, value, false) != nullptr) {418replaceAdditionalParent(SUMO_TAG_BUS_STOP, value, 0);419} else if (myNet->getAttributeCarriers()->retrieveAdditional(SUMO_TAG_TRAIN_STOP, value, false) != nullptr) {420replaceAdditionalParent(SUMO_TAG_TRAIN_STOP, value, 0);421} else {422replaceAdditionalParent(SUMO_TAG_CONTAINER_STOP, value, 0);423}424break;425case GNE_ATTR_SHIFTLANEINDEX:426shiftLaneIndex();427break;428default:429setCommonAttribute(this, key, value);430break;431}432}433434435void436GNEAccess::setMoveShape(const GNEMoveResult& moveResult) {437// change both position438myPositionOverLane = moveResult.newFirstPos;439// set lateral offset440myMoveElementLateralOffset = moveResult.firstLaneOffset;441// update geometry442updateGeometry();443}444445446void447GNEAccess::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {448// reset lateral offset449myMoveElementLateralOffset = 0;450undoList->begin(this, "position of " + getTagStr());451// now adjust start position452setAttribute(SUMO_ATTR_POSITION, toString(moveResult.newFirstPos), undoList);453// check if lane has to be changed454if (moveResult.newFirstLane) {455// set new lane456setAttribute(SUMO_ATTR_LANE, moveResult.newFirstLane->getID(), undoList);457}458// end change attribute459undoList->end();460}461462463/****************************************************************************/464465466