viernes, 25 de diciembre de 2020

Producir polígonos por dragado del Map Canvas con PyQGIS 3

En posts anteriores (1, 2) se introdujo la producción de rectángulos y círculos por dragado del Map Canvas con PyQgis 3. En el actual generalizamos el código para que este abarque polígonos regulares de más de tres lados (en el __init__ de la clase se define el número de lados).

El código se detalla a continuación para polígonos de 5 lados:

 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
import numpy as np
import math

map_canvas = iface.mapCanvas()

class dragPolygon(QgsMapToolEmitPoint):
    def __init__(self, canvas):
        self.canvas = canvas
        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)
        self.polygonSides = 5
        
    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()
            z = self.get_pol_par(self.start_point, end_point, self.polygonSides).asWkt()
            self.rubber_band.setToGeometry(QgsGeometry.fromWkt(z), None)
                
    def canvasReleaseEvent(self, e):
        self.end_point = self.toMapCoordinates(e.pos())
        p = self.get_pol(self.polygonSides)
        self.rubber_band.reset()
        self.start_point = None
        self.end_point = None

        pol_feat = p
        
        #definng Status Bar
        statusBar = 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,
                                   'polygon',
                                   'memory')

        prov = mem_layer.dataProvider()

        feat = QgsFeature()
        feat.setAttributes([0])
        feat.setGeometry(QgsGeometry.fromWkt(pol_feat))
        prov.addFeatures([feat])
        QgsProject.instance().addMapLayer(mem_layer)
      
    def get_pol_par(self, startPoint, endPoint, polygonSides):
        diameter = math.sqrt((startPoint.x() - endPoint.x())**2 + (startPoint.y() - endPoint.y())**2)
        point = QgsPointXY(startPoint)
        geom = QgsGeometry.fromPolygonXY([[ QgsPointXY(point.x() + np.sin(angle)*diameter, point.y() + np.cos(angle)*diameter)
                        for angle in np.linspace(0, 2*np.pi, polygonSides, endpoint = False) ]])
        return geom
    
    def get_pol(self, polygonSides):
        diameter = math.sqrt((self.start_point.x() - self.end_point.x())**2 + (self.start_point.y() - self.end_point.y())**2)
        point = QgsPointXY(self.start_point)
        geom = QgsGeometry.fromPolygonXY([[ QgsPointXY(point.x() + np.sin(angle)*diameter, point.y() + np.cos(angle)*diameter)
                        for angle in np.linspace(0, 2*np.pi, polygonSides, endpoint = False) ]])

        return geom.asWkt()
        
T = dragPolygon(map_canvas)
map_canvas.setMapTool(T)
#T.deactivated.connect(lambda: print('Tool deactivated'))

La ejecución del código anterior en la Python Console de QGIS producirá la rubber band de la imagen siguiente cuando se draga el ratón desde un punto cercano al centro del ráster.

Cuando se liberá el cursor, el rasgo con forma pentagonal se posiciona en la misma zona que corresponde a la última rubber band que se produce de manera interactiva durante el dragado con el mouse. Es lo que se observa en la imagen siguiente.

No hay comentarios: