Path: blob/main/src/netedit/frames/network/GNEShapeFrame.cpp
169685 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 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, MFXGroupBoxModule, GEOPOICreatorMap, ARRAYNUMBER(GEOPOICreatorMap))454647// ===========================================================================48// method definitions49// ===========================================================================5051// ---------------------------------------------------------------------------52// GNEShapeFrame::GEOPOICreator - methods53// ---------------------------------------------------------------------------5455GNEShapeFrame::GEOPOICreator::GEOPOICreator(GNEShapeFrame* polygonFrameParent) :56MFXGroupBoxModule(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->myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?200myShapeFrameParent->myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",201myShapeFrameParent->myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());202// build shape203additionalHandler.parseSumoBaseObject(myShapeFrameParent->myBaseShape);204// check if view has to be centered over created GEO POI205if (myCenterViewAfterCreationCheckButton->getCheck() == TRUE) {206// create a boundary over given GEO Position and center view over it207Boundary centerPosition;208GeoConvHelper::getFinal().x2cartesian_const(geoPos);209centerPosition.add(geoPos);210centerPosition = centerPosition.grow(10);211myShapeFrameParent->myViewNet->getViewParent()->getView()->centerTo(centerPosition);212}213}214// refresh shape attributes215myShapeFrameParent->myShapeAttributesEditor->refreshAttributesEditor();216}217return 1;218}219220221// ---------------------------------------------------------------------------222// GNEShapeFrame - methods223// ---------------------------------------------------------------------------224225GNEShapeFrame::GNEShapeFrame(GNEViewParent* viewParent, GNEViewNet* viewNet) :226GNEFrame(viewParent, viewNet, TL("Shapes")),227myBaseShape(nullptr) {228229// create item Selector module for shapes230myShapeTagSelector = new GNETagSelector(this, GNETagProperties::Type::SHAPE, SUMO_TAG_POLY);231232// Create shape parameters233myShapeAttributesEditor = new GNEAttributesEditor(this, GNEAttributesEditorType::EditorType::CREATOR);234235// Create drawing controls236myDrawingShape = new GNEDrawingShape(this);237238/// @brief create GEOPOICreator239myGEOPOICreator = new GEOPOICreator(this);240}241242243GNEShapeFrame::~GNEShapeFrame() {244// check if we have to delete base additional object245if (myBaseShape) {246delete myBaseShape;247}248}249250251void252GNEShapeFrame::show() {253// refresh tag selector254myShapeTagSelector->refreshTagSelector();255// show frame256GNEFrame::show();257}258259260bool261GNEShapeFrame::processClick(const Position& clickedPosition, const GNEViewNetHelper::ViewObjectsSelector& viewObjects, bool& updateTemporalShape) {262// reset updateTemporalShape263updateTemporalShape = false;264// check if current selected shape is valid265if (myShapeTagSelector->getCurrentTemplateAC() != nullptr) {266// get tag267SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();268// continue depending of tag269switch (shapeTag) {270case SUMO_TAG_POI:271return processClickPOI(shapeTag, clickedPosition);272case GNE_TAG_POIGEO:273return processClickPOIGeo(clickedPosition);274case GNE_TAG_POILANE:275return processClickPOILanes(viewObjects);276case SUMO_TAG_POLY:277case GNE_TAG_JPS_WALKABLEAREA:278case GNE_TAG_JPS_OBSTACLE:279return processClickPolygons(clickedPosition, updateTemporalShape);280default:281break;282}283}284myViewNet->setStatusBarText(TL("Current selected shape isn't valid."));285return false;286}287288289std::string290GNEShapeFrame::getIdsSelected(const FXList* list) {291// Obtain Id's of list292std::string vectorOfIds;293for (int i = 0; i < list->getNumItems(); i++) {294if (list->isItemSelected(i)) {295if (vectorOfIds.size() > 0) {296vectorOfIds += " ";297}298vectorOfIds += (list->getItem(i)->getText()).text();299}300}301return vectorOfIds;302}303304305GNEDrawingShape*306GNEShapeFrame::getDrawingShapeModule() const {307return myDrawingShape;308}309310311void312GNEShapeFrame::createBaseShapeObject(const SumoXMLTag shapeTag) {313// check if baseShape exist, and if yes, delete it314if (myBaseShape) {315// delete baseShape (and all children)316delete myBaseShape;317}318// just create a base shape319myBaseShape = new CommonXMLStructure::SumoBaseObject(nullptr);320// set tag321myBaseShape->setTag(shapeTag);322}323324325bool326GNEShapeFrame::shapeDrawed() {327// show warning dialogbox and stop check if input parameters are valid328if (!myShapeAttributesEditor->checkAttributes(true)) {329return false;330} else if (myDrawingShape->getTemporalShape().size() == 0) {331WRITE_WARNING(TL("Polygon shape cannot be empty"));332return false;333} else {334// get tag335SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();336// create baseShape object337createBaseShapeObject(shapeTag);338// obtain shape attributes and values339myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);340// obtain shape and check if has to be closed341PositionVector temporalShape = myDrawingShape->getTemporalShape();342if ((myBaseShape->hasBoolAttribute(GNE_ATTR_CLOSE_SHAPE) && myBaseShape->getBoolAttribute(GNE_ATTR_CLOSE_SHAPE)) ||343(shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {344temporalShape.closePolygon();345}346myBaseShape->addPositionVectorAttribute(SUMO_ATTR_SHAPE, temporalShape);347// declare additional handler348GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_DEMAND_FILE) ?349myBaseShape->getStringAttribute(GNE_ATTR_DEMAND_FILE) : "",350myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());351// build shape352additionalHandler.parseSumoBaseObject(myBaseShape);353// refresh shape attributes354myShapeAttributesEditor->refreshAttributesEditor();355// shape added, then return true;356return true;357}358}359360361void362GNEShapeFrame::tagSelected() {363if (myShapeTagSelector->getCurrentTemplateAC()) {364// show editors365myShapeAttributesEditor->showAttributesEditor(myShapeTagSelector->getCurrentTemplateAC(), true);366// get shape tag367SumoXMLTag shapeTag = myShapeTagSelector->getCurrentTemplateAC()->getTagProperty()->getTag();368// Check if drawing mode has to be shown369if ((shapeTag == SUMO_TAG_POLY) || (shapeTag == GNE_TAG_JPS_WALKABLEAREA) || (shapeTag == GNE_TAG_JPS_OBSTACLE)) {370myDrawingShape->showDrawingShape();371} else {372myDrawingShape->hideDrawingShape();373}374// Check if GEO POI Creator has to be shown375if (shapeTag == GNE_TAG_POIGEO) {376myGEOPOICreator->showGEOPOICreatorModule();377} else {378myGEOPOICreator->hideGEOPOICreatorModule();379}380} else {381// hide all widgets382myShapeAttributesEditor->hideAttributesEditor();383myDrawingShape->hideDrawingShape();384myGEOPOICreator->hideGEOPOICreatorModule();385}386}387388389bool390GNEShapeFrame::processClickPolygons(const Position& clickedPosition, bool& updateTemporalShape) {391if (myDrawingShape->isDrawing()) {392// add or delete a new point depending of flag "delete last created point"393if (myDrawingShape->getDeleteLastCreatedPoint()) {394myDrawingShape->removeLastPoint();395} else {396myDrawingShape->addNewPoint(clickedPosition);397}398// set temporal shape399updateTemporalShape = true;400return true;401} else {402return false;403}404}405406407bool408GNEShapeFrame::processClickPOI(SumoXMLTag POITag, const Position& clickedPosition) {409// show warning dialogbox and stop if input parameters are invalid410if (!myShapeAttributesEditor->checkAttributes(true)) {411return false;412}413// create baseShape object414createBaseShapeObject(POITag);415// obtain shape attributes and values416myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);417// add X-Y418myBaseShape->addDoubleAttribute(SUMO_ATTR_X, clickedPosition.x());419myBaseShape->addDoubleAttribute(SUMO_ATTR_Y, clickedPosition.y());420// set GEO Position as false (because we have created POI clicking over View421myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, false);422// declare additional handler423GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?424myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",425myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());426// build shape427additionalHandler.parseSumoBaseObject(myBaseShape);428// refresh shape attributes429myShapeAttributesEditor->refreshAttributesEditor();430// shape added, then return true431return true;432}433434435bool436GNEShapeFrame::processClickPOIGeo(const Position& clickedPosition) {437// show warning dialogbox and stop if input parameters are invalid438if (!myShapeAttributesEditor->checkAttributes(true)) {439return false;440}441// create baseShape object442createBaseShapeObject(SUMO_TAG_POI);443// obtain shape attributes and values444myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);445// convert position to cartesian446Position GEOPos = clickedPosition;447GeoConvHelper::getFinal().cartesian2geo(GEOPos);448// add X-Y in geo format449myBaseShape->addDoubleAttribute(SUMO_ATTR_LON, GEOPos.x());450myBaseShape->addDoubleAttribute(SUMO_ATTR_LAT, GEOPos.y());451// set GEO Position as false (because we have created POI clicking over View452myBaseShape->addBoolAttribute(SUMO_ATTR_GEO, true);453// declare additional handler454GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?455myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",456myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());457// build shape458additionalHandler.parseSumoBaseObject(myBaseShape);459// refresh shape attributes460myShapeAttributesEditor->refreshAttributesEditor();461// shape added, then return true462return true;463}464465466bool467GNEShapeFrame::processClickPOILanes(const GNEViewNetHelper::ViewObjectsSelector& viewObjects) {468// abort if lane is nullptr469if (viewObjects.getLaneFront() == nullptr) {470WRITE_WARNING(TL("POILane can be only placed over lanes"));471return false;472}473// show warning dialogbox and stop if input parameters are invalid474if (!myShapeAttributesEditor->checkAttributes(true)) {475return false;476}477// create baseShape object478createBaseShapeObject(SUMO_TAG_POI);479// obtain shape attributes and values480myShapeAttributesEditor->fillSumoBaseObject(myBaseShape);481// obtain Lane482myBaseShape->addStringAttribute(SUMO_ATTR_LANE, viewObjects.getLaneFront()->getID());483// obtain position over lane484myBaseShape->addDoubleAttribute(SUMO_ATTR_POSITION, viewObjects.getLaneFront()->getLaneShape().nearest_offset_to_point2D(485myViewNet->snapToActiveGrid(myViewNet->getPositionInformation())) /486viewObjects.getLaneFront()->getLengthGeometryFactor());487// declare additional handler488GNEAdditionalHandler additionalHandler(myViewNet->getNet(), myBaseShape->hasStringAttribute(GNE_ATTR_ADDITIONAL_FILE) ?489myBaseShape->getStringAttribute(GNE_ATTR_ADDITIONAL_FILE) : "",490myViewNet->getViewParent()->getGNEAppWindows()->isUndoRedoAllowed());491// build shape492additionalHandler.parseSumoBaseObject(myBaseShape);493// refresh shape attributes494myShapeAttributesEditor->refreshAttributesEditor();495// shape added, then return true496return true;497}498499/****************************************************************************/500501502