Path: blob/main/src/netedit/elements/additional/GNEPOI.cpp
193717 views
/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 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 netedit18/****************************************************************************/1920#include <netedit/changes/GNEChange_Attribute.h>21#include <netedit/elements/moving/GNEMoveElementLaneSingle.h>22#include <netedit/elements/moving/GNEMoveElementViewResizable.h>23#include <netedit/GNENet.h>24#include <netedit/GNETagProperties.h>25#include <utils/common/StringTokenizer.h>26#include <utils/gui/div/GLHelper.h>27#include <utils/gui/div/GUIDesigns.h>28#include <utils/gui/div/GUIParameterTableWindow.h>29#include <utils/gui/globjects/GUIGLObjectPopupMenu.h>30#include <utils/gui/globjects/GUIPointOfInterest.h>31#include <utils/gui/images/GUITextureSubSys.h>32#include <utils/xml/NamespaceIDs.h>3334#include "GNEPOI.h"3536// ===========================================================================37// method definitions38// ===========================================================================3940GNEPOI::GNEPOI(SumoXMLTag tag, GNENet* net) :41Shape(""),42GNEAdditional(net, tag),43myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),44myMoveElementViewResizable(new GNEMoveElementViewResizable(this, (tag == GNE_TAG_POIGEO) ? GNEMoveElementView::AttributesFormat::GEO : GNEMoveElementView::AttributesFormat::CARTESIAN,45GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT, SUMO_ATTR_POSITION, myPosOverView)) {46}474849GNEPOI::GNEPOI(const std::string& id, GNENet* net, FileBucket* fileBucket, const std::string& type, const RGBColor& color, const Position& pos,50const bool geo, POIIcon icon, const double layer, const double angle, const std::string& imgFile, const double width,51const double height, const std::string& name, const Parameterised::Map& parameters) :52Shape(id, type, color, layer, angle, imgFile, ""),53GNEAdditional(id, net, geo ? GNE_TAG_POIGEO : SUMO_TAG_POI, fileBucket, name),54Parameterised(parameters),55myPosOverView(pos),56myWidth(width),57myHeight(height),58myPOIIcon(icon),59myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),60myMoveElementViewResizable(new GNEMoveElementViewResizable(this, geo ? GNEMoveElementView::AttributesFormat::GEO : GNEMoveElementView::AttributesFormat::CARTESIAN,61GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT, SUMO_ATTR_POSITION, myPosOverView)) {62// update position depending of GEO63if (geo) {64Position cartesian = myPosOverView;65GeoConvHelper::getFinal().x2cartesian_const(cartesian);66myPosOverView = cartesian;67}68// update centering boundary without updating grid69updateCenteringBoundary(false);70}717273GNEPOI::GNEPOI(const std::string& id, GNENet* net, FileBucket* fileBucket, const std::string& type, const RGBColor& color, GNELane* lane, const double posOverLane,74const bool friendlyPos, const double posLat, POIIcon icon, const double layer, const double angle, const std::string& imgFile, const double width,75const double height, const std::string& name, const Parameterised::Map& parameters) :76Shape(id, type, color, layer, angle, imgFile, ""),77GNEAdditional(id, net, GNE_TAG_POILANE, fileBucket, name),78Parameterised(parameters),79myPosOverLane(posOverLane),80myFriendlyPos(friendlyPos),81myWidth(width),82myHeight(height),83myPosLat(posLat),84myPOIIcon(icon),85myMoveElementLaneSingle(new GNEMoveElementLaneSingle(this, SUMO_ATTR_POSITION, myPosOverLane, myFriendlyPos, GNEMoveElementLaneSingle::PositionType::SINGLE)),86myMoveElementViewResizable(new GNEMoveElementViewResizable(this, GNEMoveElementView::AttributesFormat::POSITION, GNEMoveElementViewResizable::ResizingFormat::WIDTH_HEIGHT,87SUMO_ATTR_POSITION, myPosOverView)) {88// set parents89setParent<GNELane*>(lane);90// update centering boundary without updating grid91updateCenteringBoundary(false);92}939495GNEPOI::~GNEPOI() {96delete myMoveElementLaneSingle;97delete myMoveElementViewResizable;98}99100101GNEMoveElement*102GNEPOI::getMoveElement() const {103if (getTagProperty()->getTag() == GNE_TAG_POILANE) {104return myMoveElementLaneSingle;105} else {106return myMoveElementViewResizable;107}108}109110111Parameterised*112GNEPOI::getParameters() {113return this;114}115116117const Parameterised*118GNEPOI::getParameters() const {119return this;120}121122123std::string124GNEPOI::generateChildID(SumoXMLTag /*childTag*/) {125return "";126}127128129CommonXMLStructure::SumoBaseObject*130GNEPOI::getSumoBaseObject() const {131CommonXMLStructure::SumoBaseObject* POIBaseObject = new CommonXMLStructure::SumoBaseObject(nullptr);132POIBaseObject->setTag(SUMO_TAG_POI);133// fill attributes134POIBaseObject->addStringAttribute(SUMO_ATTR_ID, myID);135POIBaseObject->addColorAttribute(SUMO_ATTR_COLOR, getShapeColor());136POIBaseObject->addStringAttribute(SUMO_ATTR_TYPE, getShapeType());137POIBaseObject->addStringAttribute(SUMO_ATTR_ICON, SUMOXMLDefinitions::POIIcons.getString(myPOIIcon));138POIBaseObject->addDoubleAttribute(SUMO_ATTR_LAYER, getShapeLayer());139POIBaseObject->addStringAttribute(SUMO_ATTR_IMGFILE, getShapeImgFile());140POIBaseObject->addDoubleAttribute(SUMO_ATTR_WIDTH, myWidth);141POIBaseObject->addDoubleAttribute(SUMO_ATTR_HEIGHT, myHeight);142POIBaseObject->addDoubleAttribute(SUMO_ATTR_ANGLE, getShapeNaviDegree());143POIBaseObject->addStringAttribute(SUMO_ATTR_NAME, myAdditionalName);144return POIBaseObject;145}146147148void149GNEPOI::writeAdditional(OutputDevice& device) const {150device.openTag(SUMO_TAG_POI);151// write common additional attributes152writeAdditionalAttributes(device);153// specific of poi lanes154if (getTagProperty()->getTag() == GNE_TAG_POILANE) {155// write move attributes156myMoveElementLaneSingle->writeMoveAttributes(device);157// write specific attributes158if (myPosLat != 0) {159device.writeAttr(SUMO_ATTR_POSITION_LAT, myPosLat);160}161} else {162// write move attributes163myMoveElementViewResizable->writeMoveAttributes(device);164}165// write shape attributes166writeShapeAttributes(device, RGBColor::RED, Shape::DEFAULT_LAYER_POI);167// width168if (myWidth != Shape::DEFAULT_IMG_WIDTH) {169device.writeAttr(SUMO_ATTR_WIDTH, myWidth);170}171// height172if (myHeight != Shape::DEFAULT_IMG_HEIGHT) {173device.writeAttr(SUMO_ATTR_HEIGHT, myHeight);174}175// Icon176if (myPOIIcon != POIIcon::NONE) {177device.writeAttr(SUMO_ATTR_ICON, SUMOXMLDefinitions::POIIcons.getString(myPOIIcon));178}179// params180writeParams(device);181device.closeTag();182}183184185bool186GNEPOI::isAdditionalValid() const {187// only for POIS over lanes188if (getTagProperty()->getTag() == GNE_TAG_POILANE) {189// only movement problems190return myMoveElementLaneSingle->isMoveElementValid();191} else {192return true;193}194}195196197std::string198GNEPOI::getAdditionalProblem() const {199// only for POIS over lanes200if (getTagProperty()->getTag() == GNE_TAG_POILANE) {201// only movement problems202return myMoveElementLaneSingle->getMovingProblem();203} else {204return "";205}206}207208209void210GNEPOI::fixAdditionalProblem() {211// only for POIS over lanes212if (getTagProperty()->getTag() == GNE_TAG_POILANE) {213// only movement problems214myMoveElementLaneSingle->fixMovingProblem();215}216}217218219void220GNEPOI::updateGeometry() {221// check if update width and height shapes222if ((myWidth > 0) && (myHeight > 0)) {223// calculate shape length224myMoveElementViewResizable->myShapeHeight.clear();225myMoveElementViewResizable->myShapeHeight.push_back(Position(0, myHeight * -0.5));226myMoveElementViewResizable->myShapeHeight.push_back(Position(0, myHeight * 0.5));227// move228myMoveElementViewResizable->myShapeHeight.add(myPosOverView);229// calculate shape width230PositionVector leftShape = myMoveElementViewResizable->myShapeHeight;231leftShape.move2side(myWidth * -0.5);232PositionVector rightShape = myMoveElementViewResizable->myShapeHeight;233rightShape.move2side(myWidth * 0.5);234myMoveElementViewResizable->myShapeWidth = {leftShape.getCentroid(), rightShape.getCentroid()};235}236// set additional geometry237if (getParentLanes().size() > 0) {238myAdditionalGeometry.updateGeometry(getParentLanes().front()->getLaneShape(),239myMoveElementLaneSingle->getFixedPositionOverLane(true), myPosLat);240} else {241myAdditionalGeometry.updateSinglePosGeometry(myPosOverView, 0);242}243}244245246Position247GNEPOI::getPositionInView() const {248return myAdditionalGeometry.getShape().getPolygonCenter();249}250251252double253GNEPOI::getExaggeration(const GUIVisualizationSettings& s) const {254return s.poiSize.getExaggeration(s, this);255}256257258void259GNEPOI::updateCenteringBoundary(const bool updateGrid) {260// Remove object from net261if (updateGrid) {262myNet->removeGLObjectFromGrid(this);263}264// update geometry265updateGeometry();266// reset boundary267myAdditionalBoundary.reset();268// add center269myAdditionalBoundary.add(myPosOverView);270// add width271for (const auto& pos : myMoveElementViewResizable->myShapeWidth) {272myAdditionalBoundary.add(pos);273}274// add height275for (const auto& pos : myMoveElementViewResizable->myShapeHeight) {276myAdditionalBoundary.add(pos);277}278// grow boundary279myAdditionalBoundary.grow(5);280// add object into net281if (updateGrid) {282myNet->addGLObjectIntoGrid(this);283}284}285286287void288GNEPOI::splitEdgeGeometry(const double /*splitPosition*/, const GNENetworkElement* /*originalElement*/, const GNENetworkElement* /*newElement*/, GNEUndoList* /*undoList*/) {289// nothing to split290}291292293GUIGlID294GNEPOI::getGlID() const {295return GUIGlObject::getGlID();296}297298299bool300GNEPOI::checkDrawMoveContour() const {301// get edit modes302const auto& editModes = myNet->getViewNet()->getEditModes();303// check if we're in move mode304if (!myNet->getViewNet()->isCurrentlyMovingElements() && editModes.isCurrentSupermodeNetwork() &&305!myNet->getViewNet()->getEditNetworkElementShapes().getEditedNetworkElement() &&306(editModes.networkEditMode == NetworkEditMode::NETWORK_MOVE) && myNet->getViewNet()->checkOverLockedElement(this, mySelected)) {307// only move the first element308return myNet->getViewNet()->getViewObjectsSelector().getGUIGlObjectFront() == this;309} else {310return false;311}312}313314315std::string316GNEPOI::getParentName() const {317if (getTagProperty()->getTag() == GNE_TAG_POILANE) {318return getParentLanes().front()->getID();319} else {320return myNet->getMicrosimID();321}322}323324325GUIGLObjectPopupMenu*326GNEPOI::getPopUpMenu(GUIMainWindow& app, GUISUMOAbstractView& parent) {327// create popup328GUIGLObjectPopupMenu* ret = new GUIGLObjectPopupMenu(app, parent, this);329// build common options330buildPopUpMenuCommonOptions(ret, app, myNet->getViewNet(), myTagProperty->getTag(), mySelected);331// specific of non juPedSim polygons332if (!myTagProperty->isJuPedSimElement()) {333// continue depending of lane number334if (getTagProperty()->getTag() == GNE_TAG_POILANE) {335// add option for convert to GNEPOI336GUIDesigns::buildFXMenuCommand(ret, TL("Release from lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_RELEASE);337} else {338// add option for convert to GNEPOI339GUIDesigns::buildFXMenuCommand(ret, TL("Attach to nearest lane"), GUIIconSubSys::getIcon(GUIIcon::LANE), &parent, MID_GNE_POI_ATTACH);340// check if transform341if (GeoConvHelper::getFinal().getProjString() != "!") {342if (getTagProperty()->getTag() == GNE_TAG_POIGEO) {343GUIDesigns::buildFXMenuCommand(ret, TL("Transform to POI"), GUIIconSubSys::getIcon(GUIIcon::POI), &parent, MID_GNE_POI_TRANSFORM_POI);344} else {345GUIDesigns::buildFXMenuCommand(ret, TL("Transform to POI Geo"), GUIIconSubSys::getIcon(GUIIcon::POIGEO), &parent, MID_GNE_POI_TRANSFORM_POIGEO);346}347}348}349}350return ret;351}352353354void355GNEPOI::drawGL(const GUIVisualizationSettings& s) const {356// first check if POI can be drawn357if (myNet->getViewNet()->getDemandViewOptions().showShapes() &&358myNet->getViewNet()->getDataViewOptions().showShapes()) {359// draw boundaries360GLHelper::drawBoundary(s, getCenteringBoundary());361// obtain POIExaggeration362const double POIExaggeration = getExaggeration(s);363// get detail level364const auto d = s.getDetailLevel(POIExaggeration);365// check if draw moving geometry points (only if we have a defined image366const bool movingGeometryPoints = getShapeImgFile().empty() ? false : drawMovingGeometryPoints();367// draw geometry only if we'rent in drawForObjectUnderCursor mode368if (s.checkDrawPOI(myWidth, myHeight, d, isAttributeCarrierSelected())) {369// draw POI370drawPOI(s, d, movingGeometryPoints);371// draw lock icon372GNEViewNetHelper::LockIcon::drawLockIcon(d, this, getType(), getPositionInView(), POIExaggeration);373// draw dotted contours374if (movingGeometryPoints) {375// get snap radius376const double snapRadius = myNet->getViewNet()->getVisualisationSettings().neteditSizeSettings.additionalGeometryPointRadius;377const double snapRadiusSquared = snapRadius * snapRadius;378// get mouse position379const Position mousePosition = myNet->getViewNet()->getPositionInformation();380// check if we're editing width or height381if ((myMoveElementViewResizable->myShapeHeight.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||382(myMoveElementViewResizable->myShapeHeight.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {383myMoveElementViewResizable->myMovingContourUp.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);384myMoveElementViewResizable->myMovingContourDown.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);385} else if ((myMoveElementViewResizable->myShapeWidth.front().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared) ||386(myMoveElementViewResizable->myShapeWidth.back().distanceSquaredTo2D(mousePosition) <= snapRadiusSquared)) {387myMoveElementViewResizable->myMovingContourLeft.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);388myMoveElementViewResizable->myMovingContourRight.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidthSmall, true);389}390} else {391myAdditionalContour.drawDottedContours(s, d, this, s.dottedContourSettings.segmentWidth, true);392}393}394// calculate contour395calculatePOIContour(s, d, POIExaggeration, movingGeometryPoints);396}397}398399400std::string401GNEPOI::getAttribute(SumoXMLAttr key) const {402switch (key) {403case SUMO_ATTR_ID:404return myID;405case SUMO_ATTR_COLOR:406return toString(getShapeColor());407case SUMO_ATTR_POSITION_LAT:408return toString(myPosLat);409case SUMO_ATTR_LON:410if (GeoConvHelper::getFinal().getProjString() != "!") {411return toString(getAttributeDouble(key), 8);412} else {413return TL("No geo-conversion defined");414}415case SUMO_ATTR_LAT:416if (GeoConvHelper::getFinal().getProjString() != "!") {417return toString(getAttributeDouble(key), 8);418} else {419return TL("No geo-conversion defined");420}421case SUMO_ATTR_TYPE:422return getShapeType();423case SUMO_ATTR_ICON:424return SUMOXMLDefinitions::POIIcons.getString(myPOIIcon);425case SUMO_ATTR_LAYER:426return toString(getShapeLayer());427case SUMO_ATTR_IMGFILE:428return getShapeImgFile();429case SUMO_ATTR_WIDTH:430return toString(myWidth);431case SUMO_ATTR_HEIGHT:432return toString(myHeight);433case SUMO_ATTR_ANGLE:434return toString(getShapeNaviDegree());435case SUMO_ATTR_NAME:436return myAdditionalName;437case GNE_ATTR_SHIFTLANEINDEX:438return "";439default:440if (getTagProperty()->getTag() == GNE_TAG_POILANE) {441return myMoveElementLaneSingle->getMovingAttribute(key);442} else {443return myMoveElementViewResizable->getMovingAttribute(key);444}445}446}447448449double450GNEPOI::getAttributeDouble(SumoXMLAttr key) const {451switch (key) {452case SUMO_ATTR_POSITION_LAT:453return myPosLat;454case SUMO_ATTR_LON:455if (GeoConvHelper::getFinal().getProjString() != "!") {456// calculate geo position457Position GEOPosition = myPosOverView;458GeoConvHelper::getFinal().cartesian2geo(GEOPosition);459// return lon460return GEOPosition.x();461} else {462throw InvalidArgument(getTagStr() + " attribute '" + toString(key) + "' not allowed");463}464case SUMO_ATTR_LAT:465if (GeoConvHelper::getFinal().getProjString() != "!") {466// calculate geo position467Position GEOPosition = myPosOverView;468GeoConvHelper::getFinal().cartesian2geo(GEOPosition);469// return lat470return GEOPosition.y();471} else {472throw InvalidArgument(getTagStr() + " attribute '" + toString(key) + "' not allowed");473}474case SUMO_ATTR_LAYER:475return getShapeLayer();476case SUMO_ATTR_WIDTH:477return myWidth;478case SUMO_ATTR_HEIGHT:479return myHeight;480case SUMO_ATTR_ANGLE:481return getShapeNaviDegree();482default:483if (getTagProperty()->getTag() == GNE_TAG_POILANE) {484return myMoveElementLaneSingle->getMovingAttributeDouble(key);485} else {486return myMoveElementViewResizable->getMovingAttributeDouble(key);487}488}489}490491492Position493GNEPOI::getAttributePosition(SumoXMLAttr key) const {494if (getTagProperty()->getTag() == GNE_TAG_POILANE) {495return myMoveElementLaneSingle->getMovingAttributePosition(key);496} else {497return myMoveElementViewResizable->getMovingAttributePosition(key);498}499}500501502PositionVector503GNEPOI::getAttributePositionVector(SumoXMLAttr key) const {504return getCommonAttributePositionVector(key);505}506507508void509GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value, GNEUndoList* undoList) {510switch (key) {511case SUMO_ATTR_ID:512case SUMO_ATTR_COLOR:513case SUMO_ATTR_POSITION_LAT:514case SUMO_ATTR_LON:515case SUMO_ATTR_LAT:516case SUMO_ATTR_TYPE:517case SUMO_ATTR_ICON:518case SUMO_ATTR_LAYER:519case SUMO_ATTR_IMGFILE:520case SUMO_ATTR_WIDTH:521case SUMO_ATTR_HEIGHT:522case SUMO_ATTR_ANGLE:523case SUMO_ATTR_NAME:524case GNE_ATTR_SHIFTLANEINDEX:525GNEChange_Attribute::changeAttribute(this, key, value, undoList);526break;527default:528if (getTagProperty()->getTag() == GNE_TAG_POILANE) {529return myMoveElementLaneSingle->setMovingAttribute(key, value, undoList);530} else {531return myMoveElementViewResizable->setMovingAttribute(key, value, undoList);532}533}534}535536537bool538GNEPOI::isValid(SumoXMLAttr key, const std::string& value) {539switch (key) {540case SUMO_ATTR_ID:541return isValidAdditionalID(NamespaceIDs::POIs, value);542case SUMO_ATTR_COLOR:543return canParse<RGBColor>(value);544case SUMO_ATTR_POSITION_LAT:545return canParse<double>(value);546case SUMO_ATTR_LON:547return canParse<double>(value);548case SUMO_ATTR_LAT:549return canParse<double>(value);550case SUMO_ATTR_TYPE:551return true;552case SUMO_ATTR_ICON:553return SUMOXMLDefinitions::POIIcons.hasString(value);554case SUMO_ATTR_LAYER:555if (value.empty()) {556return true;557} else {558return canParse<double>(value);559}560case SUMO_ATTR_IMGFILE:561if (value == "") {562return true;563} else {564// check that image can be loaded565return GUITexturesHelper::getTextureID(value) != -1;566}567case SUMO_ATTR_WIDTH:568return canParse<double>(value) && (parse<double>(value) > 0);569case SUMO_ATTR_HEIGHT:570return canParse<double>(value) && (parse<double>(value) > 0);571case SUMO_ATTR_ANGLE:572return canParse<double>(value);573case SUMO_ATTR_NAME:574return SUMOXMLDefinitions::isValidAttribute(value);575default:576if (getTagProperty()->getTag() == GNE_TAG_POILANE) {577return myMoveElementLaneSingle->isMovingAttributeValid(key, value);578} else {579return myMoveElementViewResizable->isMovingAttributeValid(key, value);580}581}582}583584585bool586GNEPOI::isAttributeEnabled(SumoXMLAttr key) const {587switch (key) {588case SUMO_ATTR_POSITION:589if (myTagProperty->getTag() == GNE_TAG_POIGEO) {590return (GeoConvHelper::getFinal().getProjString() != "!");591} else {592return true;593}594case SUMO_ATTR_LON:595case SUMO_ATTR_LAT:596return (GeoConvHelper::getFinal().getProjString() != "!");597default:598return true;599}600}601602603std::string604GNEPOI::getPopUpID() const {605return getTagStr() + ": " + getID();606}607608609std::string610GNEPOI::getHierarchyName() const {611return getTagStr();612}613614// ===========================================================================615// private616// ===========================================================================617618void619GNEPOI::drawPOI(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,620const bool movingGeometryPoints) const {621if (GUIPointOfInterest::checkDraw(s, this)) {622const double exaggeration = getExaggeration(s);623const auto position = getPositionInView();624// push matrix625GLHelper::pushMatrix();626// set POI color627GUIPointOfInterest::setPOIColor(s, getShapeColor(), this, drawUsingSelectColor());628// add extra offset z provided by icon to avoid overlapping629if (myDrawInFront) {630glTranslated(position.x(), position.y(), GLO_FRONTELEMENT + (double)myPOIIcon);631} else {632glTranslated(position.x(), position.y(), s.poiUseCustomLayer ? s.poiCustomLayer : getShapeLayer() + (double)myPOIIcon);633}634glRotated(-getShapeNaviDegree(), 0, 0, 1);635// check if has to be drawn as a circle or with an image636if (getShapeImgFile() != DEFAULT_IMG_FILE) {637int textureID = GUITexturesHelper::getTextureID(getShapeImgFile());638if (textureID > 0) {639GUITexturesHelper::drawTexturedBox(textureID,640myWidth * 0.5 * exaggeration, myHeight * 0.5 * exaggeration,641myWidth * 0.5 * exaggeration, myHeight * 0.5 * exaggeration);642} else {643// draw box644GLHelper::drawRectangle(Position(0, 0), myWidth * exaggeration, myHeight * exaggeration);645}646} else {647// fallback if no image is defined648GLHelper::drawFilledCircle(std::max(myWidth, myHeight) * 0.5 * exaggeration, s.poiDetail);649// check if draw polygon650if (myPOIIcon != POIIcon::NONE) {651// translate652glTranslated(0, 0, 0.1);653// rotate654glRotated(180, 0, 0, 1);655// draw texture656GUITexturesHelper::drawTexturedBox(GUITextureSubSys::getPOITexture(myPOIIcon), exaggeration * 0.8);657}658}659// pop matrix660GLHelper::popMatrix();661if (!s.drawForRectangleSelection) {662const Position namePos = position;663drawName(namePos, s.scale, s.poiName, s.angle);664if (s.poiType.show(this)) {665const Position p = namePos + Position(0, -0.6 * s.poiType.size / s.scale);666GLHelper::drawTextSettings(s.poiType, getShapeType(), p, s.scale, s.angle);667}668if (s.poiText.show(this)) {669GLHelper::pushMatrix();670glTranslated(namePos.x(), namePos.y(), 0);671std::string value = getParameter(s.poiTextParam, "");672if (value != "") {673auto lines = StringTokenizer(value, StringTokenizer::NEWLINE).getVector();674glRotated(-s.angle, 0, 0, 1);675glTranslated(0, 0.7 * s.poiText.scaledSize(s.scale) * (double)lines.size(), 0);676glRotated(s.angle, 0, 0, 1);677// FONS_ALIGN_LEFT = 1678// FONS_ALIGN_CENTER = 2679// FONS_ALIGN_MIDDLE = 16680const int align = (lines.size() > 1 ? 1 : 2) | 16;681for (std::string& line : lines) {682GLHelper::drawTextSettings(s.poiText, line, Position(0, 0), s.scale, s.angle, GLO_MAX, align);683glRotated(-s.angle, 0, 0, 1);684glTranslated(0, -0.7 * s.poiText.scaledSize(s.scale), 0);685glRotated(s.angle, 0, 0, 1);686}687}688GLHelper::popMatrix();689}690}691// draw geometry points692if (movingGeometryPoints) {693if (myMoveElementViewResizable->myShapeHeight.size() > 0) {694drawUpGeometryPoint(s, d, myMoveElementViewResizable->myShapeHeight.front(), 180, RGBColor::ORANGE);695drawDownGeometryPoint(s, d, myMoveElementViewResizable->myShapeHeight.back(), 180, RGBColor::ORANGE);696}697if (myMoveElementViewResizable->myShapeWidth.size() > 0) {698drawLeftGeometryPoint(s, d, myMoveElementViewResizable->myShapeWidth.back(), -90, RGBColor::ORANGE);699drawRightGeometryPoint(s, d, myMoveElementViewResizable->myShapeWidth.front(), -90, RGBColor::ORANGE);700}701}702}703}704705706void707GNEPOI::calculatePOIContour(const GUIVisualizationSettings& s, const GUIVisualizationSettings::Detail d,708const double exaggeration, const bool movingGeometryPoints) const {709// check if we're calculating the contour or the moving geometry points710if (movingGeometryPoints) {711myMoveElementViewResizable->myMovingContourUp.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeHeight.front(), s.neteditSizeSettings.additionalGeometryPointRadius,712getShapeLayer(), exaggeration, nullptr);713myMoveElementViewResizable->myMovingContourDown.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeHeight.back(), s.neteditSizeSettings.additionalGeometryPointRadius,714getShapeLayer(), exaggeration, nullptr);715myMoveElementViewResizable->myMovingContourLeft.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeWidth.front(), s.neteditSizeSettings.additionalGeometryPointRadius,716getShapeLayer(), exaggeration, nullptr);717myMoveElementViewResizable->myMovingContourRight.calculateContourCircleShape(s, d, this, myMoveElementViewResizable->myShapeWidth.back(), s.neteditSizeSettings.additionalGeometryPointRadius,718getShapeLayer(), exaggeration, nullptr);719} else {720const auto parentEdgeBoundary = (getTagProperty()->getTag() == GNE_TAG_POILANE) ? getParentLanes().front()->getParentEdge() : nullptr;721if (getShapeImgFile().empty()) {722myAdditionalContour.calculateContourCircleShape(s, d, this, getPositionInView(), std::max(myWidth, myHeight) * 0.5, getShapeLayer(), exaggeration, parentEdgeBoundary);723} else {724myAdditionalContour.calculateContourRectangleShape(s, d, this, getPositionInView(), myHeight * 0.5, myWidth * 0.5, getShapeLayer(), 0, 0, getShapeNaviDegree(), exaggeration, parentEdgeBoundary);725}726}727}728729730void731GNEPOI::setAttribute(SumoXMLAttr key, const std::string& value) {732switch (key) {733case SUMO_ATTR_ID: {734// update microsimID735setAdditionalID(value);736// set named ID737myID = value;738break;739}740case SUMO_ATTR_COLOR:741setShapeColor(parse<RGBColor>(value));742break;743case SUMO_ATTR_LANE:744replaceAdditionalParentLanes(value);745break;746case SUMO_ATTR_POSITION: {747if (myTagProperty->getTag() == GNE_TAG_POILANE) {748myPosOverLane = parse<double>(value);749} else {750myPosOverView = parse<Position>(value);751}752break;753}754case SUMO_ATTR_POSITION_LAT:755myPosLat = parse<double>(value);756break;757case SUMO_ATTR_LON: {758// parse geo attributes759Position pos(parse<double>(value), getAttributeDouble(SUMO_ATTR_LAT));760// transform to cartesian761GeoConvHelper::getFinal().x2cartesian_const(pos);762// update view position763myPosOverView = pos;764break;765}766case SUMO_ATTR_LAT: {767// parse geo attributes768Position pos(getAttributeDouble(SUMO_ATTR_LON), parse<double>(value));769// transform to cartesian770GeoConvHelper::getFinal().x2cartesian_const(pos);771// update view position772myPosOverView = pos;773break;774}775case SUMO_ATTR_TYPE:776setShapeType(value);777break;778case SUMO_ATTR_ICON:779SUMOXMLDefinitions::POIIcons.get(value);780break;781case SUMO_ATTR_LAYER:782if (value.empty()) {783setShapeLayer(myTagProperty->getDefaultDoubleValue(key));784} else {785setShapeLayer(parse<double>(value));786}787break;788case SUMO_ATTR_IMGFILE:789// first remove object from grid due img file affect to boundary790if (getID().size() > 0) {791myNet->removeGLObjectFromGrid(this);792}793setShapeImgFile(value);794// all textures must be refresh795GUITexturesHelper::clearTextures();796// add object into grid again797if (getID().size() > 0) {798myNet->addGLObjectIntoGrid(this);799}800break;801case SUMO_ATTR_WIDTH:802// set new width803myWidth = parse<double>(value);804break;805case SUMO_ATTR_HEIGHT:806// set new height807myHeight = parse<double>(value);808break;809case SUMO_ATTR_ANGLE:810setShapeNaviDegree(parse<double>(value));811break;812case SUMO_ATTR_NAME:813myAdditionalName = value;814break;815case GNE_ATTR_SHIFTLANEINDEX:816shiftLaneIndex();817break;818default:819if (getTagProperty()->getTag() == GNE_TAG_POILANE) {820return myMoveElementLaneSingle->setMovingAttribute(key, value);821} else {822return myMoveElementViewResizable->setMovingAttribute(key, value);823}824}825// update boundary (except for template)826if (getID().size() > 0) {827updateCenteringBoundary(myTagProperty->getTag() != GNE_TAG_POILANE);828}829}830831/****************************************************************************/832833834