martes, 29 de diciembre de 2020

Adaptar un plugin plantilla tipo botón checkeable para producir círculos por dragado del Map Canvas en QGIS 3

En el post anterior se adaptó un plugin plantilla, tipo botón checkeable, para producir rectángulos por dragado del Map Canvas en QGIS 3. Ahora, se va a adaptar la misma plantilla pero para producir círculos por dragado del Map Canvas. El código completo colocado en el archivo template.py se muestra a continuación. Sin embargo, para que funcione adecuadamente se requieren otras modificaciones que se detallan más adelante.

"""
/***************************************************************************
 Test
                                 A QGIS plugin
 Plugin for testing things
                              -------------------
        begin                : 2015-01-28
        copyright            : (C) 2015-2017 by German Carrillo, GeoTux
        email                : gcarrillo@linuxmail.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
import os, math

# Import the PyQt and QGIS libraries
from qgis.core import (QgsApplication, QgsRasterLayer, QgsRaster, QgsPointXY, QgsPoint, QgsCircle,
                       QgsGeometry, QgsRectangle, QgsProject, QgsVectorLayer, QgsFeature)
from qgis.gui import QgsMapToolEmitPoint, QgsRubberBand
from PyQt5.QtCore import QObject, QTimer, Qt
from PyQt5.QtWidgets import ( QMessageBox, QAction, QMenu, QActionGroup,
                              QWidgetAction, QToolTip)
from PyQt5.QtGui import ( QIcon )

class Test:

    def __init__(self, iface):
        # Save reference to the QGIS interface
        self.iface = iface
        self.tooltipRaster = TooltipRasterMapTool( self.iface )

    def initGui(self):
        # Create action that will start plugin configuration
        self.action = QAction( QIcon( os.path.dirname(os.path.realpath(__file__)) +
            "/icon_default.png" ), "Making Circles", self.iface.mainWindow() )
        # connect the action to the run method
        self.action.triggered.connect( self.run )
        self.action.setCheckable( True )

        self.iface.mapCanvas().mapToolSet.connect( self.mapToolWasSet )

        # Add toolbar button to the Plugins toolbar
        self.iface.addToolBarIcon(self.action)
        # Add menu item to the Plugins menu
        self.iface.addPluginToMenu("&Tooltip for Raster Values", self.action)

    def unload(self):
        # Remove the plugin menu item and icon
        self.iface.removePluginMenu( "&Tooltip for Raster Values", self.action )
        self.iface.removeToolBarIcon(self.action)

    # run method that performs all the real work
    def run(self, checked):
        if checked:
            self.iface.mapCanvas().setMapTool( self.tooltipRaster )
        else:
            self.iface.mapCanvas().unsetMapTool( self.tooltipRaster )

    def mapToolWasSet( self, newTool ):
        if type(newTool) != TooltipRasterMapTool:
            self.action.setChecked( False )


class TooltipRasterMapTool(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        self.canvas = self.iface.mapCanvas()
        QgsMapToolEmitPoint.__init__(self, self.canvas)
        self.start_point = None
        self.end_point = None
        self.rubber_band = QgsRubberBand(self.canvas, True)
        self.rubber_band.setColor(Qt.red)
        self.rubber_band.setOpacity(0.7)

    def canvasPressEvent(self, e):
        self.start_point = self.toMapCoordinates(e.pos())
        self.end_point = self.start_point

    def canvasMoveEvent(self, e):
        if self.start_point:
            end_point = self.toMapCoordinates(e.pos())
            self.rubber_band.reset()
            q = self.get_circ_par(self.start_point, end_point)
            h = QgsCircle(q[0], q[1]).toPolygon().asWkt()
            self.rubber_band.setToGeometry(QgsGeometry.fromWkt(h), None)

    def canvasReleaseEvent(self, e):
        self.end_point = self.toMapCoordinates(e.pos())
        r = self.get_circle()
        self.rubber_band.reset()
        self.start_point = None
        self.end_point = None

        circ_feat = r
        
        #definng Status Bar
        statusBar = self.iface.mainWindow().statusBar()
        #selecting elements in Status Bar
        elements = statusBar.children()
        #selecting widget in Status Bar with ESPG
        EPSG = elements[1].children()[10]
        EPSG = EPSG.text().split(':')

        uri = "Polygon?crs=epsg:" + EPSG[1] + "&field=id:integer""&index=yes"

        mem_layer = QgsVectorLayer(uri,
                                   'circle',
                                   'memory')

        prov = mem_layer.dataProvider()

        feat = QgsFeature()
        feat.setAttributes([0])
        feat.setGeometry(QgsGeometry.fromWkt(circ_feat))
        prov.addFeatures([feat])
        QgsProject.instance().addMapLayer(mem_layer)

    def get_circ_par(self, startPoint, endPoint):
        par = [QgsPoint(startPoint.x(), startPoint.y()), 
                math.sqrt((startPoint.x() - endPoint.x())**2 + (startPoint.y() - endPoint.y())**2)]
        return par

    def get_circle(self):
        dist = math.sqrt((self.start_point.x() - self.end_point.x())**2 + (self.start_point.y() - self.end_point.y())**2)
        circ = QgsCircle(QgsPoint(self.start_point), dist).toPolygon().asWkt()
        return circ

La carpeta completa de la plantilla (template) se guarda con el nombre de makingcircles en la ruta de plugins de QGIS 3. El archivo template.py que tiene el código anterior se renombra como making_circles.py. Para que lo procese de la manera esperada se hace la modificación siguiente en el __init__py.

.
.
.
def classFactory(iface):
    # load Test class from file making_circles.py
    from .making_circles import Test
    return Test(iface)

También necesitamos la modificación siguiente en el metadata.txt para que el nombre del plugin sea procesado como se espera. Una modificación similar también fue realizada en el código fuente (making_circles.py) al inicio del método 'initGui'.

; the next section is mandatory
[general]
name=Making Circles plugin
description=For Making Circles
.
.
.

Finalmente, se produce un icono personalizado 24x24 (con el mismo nombre) para identificarlo de manera precisa. La ejecución del plugin se observa en la imagen a continuación.

No hay comentarios: