Path: blob/main/src/netedit/elements/additional/GNEPOI.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 GNEPOI.cpp14/// @author Pablo Alvarez Lopez15/// @date Jun 201716///17// A class for visualizing and editing POIS in netedit (adapted from18// GUIPointOfInterest and NLHandler)19/****************************************************************************/20#include <config.h>2122#include <netedit/GNENet.h>23#include <netedit/GNETagProperties.h>24#include <netedit/GNEUndoList.h>25#include <netedit/GNEViewNet.h>26#include <netedit/GNEViewParent.h>27#include <netedit/changes/GNEChange_Attribute.h>28#include <netedit/frames/common/GNEMoveFrame.h>29#include <utils/gui/div/GLHelper.h>30#include <utils/gui/div/GUIDesigns.h>31#include <utils/gui/div/GUIParameterTableWindow.h>32#include <utils/gui/globjects/GLIncludes.h>33#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>34#include <utils/gui/globjects/GUIPointOfInterest.h>35#include <utils/gui/div/GUIGlobalViewObjectsHandler.h>36#include <utils/options/OptionsCont.h>37#include <utils/xml/NamespaceIDs.h>3839#include "GNEPOI.h"40#include "GNEAdditionalHandler.h"4142// ===========================================================================43// method definitions44// ===========================================================================4546GNEPOI::GNEPOI(SumoXMLTag tag, GNENet* net) :47PointOfInterest("", "", RGBColor::BLACK, Position(0, 0), false, "", 0, false, 0, SUMOXMLDefinitions::POIIcons.getString(POIIcon::NONE),480, 0, "", 0, 0, "", Parameterised::Map()),49GNEAdditional("", net, "", tag, "") {50}515253GNEPOI::GNEPOI(const std::string& id, GNENet* net, const std::string& filename, const std::string& type, const RGBColor& color, const double xLon, const double yLat,54const bool geo, const std::string& icon, const double layer, const double angle, const std::string& imgFile, const double width, const double height,55const std::string& name, const Parameterised::Map& parameters) :56PointOfInterest(id, type, color, Position(xLon, yLat), geo, "", 0, false, 0, icon, layer, angle, imgFile, width, height, name, parameters),57GNEAdditional(id, net, filename, geo ? GNE_TAG_POIGEO : SUMO_TAG_POI, "") {58// update position depending of GEO59if (geo) {60Position cartesian(x(), y());61GeoConvHelper::getFinal().x2cartesian_const(cartesian);62set(cartesian.x(), cartesian.y());63}64// update geometry (needed for adjust myShapeWidth and myShapeHeight)65updateGeometry();66// update centering boundary without updating grid67updateCenteringBoundary(false);68}697071GNEPOI::GNEPOI(const std::string& id, GNENet* net, const std::string& filename, const std::string& type, const RGBColor& color, GNELane* lane, const double posOverLane,72const bool friendlyPos, const double posLat, const std::string& icon, const double layer, const double angle, const std::string& imgFile, const double width,73const double height, const std::string& name, const Parameterised::Map& parameters) :74PointOfInterest(id, type, color, Position(), false, lane->getID(), posOverLane, friendlyPos, posLat, icon, layer, angle, imgFile, width, height, name, parameters),75GNEAdditional(id, net, filename, GNE_TAG_POILANE, "") {76// set parents77setParent<GNELane*>(lane);78// update geometry (needed for adjust myShapeWidth and myShapeHeight)79updateGeometry();80// update centering boundary without updating grid81updateCenteringBoundary(false);82}838485GNEPOI::~GNEPOI() {}868788GNEMoveOperation*89GNEPOI::getMoveOperation() {90if (myNet->getViewNet()->getEditModes().isCurrentSupermodeNetwork() &&91(myNet->getViewNet()->getEditModes().networkEditMode == NetworkEditMode::NETWORK_MOVE) &&92myNet->getViewNet()->getMouseButtonKeyPressed().shiftKeyPressed()) {93// get snap radius94const double snapRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;95const double snapRadiusSquared = snapRadius * snapRadius;96// get mouse position97const Position mousePosition = myNet->getViewNet()->getPositionInformation();98// check if we're editing width or height99if ((myShapeWidth.size() == 0) || (myShapeHeight.size() == 0)) {100return nullptr;101} else if (myShapeHeight.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) {102// edit height103return new GNEMoveOperation(this, myShapeHeight, true, GNEMoveOperation::OperationType::HEIGHT);104} else if (myShapeHeight.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) {105// edit height106return new GNEMoveOperation(this, myShapeHeight, false, GNEMoveOperation::OperationType::HEIGHT);107} else if (myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) {108// edit width109return new GNEMoveOperation(this, myShapeWidth, true, GNEMoveOperation::OperationType::WIDTH);110} else if (myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) {111// edit width112return new GNEMoveOperation(this, myShapeWidth, false, GNEMoveOperation::OperationType::WIDTH);113} else {114return nullptr;115}116} else if (getTagProperty()->getTag() == GNE_TAG_POILANE) {117// return move operation for POI placed over lane118return new GNEMoveOperation(this, getParentLanes().front(), myPosOverLane,119myNet->getViewNet()->getViewParent()->getMoveFrame()->getCommonMoveOptions()->getAllowChangeLane());120} else {121// return move operation for a position in view122return new GNEMoveOperation(this, *this);123}124}125126127void128GNEPOI::removeGeometryPoint(const Position /*clickedPosition*/, GNEUndoList* /*undoList*/) {129// nothing to remove130}131132133std::string134GNEPOI::generateChildID(SumoXMLTag /*childTag*/) {135return "";136}137138139CommonXMLStructure::SumoBaseObject*140GNEPOI::getSumoBaseObject() const {141CommonXMLStructure::SumoBaseObject* POIBaseObject = new CommonXMLStructure::SumoBaseObject(nullptr);142POIBaseObject->setTag(SUMO_TAG_POI);143// fill attributes144POIBaseObject->addStringAttribute(SUMO_ATTR_ID, myID);145POIBaseObject->addColorAttribute(SUMO_ATTR_COLOR, getShapeColor());146POIBaseObject->addStringAttribute(SUMO_ATTR_TYPE, getShapeType());147POIBaseObject->addStringAttribute(SUMO_ATTR_ICON, getIconStr());148POIBaseObject->addDoubleAttribute(SUMO_ATTR_LAYER, getShapeLayer());149POIBaseObject->addStringAttribute(SUMO_ATTR_IMGFILE, getShapeImgFile());150POIBaseObject->addDoubleAttribute(SUMO_ATTR_WIDTH, getWidth());151POIBaseObject->addDoubleAttribute(SUMO_ATTR_HEIGHT, getHeight());152POIBaseObject->addDoubleAttribute(SUMO_ATTR_ANGLE, getShapeNaviDegree());153POIBaseObject->addStringAttribute(SUMO_ATTR_NAME, getShapeName());154return POIBaseObject;155}156157158void159GNEPOI::writeAdditional(OutputDevice& device) const {160if (getParentLanes().size() > 0) {161// obtain fixed position over lane162double fixedPositionOverLane = myPosOverLane > getParentLanes().at(0)->getLaneShape().length() ? getParentLanes().at(0)->getLaneShape().length() : myPosOverLane < 0 ? 0 : myPosOverLane;163// write POILane using POI::writeXML164writeXML(device, false, 0, getParentLanes().at(0)->getID(), fixedPositionOverLane, myFriendlyPos, myPosLat);165} else {166writeXML(device, myGeo);167}168}169170171bool172GNEPOI::isAdditionalValid() const {173// only for POIS over lanes174if (getParentLanes().size() == 0) {175return true;176} else if (getFriendlyPos()) {177// with friendly position enabled position is "always fixed"178return true;179} else {180return fabs(myPosOverLane) <= getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();181}182}183184185std::string186GNEPOI::getAdditionalProblem() const {187// only for POIS over lanes188if (getParentLanes().size() > 0) {189// obtain final length190const double len = getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength();191// check if detector has a problem192if (GNEAdditionalHandler::checkLanePosition(myPosOverLane, 0, len, getFriendlyPos())) {193return "";194} else {195// declare variable for error position196std::string errorPosition;197// check positions over lane198if (myPosOverLane < 0) {199errorPosition = (toString(SUMO_ATTR_POSITION) + " < 0");200}201if (myPosOverLane > len) {202errorPosition = (toString(SUMO_ATTR_POSITION) + TL(" > lanes's length"));203}204return errorPosition;205}206} else {207return "";208}209}210211212void213GNEPOI::fixAdditionalProblem() {214// only for POIS over lanes215if (getParentLanes().size() > 0) {216// declare new position217double newPositionOverLane = myPosOverLane;218// declare new length (but unsed in this context)219double length = 0;220// fix pos and length with fixLanePosition221GNEAdditionalHandler::fixLanePosition(newPositionOverLane, length, getParentLanes().front()->getParentEdge()->getNBEdge()->getFinalLength());222// set new position223setAttribute(SUMO_ATTR_POSITION, toString(newPositionOverLane), myNet->getViewNet()->getUndoList());224}225}226227228void229GNEPOI::updateGeometry() {230// set position231if (getParentLanes().size() > 0) {232// obtain fixed position over lane233double fixedPositionOverLane = myPosOverLane > getParentLanes().at(0)->getLaneShapeLength() ? getParentLanes().at(0)->getLaneShapeLength() : myPosOverLane < 0 ? 0 : myPosOverLane;234// set new position regarding to lane235set(getParentLanes().at(0)->getLaneShape().positionAtOffset(fixedPositionOverLane * getParentLanes().at(0)->getLengthGeometryFactor(), -myPosLat));236}237// check if update width and height shapes238if ((getWidth() > 0) && (getHeight() > 0)) {239// calculate shape length240myShapeHeight.clear();241myShapeHeight.push_back(Position(0, getHeight() * -0.5));242myShapeHeight.push_back(Position(0, getHeight() * 0.5));243// move244myShapeHeight.add(*this);245// calculate shape width246PositionVector leftShape = myShapeHeight;247leftShape.move2side(getWidth() * -0.5);248PositionVector rightShape = myShapeHeight;249rightShape.move2side(getWidth() * 0.5);250myShapeWidth = {leftShape.getCentroid(), rightShape.getCentroid()};251}252}253254255Position256GNEPOI::getPositionInView() const {257return *this;258}259260261double262GNEPOI::getExaggeration(const GUIVisualizationSettings& s) const {263return s.poiSize.getExaggeration(s, this);264}265266267void268GNEPOI::updateCenteringBoundary(const bool updateGrid) {269// Remove object from net270if (updateGrid) {271myNet->removeGLObjectFromGrid(this);272}273// reset boundary274myAdditionalBoundary.reset();275// add position (this POI)276myAdditionalBoundary.add(*this);277// grow boundary278myAdditionalBoundary.grow(5 + std::max(getWidth() * 0.5, getHeight() * 0.5));279// add object into net280if (updateGrid) {281myNet->addGLObjectIntoGrid(this);282}283}284285286void287GNEPOI::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {288// nothing to split289}290291292GUIGlID293GNEPOI::getGlID() const {294return GUIGlObject::getGlID();295}296297298bool299GNEPOI::checkDrawMoveContour() const {300// get edit modes301const auto& editModes = myNet->getViewNet()->getEditModes();302// check if we're in move mode303if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&304!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&305(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {306// only move the first element307return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;308} else {309return false;310}311}312313314std::string315GNEPOI::getParentName() const {316if (getParentLanes().size() > 0) {317return getParentLanes().front()->getID();318} else {319return myNet->getMicrosimID();320}321}322323324GUIGLObjectPopupMenu*325GNEPOI::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {326// create popup327GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);328// build common options329buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);330// specific of non juPedSim polygons331if (!myTagProperty->isJuPedSimElement()) {332// continue depending of lane number333if (getParentLanes().size() > 0) {334// add option for convert to GNEPOI335GUIDesigns::buildFXMenuCommand(ret, TL("Release from lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_TRANSFORM);336return ret;337} else {338// add option for convert to GNEPOI339GUIDesigns::buildFXMenuCommand(ret, TL("Attach to nearest lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_TRANSFORM);340}341}342return ret;343}344345346void347GNEPOI::drawGL(const GUIVisualizationSettings& s) const {348// first check if POI can be drawn349if (myNet->getViewNet()->getDemandViewOptions().showShapes() &&350myNet->getViewNet()->getDataViewOptions().showShapes()) {351// draw boundaries352GLHelper::drawBoundary(s, getCenteringBoundary());353// obtain POIExaggeration354const double POIExaggeration = getExaggeration(s);355// get detail level356const auto d = s.getDetailLevel(POIExaggeration);357// check if draw moving geometry points (only if we have a defined image358const bool movingGeometryPoints = getShapeImgFile().empty() ? false : drawMovingGeometryPoints(false);359// draw geometry only if we'rent in drawForObjectUnderCursor mode360if (s.checkDrawPOI(getWidth(), getHeight(), d, isAttributeCarrierSelected())) {361// draw POI362drawPOI(s, d, movingGeometryPoints);363// draw lock icon364GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), POIExaggeration);365// draw dotted contours366if (movingGeometryPoints) {367// get snap radius368const double snapRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;369const double snapRadiusSquared = snapRadius * snapRadius;370// get mouse position371const Position mousePosition = myNet->getViewNet()->getPositionInformation();372// check if we're editing width or height373if ((myShapeHeight.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||374(myShapeHeight.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {375myMovingContourUp.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);376myMovingContourDown.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);377} else if ((myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||378(myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {379myMovingContourLeft.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);380myMovingContourRight.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);381}382} else {383myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);384}385}386// calculate contour387calculatePOIContour(s, d, POIExaggeration, movingGeometryPoints);388}389}390391392std::string393GNEPOI::getAttribute(SumoXMLAttr key) const {394switch (key) {395case SUMO_ATTR_ID:396return myID;397case SUMO_ATTR_COLOR:398return toString(getShapeColor());399case SUMO_ATTR_LANE:400return myLane;401case SUMO_ATTR_POSITION:402if (getParentLanes().size() > 0) {403return toString(myPosOverLane);404} else {405return toString(*this);406}407case SUMO_ATTR_FRIENDLY_POS:408return toString(getFriendlyPos());409case SUMO_ATTR_POSITION_LAT:410return toString(myPosLat);411case SUMO_ATTR_LON:412if (GeoConvHelper::getFinal().getProjString() != "!") {413// calculate geo position414Position GEOPosition(x(), y());415GeoConvHelper::getFinal().cartesian2geo(GEOPosition);416// return lon417return toString(GEOPosition.x(), 8);418} else {419return TL("No geo-conversion defined");420}421case SUMO_ATTR_LAT:422if (GeoConvHelper::getFinal().getProjString() != "!") {423// calculate geo position424Position GEOPosition(x(), y());425GeoConvHelper::getFinal().cartesian2geo(GEOPosition);426// return lat427return toString(GEOPosition.y(), 8);428} else {429return TL("No geo-conversion defined");430}431case SUMO_ATTR_TYPE:432return getShapeType();433case SUMO_ATTR_ICON:434return getIconStr();435case SUMO_ATTR_LAYER:436return toString(getShapeLayer());437case SUMO_ATTR_IMGFILE:438return getShapeImgFile();439case SUMO_ATTR_WIDTH:440return toString(getWidth());441case SUMO_ATTR_HEIGHT:442return toString(getHeight());443case SUMO_ATTR_ANGLE:444return toString(getShapeNaviDegree());445case SUMO_ATTR_NAME:446return getShapeName();447case GNE_ATTR_SHIFTLANEINDEX:448return "";449default:450return getCommonAttribute(this, key);451}452}453454455double456GNEPOI::getAttributeDouble(SumoXMLAttr key) const {457throw InvalidArgument(getTagStr() + " attribute '" + toString(key) + "' not allowed");458}459460461const Parameterised::Map&462GNEPOI::getACParametersMap() const {463return PointOfInterest::getParametersMap();464}465466467void468GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {469switch (key) {470case SUMO_ATTR_ID:471case SUMO_ATTR_COLOR:472case SUMO_ATTR_LANE:473case SUMO_ATTR_POSITION:474case SUMO_ATTR_FRIENDLY_POS:475case SUMO_ATTR_POSITION_LAT:476case SUMO_ATTR_LON:477case SUMO_ATTR_LAT:478case SUMO_ATTR_TYPE:479case SUMO_ATTR_ICON:480case SUMO_ATTR_LAYER:481case SUMO_ATTR_IMGFILE:482case SUMO_ATTR_WIDTH:483case SUMO_ATTR_HEIGHT:484case SUMO_ATTR_ANGLE:485case SUMO_ATTR_NAME:486case GNE_ATTR_SHIFTLANEINDEX:487GNEChange_Attribute::changeAttribute(this, key, value, undoList);488break;489default:490setCommonAttribute(key, value, undoList);491break;492}493}494495496bool497GNEPOI::isValid(SumoXMLAttr key, const std::string& value) {498switch (key) {499case SUMO_ATTR_ID:500return isValidAdditionalID(NamespaceIDs::POIs, value);501case SUMO_ATTR_COLOR:502return canParse<RGBColor>(value);503case SUMO_ATTR_LANE:504return (myNet->getAttributeCarriers()->retrieveLane(value, false) != nullptr);505case SUMO_ATTR_POSITION:506if (getParentLanes().size() > 0) {507return canParse<double>(value);508} else {509return canParse<Position>(value);510}511case SUMO_ATTR_FRIENDLY_POS:512return canParse<bool>(value);513case SUMO_ATTR_POSITION_LAT:514return canParse<double>(value);515case SUMO_ATTR_LON:516return canParse<double>(value);517case SUMO_ATTR_LAT:518return canParse<double>(value);519case SUMO_ATTR_TYPE:520return true;521case SUMO_ATTR_ICON:522return SUMOXMLDefinitions::POIIcons.hasString(value);523case SUMO_ATTR_LAYER:524if (value.empty()) {525return true;526} else {527return canParse<double>(value);528}529case SUMO_ATTR_IMGFILE:530if (value == "") {531return true;532} else {533// check that image can be loaded534return GUITexturesHelper::getTextureID(value) != -1;535}536case SUMO_ATTR_WIDTH:537return canParse<double>(value) && (parse<double>(value) > 0);538case SUMO_ATTR_HEIGHT:539return canParse<double>(value) && (parse<double>(value) > 0);540case SUMO_ATTR_ANGLE:541return canParse<double>(value);542case SUMO_ATTR_NAME:543return SUMOXMLDefinitions::isValidAttribute(value);544default:545return isCommonValid(key, value);546}547}548549550bool551GNEPOI::isAttributeEnabled(SumoXMLAttr key) const {552switch (key) {553case SUMO_ATTR_POSITION:554if (myTagProperty->getTag() == GNE_TAG_POIGEO) {555return (GeoConvHelper::getFinal().getProjString() != "!");556} else {557return true;558}559case SUMO_ATTR_LON:560case SUMO_ATTR_LAT:561return (GeoConvHelper::getFinal().getProjString() != "!");562default:563return true;564}565}566567568std::string569GNEPOI::getPopUpID() const {570return getTagStr() + ": " + getID();571}572573574std::string575GNEPOI::getHierarchyName() const {576return getTagStr();577}578579// ===========================================================================580// private581// ===========================================================================582583void584GNEPOI::drawPOI(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,585const bool movingGeometryPoints) const {586if (GUIPointOfInterest::checkDraw(s, this)) {587// draw inner polygon588if (myDrawInFront) {589GUIPointOfInterest::drawInnerPOI(s, this, this, drawUsingSelectColor(), GLO_FRONTELEMENT,590myShapeWidth.length2D(), myShapeHeight.length2D());591} else {592GUIPointOfInterest::drawInnerPOI(s, this, this, drawUsingSelectColor(), s.poiUseCustomLayer ? s.poiCustomLayer : getShapeLayer(),593myShapeWidth.length2D(), myShapeHeight.length2D());594}595// draw geometry points596if (movingGeometryPoints) {597if (myShapeHeight.size() > 0) {598drawUpGeometryPoint(s, d, myShapeHeight.front(), 180, RGBColor::ORANGE);599drawDownGeometryPoint(s, d, myShapeHeight.back(), 180, RGBColor::ORANGE);600}601if (myShapeWidth.size() > 0) {602drawLeftGeometryPoint(s, d, myShapeWidth.back(), -90, RGBColor::ORANGE);603drawRightGeometryPoint(s, d, myShapeWidth.front(), -90, RGBColor::ORANGE);604}605}606}607}608609610void611GNEPOI::calculatePOIContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,612const double exaggeration, const bool movingGeometryPoints) const {613// check if we're calculating the contour or the moving geometry points614if (movingGeometryPoints) {615myMovingContourUp.calculateContourCircleShape(s, d, this, myShapeHeight.front(), s.neteditSizeSettings.additionalGeometryPointRadius,616getShapeLayer(), exaggeration, nullptr);617myMovingContourDown.calculateContourCircleShape(s, d, this, myShapeHeight.back(), s.neteditSizeSettings.additionalGeometryPointRadius,618getShapeLayer(), exaggeration, nullptr);619myMovingContourLeft.calculateContourCircleShape(s, d, this, myShapeWidth.front(), s.neteditSizeSettings.additionalGeometryPointRadius,620getShapeLayer(), exaggeration, nullptr);621myMovingContourRight.calculateContourCircleShape(s, d, this, myShapeWidth.back(), s.neteditSizeSettings.additionalGeometryPointRadius,622getShapeLayer(), exaggeration, nullptr);623} else {624const auto parentEdgeBoundary = (getParentLanes().size() > 0) ? getParentLanes().front()->getParentEdge() : nullptr;625if (getShapeImgFile().empty()) {626const double radius = getWidth() > getHeight() ? getWidth() : getHeight();627myAdditionalContour.calculateContourCircleShape(s, d, this, *this, radius * 0.5, getShapeLayer(), exaggeration, parentEdgeBoundary);628} else {629myAdditionalContour.calculateContourRectangleShape(s, d, this, *this, getHeight() * 0.5, getWidth() * 0.5, getShapeLayer(), 0, 0, getShapeNaviDegree(), exaggeration, parentEdgeBoundary);630}631}632}633634635void636GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value) {637switch (key) {638case SUMO_ATTR_ID: {639// update microsimID640setAdditionalID(value);641// set named ID642myID = value;643break;644}645case SUMO_ATTR_COLOR:646setShapeColor(parse<RGBColor>(value));647break;648case SUMO_ATTR_LANE:649myLane = value;650replaceAdditionalParentLanes(value);651break;652case SUMO_ATTR_POSITION: {653if (myTagProperty->getTag() == GNE_TAG_POILANE) {654myPosOverLane = parse<double>(value);655} else {656// set position657set(parse<Position>(value));658}659// update centering boundary660updateCenteringBoundary(true);661// update geometry662updateGeometry();663break;664}665case SUMO_ATTR_FRIENDLY_POS:666setFriendlyPos(parse<bool>(value));667break;668case SUMO_ATTR_POSITION_LAT:669myPosLat = parse<double>(value);670// update centering boundary671updateCenteringBoundary(true);672// update geometry673updateGeometry();674break;675case SUMO_ATTR_LON: {676// calculate cartesian677Position cartesian(parse<double>(value), parse<double>(getAttribute(SUMO_ATTR_LAT)));678GeoConvHelper::getFinal().x2cartesian_const(cartesian);679// set cartesian680set(cartesian);681// update centering boundary682updateCenteringBoundary(true);683// update geometry684updateGeometry();685break;686}687case SUMO_ATTR_LAT: {688// calculate cartesian689Position cartesian(parse<double>(getAttribute(SUMO_ATTR_LON)), parse<double>(value));690GeoConvHelper::getFinal().x2cartesian_const(cartesian);691// set cartesian692set(cartesian);693// update centering boundary694updateCenteringBoundary(true);695// update geometry696updateGeometry();697break;698}699case SUMO_ATTR_TYPE:700setShapeType(value);701break;702case SUMO_ATTR_ICON:703setIcon(value);704break;705case SUMO_ATTR_LAYER:706if (value.empty()) {707setShapeLayer(myTagProperty->getDefaultDoubleValue(key));708} else {709setShapeLayer(parse<double>(value));710}711break;712case SUMO_ATTR_IMGFILE:713// first remove object from grid due img file affect to boundary714if (getID().size() > 0) {715myNet->removeGLObjectFromGrid(this);716}717setShapeImgFile(value);718// all textures must be refresh719GUITexturesHelper::clearTextures();720// add object into grid again721if (getID().size() > 0) {722myNet->addGLObjectIntoGrid(this);723}724break;725case SUMO_ATTR_WIDTH:726// set new width727setWidth(parse<double>(value));728// update centering boundary and geometry (except for templates)729if (getID().size() > 0) {730updateCenteringBoundary(true);731updateGeometry();732}733break;734case SUMO_ATTR_HEIGHT:735// set new height736setHeight(parse<double>(value));737// update centering boundary and geometry (except for templates)738if (getID().size() > 0) {739updateCenteringBoundary(true);740updateGeometry();741}742break;743case SUMO_ATTR_ANGLE:744setShapeNaviDegree(parse<double>(value));745break;746case SUMO_ATTR_NAME:747setShapeName(value);748break;749case GNE_ATTR_SHIFTLANEINDEX:750shiftLaneIndex();751break;752default:753return setCommonAttribute(this, key, value);754}755}756757758void759GNEPOI::setMoveShape(const GNEMoveResult& moveResult) {760// check what are being updated761if (moveResult.operationType == GNEMoveOperation::OperationType::HEIGHT) {762myShapeHeight = moveResult.shapeToUpdate;763} else if (moveResult.operationType == GNEMoveOperation::OperationType::WIDTH) {764myShapeWidth = moveResult.shapeToUpdate;765} else {766if (getTagProperty()->getTag() == GNE_TAG_POILANE) {767myPosOverLane = moveResult.newFirstPos;768} else {769set(moveResult.shapeToUpdate.front());770}771// update geometry772updateGeometry();773}774}775776777void778GNEPOI::commitMoveShape(const GNEMoveResult& moveResult, GNEUndoList* undoList) {779// check what are being updated780if (moveResult.operationType == GNEMoveOperation::OperationType::HEIGHT) {781undoList->begin(this, "height of " + getTagStr());782setAttribute(SUMO_ATTR_HEIGHT, toString(moveResult.shapeToUpdate.length2D()), undoList);783undoList->end();784} else if (moveResult.operationType == GNEMoveOperation::OperationType::WIDTH) {785undoList->begin(this, "width of " + getTagStr());786setAttribute(SUMO_ATTR_WIDTH, toString(moveResult.shapeToUpdate.length2D()), undoList);787undoList->end();788} else {789undoList->begin(this, "position of " + getTagStr());790if (getTagProperty()->getTag() == GNE_TAG_POILANE) {791GNEChange_Attribute::changeAttribute(this, SUMO_ATTR_POSITION, toString(moveResult.newFirstPos), undoList);792} else {793GNEChange_Attribute::changeAttribute(this, SUMO_ATTR_POSITION, toString(moveResult.shapeToUpdate.front()), undoList);794}795undoList->end();796}797}798799/****************************************************************************/800801802