Example #1
0
class ShapeTool(QgsMapTool):
    #signal emitted when the mouse is clicked. This indicates that the tool finished its job
    toolFinished = pyqtSignal()
    def __init__(self, canvas, geometryType, param, type, color = QColor( 254, 178, 76, 63 )):
        """
        Constructor
        """
        QgsMapTool.__init__(self, canvas)
        self.canvas = canvas
        self.active = False
        self.geometryType = self.tr(geometryType)
        self.param=param
        self.type=type       
        self.cursor=None
        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)     
        self.setColor(color)
        self.reset()
        self.rotAngle = 0
        self.currentCentroid = None
        self.rotate = False
    
    def setColor(self, mFillColor):
        """
        Adjusting the color to create the rubber band
        """
    
        self.rubberBand.setColor(mFillColor)
        self.rubberBand.setWidth(1)
    
    def reset(self):
        """
        Resetting the rubber band
        """
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        try:
            self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
        except:
            pass

    def rotateRect(self, centroid, e):
        """
        Calculates the angle for the rotation.
        """
        item_position = self.canvas.mapToGlobal(e.pos())
        c = self.toCanvasCoordinates(centroid)
        c = self.canvas.mapToGlobal(c)
        rotAngle = pi - atan2(
            item_position.y() - c.y(), item_position.x() - c.x())
        return rotAngle

    def canvasPressEvent(self, e):
        """
        When the canvas is pressed the tool finishes its job
        """
        # enforce mouse restoring if clicked right after rotation 
        QApplication.restoreOverrideCursor()
        self.canvas.unsetMapTool(self)
        self.toolFinished.emit()

    def _baseDistanceInMeters(self):
        """
        Calculates the distance in meters of 2 points 1 unit map away on
        current canvas CRS.
        :return: (float) distance in meters between two points 1 map unit apart
                 from each other.
        """
        source_crs = self.canvas.mapSettings().destinationCrs()
        dest_crs = QgsCoordinateReferenceSystem(3857)
        tr = QgsCoordinateTransform(
            source_crs, dest_crs, QgsCoordinateTransformContext())
        p1t = QgsGeometry().fromPointXY(QgsPointXY(1, 0))
        p1t.transform(tr)
        p2t = QgsGeometry().fromPointXY(QgsPointXY(0, 0))
        p2t.transform(tr)
        return QgsDistanceArea().measureLine(p1t.asPoint(), p2t.asPoint())

    def getAdjustedSize(self, size):
        """
        If map unit is not metric, the figure to be drawn needs to have its
        size adjusted. This is necessary because input parameters are designed
        to be meters on tool's GUI.
        :param size: (float) tool's radius/length reference size in meters.
        :return: (float)  
        """
        source_crs = self.canvas.mapSettings().destinationCrs()
        if source_crs.mapUnits() != QgsUnitTypes.DistanceMeters:
            return size / self._baseDistanceInMeters()
        return size

    def canvasMoveEvent(self, e):
        """
        Deals with mouse move event to update the rubber band position in the canvas
        """
        ctrlIsHeld = QApplication.keyboardModifiers() == Qt2.ControlModifier
        if e.button() != None and not ctrlIsHeld:
            if self.rotate:
                # change rotate status
                self.rotate = False
            QApplication.restoreOverrideCursor()
            self.endPoint = self.toMapCoordinates( e.pos() )
        elif e.button() != None and ctrlIsHeld \
            and self.geometryType == self.tr(u"Square"):
            # calculate angle between mouse and last rubberband centroid before holding control
            self.rotAngle = self.rotateRect(self.currentCentroid, e)
            if not self.rotate:
                # only override mouse if it is not overriden already
                QApplication.setOverrideCursor(QCursor(Qt2.BlankCursor))
                self.rotate = True
        if self.geometryType == self.tr(u"Circle"):
                self.showCircle(self.endPoint)
        elif self.geometryType == self.tr(u"Square"):
            self.showRect(self.endPoint, sqrt(self.param)/2, self.rotAngle)
    
    def showCircle(self, startPoint):
        """
        Draws a circle in the canvas
        """
        nPoints = 50
        x = startPoint.x()
        y = startPoint.y()
        if self.type == self.tr('distance'):
            r = self.getAdjustedSize(self.param)
            self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
            for itheta in range(nPoints+1):
                theta = itheta*(2.0*pi/nPoints)
                self.rubberBand.addPoint(QgsPointXY(x+r*cos(theta), y+r*sin(theta)))
            self.rubberBand.show()
        else:
            r = self.getAdjustedSize(sqrt(self.param/pi))
            self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
            for itheta in range(nPoints+1):
                theta = itheta*(2.0*pi/nPoints)
                self.rubberBand.addPoint(QgsPointXY(x+r*cos(theta), y+r*sin(theta)))
            self.rubberBand.show()

    def showRect(self, startPoint, param, rotAngle=0):
        """
        Draws a rectangle in the canvas
        """  
        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
        x = startPoint.x() # center point x
        y = startPoint.y() # center point y
        # rotation angle is always applied in reference to center point
        # to avoid unnecessary calculations
        c = cos(rotAngle)
        s = sin(rotAngle)
        # translating coordinate system to rubberband centroid
        param = self.getAdjustedSize(param)
        for posx, posy in ((-1, -1), (-1, 1), (1, 1), (1, -1)):
            px = posx * param
            py = posy * param
            pnt = QgsPointXY(px * c - py * s + x, py * c + px * s + y)
            self.rubberBand.addPoint(pnt, False)
        self.rubberBand.setVisible(True)
        self.rubberBand.updateRect()
        self.rubberBand.update()
        self.rubberBand.show()
        self.currentCentroid = startPoint
        
    def deactivate(self):
        """
        Deactivates the tool and hides the rubber band
        """
        self.rubberBand.hide()
        QgsMapTool.deactivate(self)
        # restore mouse in case tool is disabled right after rotation
        QApplication.restoreOverrideCursor()
        
    def activate(self):
        """
        Activates the tool
        """
        QgsMapTool.activate(self)