jueves, 16 de enero de 2020

Clase plantilla con QDialog para crear círculos

En el post anterior se introdujo una Clase plantilla con QDialog para crear puntos (como memory layers) a partir de las coordenadas del Map Canvas de QGIS obtenidas con un click del ratón. En este post se van a aprovechar estas coordenadas para crear un buffer circular, dado el radio, que se pide a través de un QInputDialog que se genera en la misma función de creación del buffer circular.

El código completo se expone a continuación:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
from PyQt5.QtCore import Qt
from qgis.gui import QgsMapToolEmitPoint

class Dlg(QDialog):

    def __init__(self):
        QDialog.__init__(self)
        self.layout = QGridLayout(self)
        self.label1 = QLabel('Coordinates of map: ')
        self.label2 = QLabel('Select Projection: ')
        self.label3 = QLabel('Create Point: ')
        self.label4 = QLabel('Create Circle: ')
        self.line_edit = QLineEdit()
        self.line_edit.setFixedWidth(350)
        self.sel_proj = QgsProjectionSelectionWidget()
        proj = QgsProject.instance().crs().postgisSrid()
        crs = QgsCoordinateReferenceSystem()
        crs.createFromSrid(proj)
        self.sel_proj.setCrs(crs)
        self.btn1 = QPushButton('OK', self)
        self.btn1.setFixedWidth(100)
        self.btn2 = QPushButton('OK', self)
        self.btn2.setFixedWidth(100)

        # Save reference to the QGIS interface
        self.iface = iface
        
        # a reference to our map canvas
        self.canvas = self.iface.mapCanvas() 
        # this QGIS tool emits as QgsPoint after each click on the map canvas
        self.pointTool = QgsMapToolEmitPoint(self.canvas)

        self.layout.addWidget(self.label1, 0, 0)
        self.layout.addWidget(self.line_edit, 0, 1)
        self.layout.addWidget(self.label2, 2, 0)
        self.layout.addWidget(self.sel_proj, 2, 1)
        self.layout.addWidget(self.label3, 3, 0)
        self.layout.addWidget(self.btn1, 3, 1)
        self.layout.addWidget(self.label4, 4, 0)
        self.layout.addWidget(self.btn2, 4, 1)
        
        self.pointTool.canvasClicked.connect(self.display_point)
        self.canvas.setMapTool(self.pointTool)
        
        self.btn1.clicked.connect(self.create_point)
        self.btn2.clicked.connect(self.create_circle)

    def display_point(self, point, button):
        # report map coordinates from a canvas click
        coords = "{}, {}".format(point.x(), point.y())
        self.line_edit.setText(str(coords))
    
    def create_point(self):
        
        pt = self.line_edit.text().split(',')
        
        try:
            x = float(pt[0])
            y = float(pt[1])
            point = QgsPointXY(x,y)

            epsg = self.sel_proj.crs().postgisSrid()
            
            uri = "Point?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"

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

            prov = mem_layer.dataProvider()

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

        except ValueError:
            pass

    def create_circle(self):
        
        msg = u"radius?"
        
        dlg = QInputDialog()
     
        d = dlg.getDouble(None, "Create Circle", msg, decimals= 5)
        
        pt = self.line_edit.text().split(',')
        
        try:
            x = float(pt[0])
            y = float(pt[1])
            point = QgsPointXY(x,y)
            
            if d[0] != 0:
            
                geom = QgsGeometry.fromPointXY(point).buffer(d[0], -1).asWkt()

                epsg = self.sel_proj.crs().postgisSrid()
                
                uri = "Polygon?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"

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

                prov = mem_layer.dataProvider()

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

        except ValueError:
            pass

w = Dlg()
w.setWindowTitle('Geometry Creator from Point')
w.setWindowFlags(Qt.WindowStaysOnTopHint)
w.move(300,200)
w.show()

Cuando se ejecuta el código anterior en la Python Console de QGIS 3, con el ráster de fondo, se obtiene el resultado de la imagen siguiente:


A continuación, se hace click en un punto arbitrario del ráster para registrar las coordenadas, posteriormente click en el botón de "Create Circle" para luego digitar 10000 (radio en metros) en el QFileDialog; tal como se presenta en la imagen a continuación.


Después de hacer click en OK, el círculo resultante (como memory layer) puede ser observado en la imagen siguiente:


No hay comentarios: