Path: blob/main/src/netedit/frames/network/GNEShapeFrame.cpp
193674 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 GNEShapeFrame.cpp14/// @author Pablo Alvarez Lopez15/// @date Aug 201716///17// The Widget for add polygons18/****************************************************************************/1920#include <netedit/GNEApplicationWindow.h>21#include <netedit/GNENet.h>22#include <netedit/GNEViewParent.h>23#include <netedit/elements/additional/GNEAdditionalHandler.h>24#include <netedit/frames/GNEAttributesEditor.h>25#include <netedit/frames/GNEDrawingShape.h>26#include <netedit/frames/GNETagSelector.h>27#include <utils/foxtools/MFXDynamicLabel.h>28#include <utils/gui/div/GUIDesigns.h>29#include <utils/gui/div/GUIUserIO.h>3031#include "GNEShapeFrame.h"3233// ===========================================================================34// FOX callback mapping35// ===========================================================================3637FXDEFMAP(GNEShapeFrame::GEOPOICreator) GEOPOICreatorMap[] = {38FXMAPFUNC(SEL_COMMAND, MID_GNE_SET_ATTRIBUTE, GNEShapeFrame::GEOPOICreator::onCmdSetCoordinates),39FXMAPFUNC(SEL_COMMAND, MID_CHOOSEN_OPERATION, GNEShapeFrame::GEOPOICreator::onCmdSetFormat),40FXMAPFUNC(SEL_COMMAND, MID_GNE_CREATE, GNEShapeFrame::GEOPOICreator::onCmdCreateGEOPOI),41};4243// Object implementation44FXIMPLEMENT(GNEShapeFrame::GEOPOICreator, GNEGroupBoxModule, GEOPOICreatorMap, ARRAYNUMBER(GEOPOICreatorMap))454647// ===========================================================================48// method definitions49// ===========================================================================5051// ---------------------------------------------------------------------------52// GNEShapeFrame::GEOPOICreator - methods53// ---------------------------------------------------------------------------5455GNEShapeFrame::GEOPOICreator::GEOPOICreator(GNEShapeFrame* polygonFrameParent) :56GNEGroupBoxModule(polygonFrameParent, TL("GEO POI Creator")),57myShapeFrameParent(polygonFrameParent) {58// create RadioButtons for formats59myLonLatRadioButton = new FXRadioButton(getCollapsableFrame(), TL("Format: Lon-Lat"), this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);60myLatLonRadioButton = new FXRadioButton(getCollapsableFrame(), TL("Format: Lat-Lon"), this, MID_CHOOSEN_OPERATION, GUIDesignRadioButton);61// set lat-lon as default62myLatLonRadioButton->setCheck(TRUE);63// create text field for coordinates64myCoordinatesTextField = new FXTextField(getCollapsableFrame(), GUIDesignTextFieldNCol, this, MID_GNE_SET_ATTRIBUTE, GUIDesignTextField);65// create checkBox66myCenterViewAfterCreationCheckButton = new FXCheckButton(getCollapsableFrame(), TL("Center View after creation"), this, MID_GNE_SET_ATTRIBUTE, GUIDesignCheckButton);67// create button for create GEO POIs68myCreateGEOPOIButton = GUIDesigns::buildFXButton(getCollapsableFrame(), TL("Create GEO POI (clipboard)"), "", "", nullptr, this, MID_GNE_CREATE, GUIDesignButton);69// create information label70myLabelCartesianPosition = new MFXDynamicLabel(getCollapsableFrame(),71(TL("Cartesian equivalence:") + std::string("\n") +72TL("- X = give valid longitude") + std::string("\n") +73TL("- Y = give valid latitude")).c_str(),740, GUIDesignLabelFrameInformation);75}767778GNEShapeFrame::GEOPOICreator::~GEOPOICreator() {}798081void82GNEShapeFrame::GEOPOICreator::showGEOPOICreatorModule() {83// check if there is an GEO Proj string is defined84if (GeoConvHelper::getFinal().getProjString() != "!") {85myCoordinatesTextField->enable();86myCoordinatesTextField->setText("");87myCoordinatesTextField->enable();88myCreateGEOPOIButton->enable();89} else {90myCoordinatesTextField->setText(TL("No geo-conversion defined"));91myCoordinatesTextField->disable();92myCreateGEOPOIButton->disable();93}94show();95}969798void99GNEShapeFrame::GEOPOICreator::hideGEOPOICreatorModule() {100hide();101}102103104long105GNEShapeFrame::GEOPOICreator::onCmdSetCoordinates(FXObject*, FXSelector, void*) {106// check if input contains spaces107std::string input = myCoordinatesTextField->getText().text();108std::string inputWithoutSpaces;109for (const auto& i : input) {110if (i != ' ') {111inputWithoutSpaces.push_back(i);112}113}114// if input contains spaces, call this function again, and in other case set red text color115if (input.size() != inputWithoutSpaces.size()) {116myCoordinatesTextField->setText(inputWithoutSpaces.c_str());117}118if (inputWithoutSpaces.size() > 0) {119myCreateGEOPOIButton->setText(TL("Create GEO POI"));120} else {121myCreateGEOPOIButton->setText(TL("Create GEO POI (clipboard)"));122}123// simply check if given value can be parsed to Position124if (GNEAttributeCarrier::canParse<Position>(myCoordinatesTextField->getText().text())) {125myCoordinatesTextField->setTextColor(GUIDesignTextColorBlack);126myCoordinatesTextField->killFocus();127// convert coordinates into lon-lat128Position geoPos = GNEAttributeCarrier::parse<Position>(myCoordinatesTextField->getText().text());129if (myLatLonRadioButton->getCheck() == TRUE) {130geoPos.swapXY();131}132GeoConvHelper::getFinal().x2cartesian_const(geoPos);133// check if GEO Position has to be swapped134// update myLabelCartesianPosition135myLabelCartesianPosition->setText(136(TL("Cartesian equivalence:") + std::string("\n- X = ") + toString(geoPos.x()) + std::string("\n- Y = ") + toString(geoPos.y())).c_str());137} else {138myCoordinatesTextField->setTextColor(GUIDesignTextColorRed);139myLabelCartesianPosition->setText(140(TL("Cartesian equivalence:") + std::string("\n") +141TL("- X = give valid longitude") + std::string("\n") +142TL("- Y = give valid latitude")).c_str());143};144return 1;145}146147148long149GNEShapeFrame::GEOPOICreator::onCmdSetFormat(FXObject* obj, FXSelector, void*) {150//disable other radio button depending of selected option151if (obj == myLonLatRadioButton) {152myLonLatRadioButton->setCheck(TRUE);153myLatLonRadioButton->setCheck(FALSE);154} else if (obj == myLatLonRadioButton) {155myLonLatRadioButton->setCheck(FALSE);156myLatLonRadioButton->setCheck(TRUE);157}158// in both cases call onCmdSetCoordinates(0,0,0) to set new cartesian equivalence159onCmdSetCoordinates(0, 0, 0);160return 1;161}162163164long165GNEShapeFrame::GEOPOICreator::onCmdCreateGEOPOI(FXObject*, FXSelector, void*) {166// first check if current GEO Position is valid167if (myShapeFrameParent->myShapeAttributesEditor->checkAttributes(true)) {168std::string geoPosStr = myCoordinatesTextField->getText().text();169if (geoPosStr.empty()) {170// use clipboard171WRITE_WARNING(TL("Using clipboard"));172geoPosStr = GUIUserIO::copyFromClipboard(*getApp());173myCoordinatesTextField->setText(geoPosStr.c_str());174// remove spaces, update cartesian value175onCmdSetCoordinates(0, 0, 0);176geoPosStr = myCoordinatesTextField->getText().text();177myCoordinatesTextField->setText("");178myCreateGEOPOIButton->setText(TL("Create GEO POI (clipboard)"));179}180if (GNEAttributeCarrier::canParse<Position>(geoPosStr)) {181// create baseShape object182myShapeFrameParent->createBaseShapeObject(SUMO_TAG_POI);183// obtain shape attributes and values184myShapeFrameParent->myShapeAttributesEditor->fillSumoBaseObject(myShapeFrameParent->myBaseShape);185// force GEO attribute to true and obtain position186myShapeFrameParent->myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);187Position geoPos = GNEAttributeCarrier::parse<Position>(geoPosStr);188// convert coordinates into lon-lat189if (myLatLonRadioButton->getCheck() == TRUE) {190geoPos.swapXY();191}192// add lon/lat193myShapeFrameParent->myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, geoPos.x());194myShapeFrameParent->myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, geoPos.y());195// set GEO Position as true196myShapeFrameParent->myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);197// declare additional handler198GNEAdditionalHandler additionalHandler(myShapeFrameParent->myViewNet->getNet(),199myShapeFrameParent->myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POIGEO)->getFileBucket(),200myShapeFrameParent->myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());201// build shape202additionalHandler.parseSumoBaseObject(myShapeFrameParent->myBaseShape);203// check if view has to be centered over created GEO POI204if (myCenterViewAfterCreationCheckButton->getCheck() == TRUE) {205// create a boundary over given GEO Position and center view over it206Boundary centerPosition;207GeoConvHelper::getFinal().x2cartesian_const(geoPos);208centerPosition.add(geoPos);209centerPosition = centerPosition.grow(10);210myShapeFrameParent->myViewNet->getViewParent()->getView()->centerTo(centerPosition);211}212}213// refresh shape attributes214myShapeFrameParent->myShapeAttributesEditor->refreshAttributesEditor();215}216return 1;217}218219220// ---------------------------------------------------------------------------221// GNEShapeFrame - methods222// ---------------------------------------------------------------------------223224GNEShapeFrame::GNEShapeFrame(GNEViewParent* viewParent, GNEViewNet* viewNet) :225GNEFrame(viewParent, viewNet, TL("Shapes")),226myBaseShape(nullptr) {227228// create item Selector module for shapes229myShapeTagSelector = new GNETagSelector(this, GNETagProperties::Type::SHAPE, SUMO_TAG_POLY);230231// Create shape parameters232myShapeAttributesEditor = new GNEAttributesEditor(this, GNEAttributesEditorType::EditorType::CREATOR);233234// Create drawing controls235myDrawingShape = new GNEDrawingShape(this);236237/// @brief create GEOPOICreator238myGEOPOICreator = new GEOPOICreator(this);239}240241242GNEShapeFrame::~GNEShapeFrame() {243// check if we have to delete base additional object244if (myBaseShape) {245delete myBaseShape;246}247}248249250void251GNEShapeFrame::show() {252// refresh tag selector253myShapeTagSelector->refreshTagSelector();254// show frame255GNEFrame::show();256}257258259bool260GNEShapeFrame::processClick(const Position& clickedPosition, const GNEViewNetHelper::ViewObjectsSelector& viewObjects, bool& updateTemporalShape) {261// reset updateTemporalShape262updateTemporalShape = false;263// check if current selected shape is valid264if (myShapeTagSelector->getCurrentTemplateAC() != nullptr) {265// get tag266SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();267// continue depending of tag268switch (shapeTag) {269case SUMO_TAG_POI:270return processClickPOI(shapeTag, clickedPosition);271case GNE_TAG_POIGEO:272return processClickPOIGeo(clickedPosition);273case GNE_TAG_POILANE:274return processClickPOILanes(viewObjects);275case SUMO_TAG_POLY:276case GNE_TAG_JPS_WALKABLEAREA:277case GNE_TAG_JPS_OBSTACLE:278return processClickPolygons(clickedPosition, updateTemporalShape);279default:280break;281}282}283myViewNet->setStatusBarText(TL("Current selected shape isn't valid."));284return false;285}286287288std::string289GNEShapeFrame::getIdsSelected(const FXList* list) {290// Obtain Id's of list291std::string vectorOfIds;292for (int i = 0; i < list->getNumItems(); i++) {293if (list->isItemSelected(i)) {294if (vectorOfIds.size() > 0) {295vectorOfIds += " ";296}297vectorOfIds += (list->getItem(i)->getText()).text();298}299}300return vectorOfIds;301}302303304GNEDrawingShape*305GNEShapeFrame::getDrawingShapeModule() const {306return myDrawingShape;307}308309310void311GNEShapeFrame::createBaseShapeObject(const SumoXMLTag shapeTag) {312// check if baseShape exist, and if yes, delete it313if (myBaseShape) {314// delete baseShape (and all children)315delete myBaseShape;316}317// just create a base shape318myBaseShape = new CommonXMLStructure::SumoBaseObject(nullptr);319// set tag320myBaseShape->setTag(shapeTag);321}322323324bool325GNEShapeFrame::shapeDrawed() {326// show warning dialogbox and stop check if input parameters are valid327if (!myShapeAttributesEditor->checkAttributes(true)) {328return false;329} else if (myDrawingShape->getTemporalShape().size() == 0) {330WRITE_WARNING(TL("Polygon shape cannot be empty"));331return false;332} else {333// get tag334SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();335// create baseShape object336createBaseShapeObject(shapeTag);337// obtain shape attributes and values338myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);339// obtain shape and check if has to be closed340PositionVector temporalShape = myDrawingShape->getTemporalShape();341if ((myBaseShape->hasBoolAttribute(GNE_ATTR_CLOSE_SHAPE) && myBaseShape->getBoolAttribute(GNE_ATTR_CLOSE_SHAPE)) ||342(shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {343temporalShape.closePolygon();344}345myBaseShape->addPositionVectorAttribute(SUMO_ATTR_SHAPE, temporalShape);346// declare additional handler347GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(shapeTag)->getFileBucket(),348myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());349// build shape350additionalHandler.parseSumoBaseObject(myBaseShape);351// refresh shape attributes352myShapeAttributesEditor->refreshAttributesEditor();353// shape added, then return true;354return true;355}356}357358359void360GNEShapeFrame::tagSelected() {361if (myShapeTagSelector->getCurrentTemplateAC()) {362// show editors363myShapeAttributesEditor->showAttributesEditor(myShapeTagSelector->getCurrentTemplateAC(), true);364// get shape tag365SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();366// Check if drawing mode has to be shown367if ((shapeTag == SUMO_TAG_POLY) || (shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {368myDrawingShape->showDrawingShape();369} else {370myDrawingShape->hideDrawingShape();371}372// Check if GEO POI Creator has to be shown373if (shapeTag == GNE_TAG_POIGEO) {374myGEOPOICreator->showGEOPOICreatorModule();375} else {376myGEOPOICreator->hideGEOPOICreatorModule();377}378} else {379// hide all widgets380myShapeAttributesEditor->hideAttributesEditor();381myDrawingShape->hideDrawingShape();382myGEOPOICreator->hideGEOPOICreatorModule();383}384}385386387bool388GNEShapeFrame::processClickPolygons(const Position& clickedPosition, bool& updateTemporalShape) {389if (myDrawingShape->isDrawing()) {390// add or delete a new point depending of flag "delete last created point"391if (myDrawingShape->getDeleteLastCreatedPoint()) {392myDrawingShape->removeLastPoint();393} else {394myDrawingShape->addNewPoint(clickedPosition);395}396// set temporal shape397updateTemporalShape = true;398return true;399} else {400return false;401}402}403404405bool406GNEShapeFrame::processClickPOI(SumoXMLTag POITag, const Position& clickedPosition) {407// show warning dialogbox and stop if input parameters are invalid408if (!myShapeAttributesEditor->checkAttributes(true)) {409return false;410}411// create baseShape object412createBaseShapeObject(POITag);413// obtain shape attributes and values414myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);415// add X-Y416myBaseShape->addDoubleAttribute(SUMO_ATTR_X, clickedPosition.x());417myBaseShape->addDoubleAttribute(SUMO_ATTR_Y, clickedPosition.y());418// set GEO Position as false (because we have created POI clicking over View419myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, false);420// declare additional handler421GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(POITag)->getFileBucket(),422myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());423// build shape424additionalHandler.parseSumoBaseObject(myBaseShape);425// refresh shape attributes426myShapeAttributesEditor->refreshAttributesEditor();427// shape added, then return true428return true;429}430431432bool433GNEShapeFrame::processClickPOIGeo(const Position& clickedPosition) {434// show warning dialogbox and stop if input parameters are invalid435if (!myShapeAttributesEditor->checkAttributes(true)) {436return false;437}438// create baseShape object439createBaseShapeObject(SUMO_TAG_POI);440// obtain shape attributes and values441myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);442// convert position to cartesian443Position GEOPos = clickedPosition;444GeoConvHelper::getFinal().cartesian2geo(GEOPos);445// add X-Y in geo format446myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, GEOPos.x());447myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, GEOPos.y());448// set GEO Position as false (because we have created POI clicking over View449myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);450// declare additional handler451GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POIGEO)->getFileBucket(),452myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());453// build shape454additionalHandler.parseSumoBaseObject(myBaseShape);455// refresh shape attributes456myShapeAttributesEditor->refreshAttributesEditor();457// shape added, then return true458return true;459}460461462bool463GNEShapeFrame::processClickPOILanes(const GNEViewNetHelper::ViewObjectsSelector& viewObjects) {464// abort if lane is nullptr465if (viewObjects.getLaneFront() == nullptr) {466WRITE_WARNING(TL("POILane can be only placed over lanes"));467return false;468}469// show warning dialogbox and stop if input parameters are invalid470if (!myShapeAttributesEditor->checkAttributes(true)) {471return false;472}473// create baseShape object474createBaseShapeObject(SUMO_TAG_POI);475// obtain shape attributes and values476myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);477// obtain Lane478myBaseShape->addStringAttribute(SUMO_ATTR_LANE, viewObjects.getLaneFront()->getID());479// obtain position over lane480myBaseShape->addDoubleAttribute(SUMO_ATTR_POSITION, viewObjects.getLaneFront()->getLaneShape().nearest_offset_to_point2D(481myViewNet->snapToActiveGrid(myViewNet->getPositionInformation())) /482viewObjects.getLaneFront()->getLengthGeometryFactor());483// declare additional handler484GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myViewNet->getNet()->getACTemplates()->getTemplateAC(GNE_TAG_POILANE)->getFileBucket(),485myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());486// build shape487additionalHandler.parseSumoBaseObject(myBaseShape);488// refresh shape attributes489myShapeAttributesEditor->refreshAttributesEditor();490// shape added, then return true491return true;492}493494/****************************************************************************/495496497