예제 #1
0
 def __init__(self, iface, parent=None):
     """
     Class constructor.
     """
     # super(QgsRasterLayer, self).__init__()
     self.canvas = iface.mapCanvas()
     super(DsgRasterInfoTool, self).__init__(parent)
     self.setupUi(self)
     self.bandTooltipButton.setToolTip(self.tr("Show raster tooltip"))
     self.dynamicHistogramButton.setToolTip(
         self.tr("Dynamic histogram view"))
     self.valueSetterButton.setToolTip(
         self.
         tr("Set raster value from mouse click\nShift + Left Click + Mouse Drag: Selects a set of points and assigns raster value for each point"
            ))
     self.assignBandValueTool = None
     self.parent = parent
     self.splitter.hide()
     self.iface = iface
     self.timerMapTips = QTimer(self.canvas)
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     self.addShortcuts()
     self.valueSetterButton.setEnabled(False)
     self.iface.mapCanvas().currentLayerChanged.connect(
         self.enableAssignValue)
     self.iface.actionToggleEditing().triggered.connect(
         self.enableAssignValue)
     # start currentLayer selection
     self.currentLayer = None
    def __init__(self, postgisDb, iface, instantiating=False):
        """
        Constructor
        """
        super(IdentifyOutOfBoundsAnglesProcess,
              self).__init__(postgisDb, iface, instantiating)
        self.processAlias = self.tr('Identify Out Of Bounds Angles')
        self.geometryHandler = DsgGeometryHandler(iface,
                                                  parent=iface.mapCanvas())

        if not self.instantiating:
            # getting tables with elements
            self.classesWithElemDict = self.abstractDb.getGeomColumnDictV2(
                primitiveFilter=['a', 'l'],
                withElements=True,
                excludeValidation=True)
            # adjusting process parameters
            interfaceDictList = []
            for key in self.classesWithElemDict:
                cat, lyrName, geom, geomType, tableType = key.split(',')
                interfaceDictList.append({
                    self.tr('Category'): cat,
                    self.tr('Layer Name'): lyrName,
                    self.tr('Geometry\nColumn'): geom,
                    self.tr('Geometry\nType'): geomType,
                    self.tr('Layer\nType'): tableType
                })
            self.parameters = {
                'Angle': 10.0,
                'Classes': interfaceDictList,
                'Only Selected': False
            }
예제 #3
0
 def __init__(self, canvas, iface):
     self.iface = iface
     self.canvas = canvas
     self.toolAction = None
     QgsMapTool.__init__(self, self.canvas)
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     self.iface.currentLayerChanged.connect(self.setToolEnabled)
     self.iface.actionToggleEditing().triggered.connect(self.setToolEnabled)
예제 #4
0
 def __init__(self, iface, parent):
     """
     Class constructor.
     """
     # super(QgsRasterLayer, self).__init__()
     self.canvas = iface.mapCanvas()
     super(BandValueTool, self).__init__(self.canvas)
     self.parent = parent
     self.iface = iface
     self.toolAction = None
     self.QgsMapToolEmitPoint = QgsMapToolEmitPoint(self.canvas)
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     self.timerMapTips = QTimer( self.canvas )
     self.timerMapTips.timeout.connect( self.showToolTip )
     self.activated = False
예제 #5
0
 def __init__(self, iface, rasterLayer):
     """
     Tool Behaviours: (all behaviours start edition, except for rectangle one)
     1- Left Click: Creates a new point feature with the value from raster, according to selected attribute. 
     5- Shift + drag and drop: draws a rectangle, then features that intersect this rectangle are selected 
     and their value is set according to raster value and selected attribute.
     """
     self.iface = iface        
     self.canvas = self.iface.mapCanvas()
     QgsMapTool.__init__(self, self.canvas)
     self.toolAction = None
     self.qgsMapToolEmitPoint = QgsMapToolEmitPoint(self.canvas)
     self.dsgGeometryHandler = DsgGeometryHandler(iface)
     self.rasterLayer = rasterLayer
     self.setRubberbandParameters()
     self.reset()
     self.auxList = []
     self.decimals = self.getDecimals()
 def __init__(self, iface, parent=None):
     """
     Class constructor.
     """
     # super(QgsRasterLayer, self).__init__()
     self.canvas = iface.mapCanvas()
     super(DsgRasterInfoTool, self).__init__(parent)
     self.setupUi(self)
     self.assignBandValueTool = None
     self.parent = parent
     self.splitter.hide()
     self.iface = iface
     self.timerMapTips = QTimer(self.canvas)
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     self.addShortcuts()
     self.valueSetterButton.setEnabled(False)
     self.iface.mapCanvas().currentLayerChanged.connect(
         self.enableAssignValue)
     self.iface.actionToggleEditing().triggered.connect(
         self.enableAssignValue)
예제 #7
0
 def __init__(self, iface, parent = None):
     """
     Class constructor.
     """
     # super(QgsRasterLayer, self).__init__()
     self.canvas = iface.mapCanvas()
     super(DsgRasterInfoTool, self).__init__(parent)
     self.setupUi(self)
     self.bandTooltipButton.setToolTip(self.tr("Show raster tooltip"))
     self.dynamicHistogramButton.setToolTip(self.tr("Dynamic histogram view"))
     self.valueSetterButton.setToolTip(self.tr("Set raster value from mouse click\nShift + Left Click + Mouse Drag: Selects a set of points and assigns raster value for each point"))
     self.assignBandValueTool = None
     self.parent = parent
     self.splitter.hide()
     self.iface = iface
     self.timerMapTips = QTimer( self.canvas )
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     self.addShortcuts()
     self.valueSetterButton.setEnabled(False)
     self.iface.mapCanvas().currentLayerChanged.connect(self.enableAssignValue)
     self.iface.actionToggleEditing().triggered.connect(self.enableAssignValue)
     # start currentLayer selection
     self.currentLayer = None
예제 #8
0
class DsgRasterInfoTool(QWidget, Ui_DsgRasterInfoTool):
    """
    This class is supposed to help revision operators. It shows, on mouse hovering
    raster layer's band values. For a MDS product, altimetry is, then, given.
    Tool Behaviour:
    1- On hoverring a pixel: expose band value(s)
    2- On mouse click: create a new instance of desired layer (filled on config).
        * behaviour 2 is an extrapolation of first conception
    """
    def __init__(self, iface, parent = None):
        """
        Class constructor.
        """
        # super(QgsRasterLayer, self).__init__()
        self.canvas = iface.mapCanvas()
        super(DsgRasterInfoTool, self).__init__(parent)
        self.setupUi(self)
        self.bandTooltipButton.setToolTip(self.tr("Show raster tooltip"))
        self.dynamicHistogramButton.setToolTip(self.tr("Dynamic histogram view"))
        self.valueSetterButton.setToolTip(self.tr("Set raster value from mouse click\nShift + Left Click + Mouse Drag: Selects a set of points and assigns raster value for each point"))
        self.assignBandValueTool = None
        self.parent = parent
        self.splitter.hide()
        self.iface = iface
        self.timerMapTips = QTimer( self.canvas )
        self.DsgGeometryHandler = DsgGeometryHandler(iface)
        self.addShortcuts()
        self.valueSetterButton.setEnabled(False)
        self.iface.mapCanvas().currentLayerChanged.connect(self.enableAssignValue)
        self.iface.actionToggleEditing().triggered.connect(self.enableAssignValue)
        # start currentLayer selection
        self.currentLayer = None

    def resetEditingSignals(self, currentLayer):
        """
        Disconnects editing signal from previously selected layer and connects it to newly selected layer.
        Method is called whenever currentlLayerChanged signal is emitted.
        """
        # get previous selected layer
        prevLayer = self.currentLayer
        # update current selected layer
        self.currentLayer = currentLayer
        activateAlias = lambda : self.activateValueSetter(True)
        deactivateAlias = lambda : self.activateValueSetter(False)
        if prevLayer:
            try:
                # if there was a previous selection, signals must be disconnected from it before connecting to the new layer
                prevLayer.editingStarted.disconnect(activateAlias)
                prevLayer.editingStopped.disconnect(deactivateAlias)
            except:
                # in case signal is not yet connected, somehow
                pass
        # connecting signals to new layer
        if isinstance(self.currentLayer, QgsVectorLayer):
            self.currentLayer.editingStarted.connect(activateAlias)
            self.currentLayer.editingStopped.connect(deactivateAlias)

    def add_action(self, icon_path, text, callback, parent=None):
        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        if parent:
            parent.addAction(action)
        return action

    def addShortcuts(self):
        icon_path = ':/plugins/DsgTools/icons/rasterToolTip.png'
        text = self.tr('DSGTools: Raster information tool')
        self.activateToolAction = self.add_action(icon_path, text, self.rasterInfoPushButton.toggle, parent = self.parent)
        self.iface.registerMainWindowAction(self.activateToolAction, '')
        icon_path = ':/plugins/DsgTools/icons/band_tooltip.png'
        text = self.tr('DSGTools: Band tooltip')
        self.bandTooltipButtonAction = self.add_action(icon_path, text, self.bandTooltipButton.toggle, parent = self.parent)
        self.iface.registerMainWindowAction(self.bandTooltipButtonAction, '')
        icon_path = ':/plugins/DsgTools/icons/histogram.png'
        text = self.tr('DSGTools: Dynamic Histogram Viewer')
        self.dynamicHistogramButtonAction = self.add_action(icon_path, text, self.dynamicHistogramButton.toggle, parent = self.parent)
        self.iface.registerMainWindowAction(self.dynamicHistogramButtonAction, '')
        icon_path = ':/plugins/DsgTools/icons/valueSetter.png'
        text = self.tr('DSGTools: Set Value From Point')
        self.valueSetterButtonAction = self.add_action(icon_path, text, self.valueSetterButton.toggle, parent = self.parent)
        self.iface.registerMainWindowAction(self.valueSetterButtonAction, '')
        # self.timerMapTips.timeout.connect( self.showToolTip )

    def enableAssignValue(self):
        layer = self.iface.mapCanvas().currentLayer()
        if layer and isinstance(layer, QgsVectorLayer):
            if layer.geometryType() == QGis.Point and layer.isEditable():
                self.valueSetterButton.setEnabled(True)
                # reset editing signals
                self.resetEditingSignals(currentLayer=layer)
            else:
                self.valueSetterButton.setEnabled(False)
                self.valueSetterButton.setChecked(False)
                self.activateValueSetter(False)
        else:
            self.valueSetterButton.setEnabled(False)
            self.valueSetterButton.setChecked(False)
            self.activateValueSetter(False)

    def deactivate(self):
        self.activateBandValueTool(False)
        self.activateStretchTool(False)
        self.activateValueSetter(False)

    @pyqtSlot(bool, name = 'on_rasterInfoPushButton_toggled')
    def toggleBar(self, toggled=None):
        """
        Shows/Hides the tool bar
        """
        if toggled is None:
            toggled = self.rasterInfoPushButton.isChecked()
        if toggled:
            self.splitter.show()
        else:
            self.splitter.hide()      
    
    @pyqtSlot(bool, name = 'on_bandTooltipButton_toggled')
    def activateBandValueTool(self, state):
        if state:
            self.iface.mapCanvas().xyCoordinates.connect(self.showToolTip)
        else:
            self.iface.mapCanvas().xyCoordinates.disconnect(self.showToolTip)
    
    @pyqtSlot(bool, name = 'on_dynamicHistogramButton_toggled')
    def activateStretchTool(self, state):
        if state:
            self.iface.mapCanvas().extentsChanged.connect(self.stretch_raster)
        else:
            self.iface.mapCanvas().extentsChanged.disconnect(self.stretch_raster)
    
    @pyqtSlot(bool, name = 'on_valueSetterButton_toggled')
    def activateValueSetter(self, state):
        if state:
            raster = self.rasterComboBox.currentLayer()
            self.loadTool(self.iface, raster)
        else:
            self.unloadTool()
    
    def loadTool(self, iface, raster):
        self.assignBandValueTool = AssignBandValueTool(self.iface, raster)
        self.assignBandValueTool.activate()
    
    def unloadTool(self):
        if self.assignBandValueTool:
            self.assignBandValueTool.deactivate()
        self.assignBandValueTool = None

    def stretch_raster(self):
        try:
            formerLayer = self.iface.activeLayer()
            layer = self.rasterComboBox.currentLayer()
            self.iface.mapCanvas().currentLayerChanged.disconnect(self.enableAssignValue)
            self.iface.setActiveLayer(layer)
            self.iface.mainWindow().findChild( QAction, 'mActionLocalCumulativeCutStretch' ).trigger()
            self.iface.setActiveLayer(formerLayer)
            self.iface.mapCanvas().currentLayerChanged.connect(self.enableAssignValue)
        except AttributeError:
            pass
    
    def getPixelValue(self, mousePos, rasterLayer):
        """
        
        """
        rasterCrs = rasterLayer.crs()
        mousePosGeom = QgsGeometry.fromPoint(mousePos)
        canvasCrs = self.canvas.mapRenderer().destinationCrs()
        self.DsgGeometryHandler.reprojectFeature(mousePosGeom, rasterCrs, canvasCrs)
        mousePos = mousePosGeom.asPoint()
        # identify pixel(s) information
        i = rasterLayer.dataProvider().identify( mousePos, QgsRaster.IdentifyFormatValue )
        if i.isValid():
            text = ", ".join(['{0:g}'.format(r) for r in i.results().values() if r is not None] )
        else:
            text = ""
        return text

    def showToolTip(self, qgsPoint):
        """
        
        """
        self.timerMapTips.stop()
        self.timerMapTips.start( 6000 ) # time in milliseconds
        if self.canvas.underMouse():
            raster = self.rasterComboBox.currentLayer()
            if raster:
                text = self.getPixelValue(qgsPoint, raster)
                p = self.canvas.mapToGlobal( self.canvas.mouseLastXY() )
                QToolTip.showText( p, text, self.canvas )
예제 #9
0
class IdentifyOutOfBoundsAnglesProcess(ValidationProcess):
    def __init__(self, postgisDb, iface, instantiating=False):
        """
        Constructor
        """
        super(IdentifyOutOfBoundsAnglesProcess,self).__init__(postgisDb, iface, instantiating)
        self.processAlias = self.tr('Identify Out Of Bounds Angles')
        self.geometryHandler = DsgGeometryHandler(iface, parent = iface.mapCanvas())
        
        if not self.instantiating:
            # getting tables with elements
            self.classesWithElemDict = self.abstractDb.getGeomColumnDictV2(primitiveFilter=['a', 'l'], withElements=True, excludeValidation = True)
            # adjusting process parameters
            interfaceDictList = []
            for key in self.classesWithElemDict:
                cat, lyrName, geom, geomType, tableType = key.split(',')
                interfaceDictList.append({self.tr('Category'):cat, self.tr('Layer Name'):lyrName, self.tr('Geometry\nColumn'):geom, self.tr('Geometry\nType'):geomType, self.tr('Layer\nType'):tableType})
            self.parameters = {'Angle': 10.0, 'Classes': interfaceDictList, 'Only Selected':False}
    
    def getOutOfBoundsAngleInPolygon(self, feat, geometry_column, part, angle, outOfBoundsList):
        for linearRing in part.asPolygon():
            linearRing = self.geometryHandler.getClockWiseList(linearRing)
            nVertex = len(linearRing)-1
            for i in xrange(nVertex):
                if i == 0:
                    vertexAngle = (linearRing[i].azimuth(linearRing[-2]) - linearRing[i].azimuth(linearRing[i+1]) + 360)
                else:
                    vertexAngle = (linearRing[i].azimuth(linearRing[i-1]) - linearRing[i].azimuth(linearRing[i+1]) + 360)
                vertexAngle = math.fmod(vertexAngle, 360)
                if vertexAngle > 180:
                    # if angle calculated is the outter one
                    vertexAngle = 360 - vertexAngle
                if vertexAngle < angle:
                    geomDict = {'angle':vertexAngle,'feat_id':feat.id(), 'geometry_column': geometry_column, 'geom':QgsGeometry.fromPoint(linearRing[i])}
                    outOfBoundsList.append(geomDict)
    
    def getOutOfBoundsAngleInLine(self, feat, geometry_column, part, angle, outOfBoundsList):
        line = part.asPolyline()
        nVertex = len(line)-1
        for i in xrange(1,nVertex):
            vertexAngle = (line[i].azimuth(line[i-1]) - line[i].azimuth(line[i+1]) + 360)
            vertexAngle = math.fmod(vertexAngle, 360)
            if vertexAngle > 180:
                vertexAngle = 360 - vertexAngle
            if vertexAngle < angle:
                geomDict = {'angle':vertexAngle,'feat_id':feat.id(), 'geometry_column': geometry_column, 'geom':QgsGeometry.fromPoint(line[i])}
                outOfBoundsList.append(geomDict)
    
    def getOutOfBoundsAngle(self, feat, angle, geometry_column):
        outOfBoundsList = []
        geom = feat.geometry()
        for part in geom.asGeometryCollection():
            if part.type() == QGis.Polygon:
                self.getOutOfBoundsAngleInPolygon(feat, geometry_column, part, angle, outOfBoundsList)
            if part.type() == QGis.Line:
                self.getOutOfBoundsAngleInLine(feat, geometry_column, part, angle, outOfBoundsList)            
        return outOfBoundsList
    
    def getOutOfBoundsAngleList(self, lyr, angle, geometry_column, onlySelected = False):
        featureList, size = self.getFeatures(lyr, onlySelected = onlySelected)
        outOfBoundsList = []
        for feat in featureList:
            outOfBoundsList += self.getOutOfBoundsAngle(feat, angle, geometry_column)
        return outOfBoundsList
    
    def buildAndRaiseOutOfBoundsFlag(self, tableSchema, tableName, flagLyr, geomDictList):
        """
        
        """
        featFlagList = []
        for geomDict in geomDictList:
            # reason = self.tr('Angle of {0} degrees is out of bound.').format(geomDict['angle'])
            reason = self.tr('Angle out of bounds ({0:.2f} deg)').format(geomDict['angle'])
            newFlag = self.buildFlagFeature(flagLyr, self.processName, tableSchema, tableName, geomDict['feat_id'], geomDict['geometry_column'], geomDict['geom'], reason)
            featFlagList.append(newFlag)
        return self.raiseVectorFlags(flagLyr, featFlagList)


    def execute(self):
        """
        Reimplementation of the execute method from the parent class
        """
        QgsMessageLog.logMessage(self.tr('Starting ')+self.getName()+self.tr(' Process.'), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        try:
            self.setStatus(self.tr('Running'), 3) #now I'm running!
            self.abstractDb.deleteProcessFlags(self.getName()) #erase previous flags
            classesWithElem = self.parameters['Classes']
            if len(classesWithElem) == 0:
                self.setStatus(self.tr('No classes selected!. Nothing to be done.'), 1) #Finished
                QgsMessageLog.logMessage(self.tr('No classes selected! Nothing to be done.'), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                return 1
            tol = self.parameters['Angle']
            error = False
            flagLyr = self.getFlagLyr(0)
            for key in classesWithElem:
                self.startTimeCount()
                # preparation
                classAndGeom = self.classesWithElemDict[key]
                lyr = self.loadLayerBeforeValidationProcess(classAndGeom)
                # running the process
                localProgress = ProgressWidget(0, 1, self.tr('Running process on ')+classAndGeom['tableName'], parent=self.iface.mapCanvas())
                localProgress.step()
                result = self.getOutOfBoundsAngleList(lyr, tol, classAndGeom['geom'], onlySelected = self.parameters['Only Selected'])
                localProgress.step()
                
                # storing flags
                if len(result) > 0:
                    numberOfProblems = self.buildAndRaiseOutOfBoundsFlag(classAndGeom['tableSchema'], classAndGeom['tableName'], flagLyr, result)
                    QgsMessageLog.logMessage(str(numberOfProblems) + self.tr(' features from') + classAndGeom['tableName'] + self.tr(' have out of bounds angle(s). Check flags.'), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                else:
                    QgsMessageLog.logMessage(self.tr('There are no out of bounds angles on ') + classAndGeom['tableName'] + '.', "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                self.logLayerTime(classAndGeom['tableSchema']+'.'+classAndGeom['tableName'])
            if error:
                self.setStatus(self.tr('There are features with angles out of bounds. Check log.'), 4) #Finished with errors
            else:
                self.setStatus(self.tr('There are no features with angles out of bounds.'), 1) #Finished
            return 1
        except Exception as e:
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            self.finishedWithError()
            return 0
예제 #10
0
class FlipLine(QgsMapTool):
    """
    Tool expected behaviour:
    When (valid) line features are selected, it flips them upon tool activation.
    """
    def __init__(self, canvas, iface):
        self.iface = iface
        self.canvas = canvas
        self.toolAction = None
        QgsMapTool.__init__(self, self.canvas)
        self.DsgGeometryHandler = DsgGeometryHandler(iface)
        self.iface.currentLayerChanged.connect(self.setToolEnabled)
        self.iface.actionToggleEditing().triggered.connect(self.setToolEnabled)

    def setToolEnabled(self, layer):
        try:
            if isinstance(self.sender(), QtGui.QAction):
                layer = self.iface.mapCanvas().currentLayer()
        except:
            from PyQt4 import QtGui
            if isinstance(self.sender(), QtGui.QAction):
                layer = self.iface.mapCanvas().currentLayer()
        if not layer or not isinstance(
                layer, QgsVectorLayer
        ) or layer.geometryType() != QGis.Line or not layer.isEditable():
            enabled = False
        else:
            enabled = True
        self.toolAction.setEnabled(enabled)
        return enabled

    def activate(self):
        """
        Activates tool.
        """
        if self.toolAction:
            self.toolAction.setChecked(False)

    def deactivate(self):
        self.iface.currentLayerChanged.disconnect(self.setToolEnabled)
        self.iface.actionToggleEditing().triggered.disconnect(
            self.setToolEnabled)

    def setAction(self, action):
        """
        Defines an action for tool.
        action: QAction to be set.
        """
        self.toolAction = action

    def canvasReleaseEvent(self, e):
        """
        Selects all line features inside the rectangle created by dragging the mouse around 
        canvas whilst holding Shift key.
        :param e: mouse event on canvas.
        """
        if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
            self.isEmittingPoint = False
            r = self.rectangle()
            layers = self.canvas.layers()
            for layer in layers:
                #ignore layers on black list and features that are not vector layers and if layer not a line
                if (not isinstance(layer, QgsVectorLayer)
                    ) or layer.geometryType() != 1 or (
                        self.layerHasPartInBlackList(layer.name())):
                    continue
                if r is not None:
                    #builds bbRect and select from layer, adding selection
                    bbRect = self.canvas.mapSettings().mapToLayerCoordinates(
                        layer, r)
                    layer.select(bbRect, True)
            self.rubberBand.hide()

    def getAllSelectedLines(self):
        """
        Gets all selected lines on canvas.
        """
        selection = []
        for layer in self.iface.legendInterface().layers():
            if (not isinstance(layer,
                               QgsVectorLayer)) or layer.geometryType() != 1:
                continue
            for feat in layer.selectedFeatures():
                selection.append([layer, feat, layer.geometryType()])
        return selection

    def flipSelectedLines(self):
        """
        Method for flipping all selected lines. Used for button callback.
        """
        # get all selected features and remove all features that are not lines
        selectedFeatures = self.getAllSelectedLines()
        pop = 0
        for idx, item in enumerate(selectedFeatures):
            if item[2] != 1:
                selectedFeatures.pop(idx - pop)
                pop += 1
        if not selectedFeatures:
            logMsg = self.getLogMessage(None, None)
            self.iface.messageBar().pushMessage(self.tr('Error'),
                                                logMsg,
                                                level=QgsMessageBar.CRITICAL,
                                                duration=3)
            # QtGui.QMessageBox.critical(self, self.tr('Critical!'), logMsg)
            QgsMessageLog.logMessage(logMsg, "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            return
        # call the method for flipping features from geometry module
        flippedLines, failedLines = self.DsgGeometryHandler.flipFeatureList(
            featureList=selectedFeatures, debugging=True)
        logMsg = self.getLogMessage(flippedLines, failedLines)
        self.iface.messageBar().pushMessage(self.tr('Success'),
                                            logMsg,
                                            level=QgsMessageBar.INFO,
                                            duration=3)
        QgsMessageLog.logMessage(logMsg, "DSG Tools Plugin",
                                 QgsMessageLog.INFO)

    def getLogMessage(self, flippedLines, failedLines):
        """
        Method for mounting log message to be exposed to user.
        :param flippedLines: list of lines that were selected and were successfully flipped.
        :param failedLines: list of lines that were selected and failed to be flipped.
        :param success: indicates whether the log is for a failed execution or 
        """
        nrFlipped = nrFailed = 0
        if not flippedLines and not failedLines:
            return self.tr('There are no (valid) lines selected!')
        if flippedLines:
            nrFlipped = len(flippedLines)
            logMsg = self.tr("Feature(s) flipped: ")
            for item in flippedLines:
                logMsg += "{} (id={}), ".format(item[0].name(), item[1].id())
            logMsg = (logMsg + ")").replace(", )", "")
        elif failedLines:
            nrFailed = len(failedLines)
            logMsg += self.tr("\nFeature(s) that failed to be flipped: ")
            for item in failedLines:
                logMsg += "{} (id={}), ".format(item[0].name(), item[1].id())
            logMsg = (logMsg + ")").replace(", )", ".")
        return logMsg + self.tr("\n{} lines flipped. {} failed to be flipped."
                                ).format(nrFlipped, nrFailed)

    def startFlipLineTool(self):
        if self.canvas.currentLayer() in self.iface.editableLayers():
            self.flipSelectedLines()
        else:
            self.iface.messageBar().pushMessage(
                self.tr('Warning'),
                self.tr('Start editing in current layer!'),
                level=QgsMessageBar.INFO,
                duration=3)
예제 #11
0
 def __init__(self, postgisDb, iface, instantiating=False):
     """
     Class constructor.
     :param postgisDb: (DsgTools.AbstractDb) postgis database connection.
     :param iface: (QgisInterface) QGIS interface object.
     :param instantiating: (bool) indication of whether method is being instatiated.
     """
     super(CreateNetworkNodesProcess,
           self).__init__(postgisDb, iface, instantiating)
     self.processAlias = self.tr('Create Network Nodes')
     self.hidNodeLayerName = 'aux_hid_nodes_p'
     self.canvas = self.iface.mapCanvas()
     self.DsgGeometryHandler = DsgGeometryHandler(iface)
     # name for node types (check enum atop)
     self.nodeTypeNameDict = {
         CreateNetworkNodesProcess.Flag:
         self.tr("Flag"),
         CreateNetworkNodesProcess.Sink:
         self.tr("Sink"),
         CreateNetworkNodesProcess.WaterwayBegin:
         self.tr("Waterway Beginning"),
         CreateNetworkNodesProcess.UpHillNode:
         self.tr("Up Hill Node"),
         CreateNetworkNodesProcess.DownHillNode:
         self.tr("Down Hill Node"),
         CreateNetworkNodesProcess.Confluence:
         self.tr("Confluence"),
         CreateNetworkNodesProcess.Ramification:
         self.tr("Ramification"),
         CreateNetworkNodesProcess.AttributeChange:
         self.tr("Attribute Change Node"),
         CreateNetworkNodesProcess.NodeNextToWaterBody:
         self.tr("Node Next to Water Body"),
         CreateNetworkNodesProcess.AttributeChangeFlag:
         self.tr("Attribute Change Flag"),
         CreateNetworkNodesProcess.NodeOverload:
         self.tr("Overloaded Node"),
         CreateNetworkNodesProcess.DisconnectedLine:
         self.tr("Disconnected From Network")
     }
     if not self.instantiating:
         # getting tables with elements (line primitive)
         self.classesWithElemDict = self.abstractDb.getGeomColumnDictV2(
             withElements=True, excludeValidation=True)
         # adjusting process parameters
         interfaceDict = dict()
         for key in self.classesWithElemDict:
             cat, lyrName, geom, geomType, tableType = key.split(',')
             interfaceDict[key] = {
                 self.tr('Category'): cat,
                 self.tr('Layer Name'): lyrName,
                 self.tr('Geometry\nColumn'): geom,
                 self.tr('Geometry\nType'): geomType,
                 self.tr('Layer\nType'): tableType
             }
         self.networkClassesWithElemDict = self.abstractDb.getGeomColumnDictV2(
             primitiveFilter=['l'],
             withElements=True,
             excludeValidation=True)
         networkFlowParameterList = HidrographyFlowParameters(
             self.networkClassesWithElemDict.keys())
         self.sinkClassesWithElemDict = self.abstractDb.getGeomColumnDictV2(
             primitiveFilter=['p'],
             withElements=True,
             excludeValidation=True)
         sinkFlowParameterList = HidrographyFlowParameters(
             self.sinkClassesWithElemDict.keys())
         self.parameters = {
             'Only Selected':
             False,
             'Network Layer':
             networkFlowParameterList,
             'Sink Layer':
             sinkFlowParameterList,
             'Search Radius':
             5.0,
             'Reference and Water Body Layers':
             OrderedDict({
                 'referenceDictList': {},
                 'layersDictList': interfaceDict
             })
         }
         self.nodeDict = None
         self.nodeTypeDict = None
예제 #12
0
class CreateNetworkNodesProcess(ValidationProcess):
    # enum for node types
    Flag, Sink, WaterwayBegin, UpHillNode, DownHillNode, Confluence, Ramification, AttributeChange, NodeNextToWaterBody, AttributeChangeFlag, NodeOverload, DisconnectedLine = range(
        12)

    def __init__(self, postgisDb, iface, instantiating=False):
        """
        Class constructor.
        :param postgisDb: (DsgTools.AbstractDb) postgis database connection.
        :param iface: (QgisInterface) QGIS interface object.
        :param instantiating: (bool) indication of whether method is being instatiated.
        """
        super(CreateNetworkNodesProcess,
              self).__init__(postgisDb, iface, instantiating)
        self.processAlias = self.tr('Create Network Nodes')
        self.hidNodeLayerName = 'aux_hid_nodes_p'
        self.canvas = self.iface.mapCanvas()
        self.DsgGeometryHandler = DsgGeometryHandler(iface)
        # name for node types (check enum atop)
        self.nodeTypeNameDict = {
            CreateNetworkNodesProcess.Flag:
            self.tr("Flag"),
            CreateNetworkNodesProcess.Sink:
            self.tr("Sink"),
            CreateNetworkNodesProcess.WaterwayBegin:
            self.tr("Waterway Beginning"),
            CreateNetworkNodesProcess.UpHillNode:
            self.tr("Up Hill Node"),
            CreateNetworkNodesProcess.DownHillNode:
            self.tr("Down Hill Node"),
            CreateNetworkNodesProcess.Confluence:
            self.tr("Confluence"),
            CreateNetworkNodesProcess.Ramification:
            self.tr("Ramification"),
            CreateNetworkNodesProcess.AttributeChange:
            self.tr("Attribute Change Node"),
            CreateNetworkNodesProcess.NodeNextToWaterBody:
            self.tr("Node Next to Water Body"),
            CreateNetworkNodesProcess.AttributeChangeFlag:
            self.tr("Attribute Change Flag"),
            CreateNetworkNodesProcess.NodeOverload:
            self.tr("Overloaded Node"),
            CreateNetworkNodesProcess.DisconnectedLine:
            self.tr("Disconnected From Network")
        }
        if not self.instantiating:
            # getting tables with elements (line primitive)
            self.classesWithElemDict = self.abstractDb.getGeomColumnDictV2(
                withElements=True, excludeValidation=True)
            # adjusting process parameters
            interfaceDict = dict()
            for key in self.classesWithElemDict:
                cat, lyrName, geom, geomType, tableType = key.split(',')
                interfaceDict[key] = {
                    self.tr('Category'): cat,
                    self.tr('Layer Name'): lyrName,
                    self.tr('Geometry\nColumn'): geom,
                    self.tr('Geometry\nType'): geomType,
                    self.tr('Layer\nType'): tableType
                }
            self.networkClassesWithElemDict = self.abstractDb.getGeomColumnDictV2(
                primitiveFilter=['l'],
                withElements=True,
                excludeValidation=True)
            networkFlowParameterList = HidrographyFlowParameters(
                self.networkClassesWithElemDict.keys())
            self.sinkClassesWithElemDict = self.abstractDb.getGeomColumnDictV2(
                primitiveFilter=['p'],
                withElements=True,
                excludeValidation=True)
            sinkFlowParameterList = HidrographyFlowParameters(
                self.sinkClassesWithElemDict.keys())
            self.parameters = {
                'Only Selected':
                False,
                'Network Layer':
                networkFlowParameterList,
                'Sink Layer':
                sinkFlowParameterList,
                'Search Radius':
                5.0,
                'Reference and Water Body Layers':
                OrderedDict({
                    'referenceDictList': {},
                    'layersDictList': interfaceDict
                })
            }
            self.nodeDict = None
            self.nodeTypeDict = None

    def getFrameOutterBounds(self, frameLayer):
        """
        Gets the outter bounds of all frame features composing frame layer.
        :param frameLayer: (QgsVectorLayer) frame layer.
        :return: (list-of-QgsGeometry) list of all disjuncts outter bounds of features in frame layer.
        """
        frameGeomList = []
        # dissolve every feature into a single one
        result = processing.runalg('qgis:dissolve', frameLayer, True, None,
                                   None)
        # get the layer from processing result
        outputLayer = processing.getObject(result['OUTPUT'])
        # clean possible missinformation from resulting layer
        # setting clean parameters
        tools = 'rmsa,break,rmdupl,rmdangle'
        threshold = -1
        extent = outputLayer.extent()
        (xmin, xmax, ymin, ymax) = extent.xMinimum(), extent.xMaximum(
        ), extent.yMinimum(), extent.yMaximum()
        extent = '{0},{1},{2},{3}'.format(xmin, xmax, ymin, ymax)
        snap = self.parameters['Search Radius']
        minArea = 0.0001
        result = processing.runalg('grass7:v.clean.advanced', outputLayer,
                                   tools, threshold, extent, snap, minArea,
                                   None, None)
        # get resulting layer
        outputLayer = processing.getObject(result['output'])
        # get all frame outter layer found
        for feat in outputLayer.getFeatures():
            geom = feat.geometry()
            # deaggregate geometry, if necessary
            geomList = self.DsgGeometryHandler.deaggregateGeometry(
                multiGeom=geom)
            # for every deaggregated node, get only the outter bound as a polyline (for intersection purposes)
            frameGeomList += [
                QgsGeometry().fromPolyline(g.asPolygon()[0]) for g in geomList
            ]
        return frameGeomList

    def identifyAllNodes(self, networkLayer):
        """
        Identifies all nodes from a given layer (or selected features of it). The result is returned as a dict of dict.
        :param networkLayer: target layer to which nodes identification is required.
        :return: { node_id : { start : [feature_which_starts_with_node], end : feature_which_ends_with_node } }.
        """
        nodeDict = dict()
        isMulti = QgsWKBTypes.isMultiType(int(networkLayer.wkbType()))
        if self.parameters['Only Selected']:
            features = networkLayer.selectedFeatures()
        else:
            features = [feat for feat in networkLayer.getFeatures()]
        for feat in features:
            nodes = self.DsgGeometryHandler.getFeatureNodes(networkLayer, feat)
            if nodes:
                if isMulti:
                    if len(nodes) > 1:
                        # if feat is multipart and has more than one part, a flag should be raised
                        continue  # CHANGE TO RAISE FLAG
                    elif len(nodes) == 0:
                        # if no part is found, skip feature
                        continue
                    else:
                        # if feat is multipart, "nodes" is a list of list
                        nodes = nodes[0]
                # initial node
                pInit, pEnd = nodes[0], nodes[-1]
                # filling starting node information into dictionary
                if pInit not in nodeDict:
                    # if the point is not already started into dictionary, it creates a new item
                    nodeDict[pInit] = {'start': [], 'end': []}
                if feat not in nodeDict[pInit]['start']:
                    nodeDict[pInit]['start'].append(feat)
                # filling ending node information into dictionary
                if pEnd not in nodeDict:
                    nodeDict[pEnd] = {'start': [], 'end': []}
                if feat not in nodeDict[pEnd]['end']:
                    nodeDict[pEnd]['end'].append(feat)
        return nodeDict

    def changeLineDict(self, nodeList, line):
        """
        Changes a line from a dict to another (start/end). Useful when network lines are flipped and another node
        identification is too costy.
        :param nodeList: (list-of-QgsPoint) the list of nodes connected to have given line relation dictionary changed.
        :param line: (QgsFeature) line to be flipped.
        :return: whether changing was successful for each node 
        """
        for node in nodeList:
            # node's dicts
            startDict = self.nodeDict[node]['start']
            endDict = self.nodeDict[node]['end']
            if line in startDict:
                startDict.remove(line)
                endDict.append(line)
            elif line in endDict:
                startDict.append(line)
                endDict.remove(line)
            else:
                # if line is not found for some reason
                return False
        # if a nodeList is not found, method doesn't change anything
        return bool(nodeList)

    def nodeOnFrame(self, node, frameLyrContourList, searchRadius):
        """
        Identify whether or not node is over the frame. Returns True if point is over the frame and false if
        node is not on frame. If identification fails, returns 'None'.
        :param node: node (QgsPoint) to be identified as over the frame layer or not.
        :param frameLyrContourList: (list-of-QgsGeometry) border line for the frame layer to be checked.
        :param searchRadius: maximum distance to frame layer such that the feature is considered touching it.
        :return: (bool) whether node is as close as searchRaius to frame contour.
        """
        qgisPoint = QgsGeometry.fromPoint(node)
        # building a buffer around node with search radius for intersection with Layer Frame
        buf = qgisPoint.buffer(searchRadius, -1)
        for frameContour in frameLyrContourList:
            if buf.intersects(frameContour):
                # it is condition enough one of the frame contours to be next to node
                return True
        return False

    def nodeNextToWaterBodies(self, node, waterBodiesLayers, searchRadius):
        """
        Identify whether or not node is next to a water body feature.
        :param node: (QgsPoint) node to be identified as next to a water body feature.
        :param waterBodiesLayers: (list-of-QgsVectorLayer) list of layers composing the water bodies on map.
        :param searchRadius: (float) maximum distance to frame layer such that the feature is considered touching it.
        :return: (bool) whether node is as close as searchRaius to a water body element.
        """
        qgisPoint = QgsGeometry.fromPoint(node)
        # building a buffer around node with search radius for intersection with Layer Frame
        buf = qgisPoint.buffer(searchRadius, -1)
        # building bounding box around node for feature requesting
        bbRect = buf.boundingBox()
        # check if buffer intersects features from water bodies layers
        for lyr in waterBodiesLayers:
            if lyr.geometryType() == 0:
                # ignore point primitive layers
                continue
            for feat in lyr.getFeatures(QgsFeatureRequest(bbRect)):
                if buf.intersects(feat.geometry()):
                    # any feature component of a water body intersected is enough
                    return True
        return False

    def nodeIsWaterSink(self, node, waterSinkLayer, searchRadius):
        """
        Identify whether or not node is next to a water body feature. If no water sink layer is given, method returns False
        :param node: (QgsPoint) node to be identified as coincident with a water sink feature.
        :param waterSinkLayer: (QgsVectorLayer) layer containing the water sinks on map.
        :param searchRadius: (float) maximum distance to frame layer such that the feature is considered touching it.
        :return: (bool) whether node is as close as searchRaius to a water body element.
        """
        if not waterSinkLayer:
            return False
        qgisPoint = QgsGeometry.fromPoint(node)
        # building bounding box around node for feature requesting
        bbRect = qgisPoint.buffer(searchRadius, -1).boundingBox()
        # check if qgisPoint (node geometry) is over a sink classified point
        for feat in waterSinkLayer.getFeatures(QgsFeatureRequest(bbRect)):
            if qgisPoint.distance(feat.geometry()) <= searchRadius:
                # any feature component of a water body intersected is enough
                return True
        return False

    def checkIfHasLineInsideWaterBody(self,
                                      node,
                                      waterBodiesLayers,
                                      searchRadius=1.0):
        """
        Checks whether one of ending lines connected to given node is inside of a water body feature.
        :param node: (QgsPoint) node to be identified having an ending line inside of a water body.
        :param waterBodiesLayers: (list-of-QgsVectorLayer) list of layers composing the water bodies on map.
        :return: (bool) whether node is as close as searchRaius to a water body element.
        """
        qgisPoint = QgsGeometry.fromPoint(node)
        # building a buffer around node with search radius for intersection with Layer Frame
        buf = qgisPoint.buffer(searchRadius, -1)
        # building bounding box around node for feature requesting
        bbRect = buf.boundingBox()
        # check if any wb feature inside of buffer area contains any ending line
        for line in self.nodeDict[node]['end']:
            for lyr in waterBodiesLayers:
                for feat in lyr.getFeatures(QgsFeatureRequest(bbRect)):
                    if feat.geometry().contains(line.geometry()):
                        # any feature component of a water body intersected is enough
                        return True
        return False

    def getAttributesFromFeature(self, feature, layer, fieldNames=None):
        """
        Retrieves the attributes from a given feature, except for their geometry and ID column values. If a list of
        attributes is given, method will return those attributes if found. In case no attribute is found, None will 
        :param feature: (QgsFeature) feature from which attibutes will be retrieved.
        :param layer: (QgsVectorLayer) layer containing target feature.
        :param fieldNames: (list-of-str) list of field names to be exposed.
        :return: (dict-of-object) attribute values for each attribute mapped.
        """
        # fields to be ignored
        ignoreList = []
        if not fieldNames:
            # retrieving key column name
            uri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
            keyColumn = uri.keyColumn()
            # retrieving geometry column name
            networLayerName = layer.name()
            for key in self.networkClassesWithElemDict.keys():
                if key.split(",")[1] in networLayerName:
                    geomColumn = key.split(",")[2]
                    break
            # removing attributes that are calculated OTF
            fieldNames = [
                f for f in layer.fields()
                if f.name() not in [keyColumn, geomColumn]
                and '_otf' not in f.name()
            ]
        else:
            # check if all field names given are in fact fields for the layer
            layerFields = layer.fields()
            ignoreList = [
                field for field in fieldNames if field not in layerFields
            ]
        return {
            field.name(): feature[field.name()]
            for field in fieldNames if field not in ignoreList
        }

    def attributeChangeCheck(self, node, networkLayer):
        """
        Checks if attribute change node is in fact an attribute change.
        :param node: (QgsPoint) node to be identified as over the frame layer or not.
        :param networkLayer: (QgsVectorLayer) layer containing network lines.
        :return: (bool) if lines connected to node do change attributes.
        """
        # assuming that attribute change nodes have only 1-in 1-out lines
        lineIn = self.nodeDict[node]['end'][0]
        atrLineIn = self.getAttributesFromFeature(feature=lineIn,
                                                  layer=networkLayer)
        lineOut = self.nodeDict[node]['start'][0]
        atrLineOut = self.getAttributesFromFeature(feature=lineOut,
                                                   layer=networkLayer)
        # comparing their dictionary of attributes, it is decided whether they share the exact same set of attributes (fields and values)
        return atrLineIn != atrLineOut

    def isFirstOrderDangle(self, node, networkLayer, searchRadius):
        """
        Checks whether node is a dangle into network (connected to a first order line).
        :param node: (QgsPoint) node to be validated.
        :param networkLayer: (QgsVectorLayer) network layer (line layer).
        :param searchRadius: (float) limit distance to another line.
        :return: (bool) indication whether node is a dangle.
        """
        qgisPoint = QgsGeometry.fromPoint(node)
        # building a buffer around node with search radius for intersection with Layer Frame
        buf = qgisPoint.buffer(searchRadius, -1)
        # building bounding box around node for feature requesting
        bbRect = buf.boundingBox()
        # check if buffer intersects features from water bodies layers
        count = 0
        for feat in networkLayer.getFeatures(QgsFeatureRequest(bbRect)):
            if buf.intersects(feat.geometry()):
                count += 1
                res = (count > 1)
                if res:
                    # to avoid as many iterations as possible
                    return False
        return True

    def checkIfLineIsDisconnected(self,
                                  node,
                                  networkLayer,
                                  nodeTypeDict,
                                  geomType=None):
        """
        Checks whether a waterway beginning node connected to a line disconnected from network.
        :param node: (QgsPoint) point to be classified.
        :param networkLayer: (QgsVectorLayer) network lines layer.
        :param nodeTypeDict: (dict) all current classified nodes and theirs types.
        :param geomType: (int) network layer geometry type code.
        :return: (bool) whether node is connected to a disconnected line 
        """
        if not nodeTypeDict:
            # if there are no classified nodes, method is ineffective
            return False
        # if a line is disconnected from network, then the other end of the line would have to be classified as a waterway beginning as well
        if not geomType:
            geomType = networkLayer.geometryType()
        nextNodes = []
        # to reduce calculation time
        nodePointDict = self.nodeDict[node]
        isMulti = QgsWKBTypes.isMultiType(int(networkLayer.wkbType()))
        # get all other nodes connected to lines connected to "node"
        lines = nodePointDict['start'] + nodePointDict['end']
        if len(lines) > 1:
            # if there is at least one more line connected to node, line is not disconnected
            return False
        # get line nodes
        n = self.DsgGeometryHandler.getFeatureNodes(layer=networkLayer,
                                                    feature=lines[0],
                                                    geomType=geomType)
        if nodePointDict['start']:
            # if line starts at target node, the other extremity is a final node
            if isMulti:
                if n:
                    n = n[0][-1]
            elif n:
                n = n[-1]
        elif nodePointDict['end']:
            # if line starts at target node, the other extremity is a initial node
            if isMulti:
                if n:
                    n = n[0][0]
            elif n:
                n = n[0]
        # if next node is not among the valid ending lines, it may still be connected to a disconnected line if it is a dangle
        # validEnds = [CreateNetworkNodesProcess.Sink, CreateNetworkNodesProcess.DownHillNode, CreateNetworkNodesProcess.NodeNextToWaterBody]
        if n in nodeTypeDict:
            # if both ends are classified as waterway beginning, then both ends are 1st order dangles and line is disconnected.
            return nodeTypeDict[n] in [
                CreateNetworkNodesProcess.WaterwayBegin,
                CreateNetworkNodesProcess.DownHillNode,
                CreateNetworkNodesProcess.UpHillNode,
                CreateNetworkNodesProcess.NodeNextToWaterBody
            ]
            # if nodeTypeDict[n] not in validEnds:
            #     if self.isFirstOrderDangle(node=n, networkLayer=networkLayer, searchRadius=self.parameters[self.tr('Search Radius')]):
            #         # if next node is not a valid network ending node and is a dangle, line is disconnected from network
            #         return False
            #     return True
        # in case next node is not yet classified, method is ineffective
        return False

    def nodeType(self,
                 nodePoint,
                 networkLayer,
                 frameLyrContourList,
                 waterBodiesLayers,
                 searchRadius,
                 nodeTypeDict,
                 waterSinkLayer=None,
                 networkLayerGeomType=None):
        """
        Get the node type given all lines that flows from/to it.
        :param nodePoint: (QgsPoint) point to be classified.
        :param networkLayer: (QgsVectorLayer) network lines layer.
        :param frameLyrContourList: (list-of-QgsGeometry) border line for the frame layer to be checked.
        :param waterBodiesLayers: (list-of-QgsVectorLayer) list of all waterbodies layer.
        :param searchRadius: (float) maximum distance to frame layer such that the feature is considered touching it.
        :param nodeTypeDict: (dict) dict with all currently classified nodes and their types.
        :param waterSinkLayer: (QgsVectorLayer) water sink layer.
        :param networkLayerGeomType: (int) network layer geometry type code.
        :return: returns the (int) point type.
        """
        # to reduce calculation time in expense of memory, which is cheap
        nodePointDict = self.nodeDict[nodePoint]
        sizeFlowOut = len(nodePointDict['start'])
        sizeFlowIn = len(nodePointDict['end'])
        hasStartLine = bool(sizeFlowOut)
        hasEndLine = bool(sizeFlowIn)
        if not networkLayerGeomType:
            networkLayerGeomType = networkLayer.geometryType()
        # "exclusive or"
        startXORendLine = (hasStartLine != hasEndLine)
        # # case 5: more than 3 lines flowing through one network line (it is forbidden as of Brazilian mapping norm EDGV)
        # if sizeFlowIn + sizeFlowOut > 3:
        #     return CreateNetworkNodesProcess.NodeOverload
        # case 1: all lines either flow in or out
        if startXORendLine:
            # case 1.a: point is over the frame
            if self.nodeOnFrame(node=nodePoint,
                                frameLyrContourList=frameLyrContourList,
                                searchRadius=searchRadius):
                # case 1.a.i: waterway is flowing away from mapped area (point over the frame has one line ending line)
                if hasEndLine:
                    return CreateNetworkNodesProcess.DownHillNode
                # case 1.a.ii: waterway is flowing to mapped area (point over the frame has one line starting line)
                elif hasStartLine:
                    return CreateNetworkNodesProcess.UpHillNode
            # case 1.b: point that legitimately only flows from
            elif hasEndLine:
                # case 1.b.i
                if self.nodeNextToWaterBodies(
                        node=nodePoint,
                        waterBodiesLayers=waterBodiesLayers,
                        searchRadius=searchRadius):
                    # it is considered that every free node on map is a starting node. The only valid exceptions are nodes that are
                    # next to water bodies and water sink holes.
                    if sizeFlowIn == 1:
                        # a node next to water has to be a lose end
                        return CreateNetworkNodesProcess.NodeNextToWaterBody
                # force all lose ends to be waterway beginnings if they're not dangles (which are flags)
                elif self.isFirstOrderDangle(
                        node=nodePoint,
                        networkLayer=networkLayer,
                        searchRadius=self.parameters['Search Radius']):
                    # check if node is connected to a disconnected line
                    if self.checkIfLineIsDisconnected(
                            node=nodePoint,
                            networkLayer=networkLayer,
                            nodeTypeDict=nodeTypeDict,
                            geomType=networkLayerGeomType):
                        return CreateNetworkNodesProcess.DisconnectedLine
                    # case 1.b.ii: node is in fact a water sink and should be able to take an 'in' flow
                    elif self.nodeIsWaterSink(node=nodePoint,
                                              waterSinkLayer=waterSinkLayer,
                                              searchRadius=searchRadius):
                        # if a node is indeed a water sink (operator has set it to a sink)
                        return CreateNetworkNodesProcess.Sink
                    return CreateNetworkNodesProcess.WaterwayBegin
            # case 1.c: point that legitimately only flows out
            elif hasStartLine and self.isFirstOrderDangle(
                    node=nodePoint,
                    networkLayer=networkLayer,
                    searchRadius=self.parameters['Search Radius']):
                if self.checkIfLineIsDisconnected(
                        node=nodePoint,
                        networkLayer=networkLayer,
                        nodeTypeDict=nodeTypeDict,
                        geomType=networkLayerGeomType):
                    return CreateNetworkNodesProcess.DisconnectedLine
                elif self.nodeIsWaterSink(node=nodePoint,
                                          waterSinkLayer=waterSinkLayer,
                                          searchRadius=searchRadius):
                    # in case there's a wrongly acquired line connected to a water sink
                    return CreateNetworkNodesProcess.Sink
                return CreateNetworkNodesProcess.WaterwayBegin
            # case 1.d: points that are not supposed to have one way flow (flags)
            return CreateNetworkNodesProcess.Flag
        elif sizeFlowIn > sizeFlowOut:
            # case 2 "confluence"
            return CreateNetworkNodesProcess.Confluence
        elif sizeFlowIn == sizeFlowOut:
            if sizeFlowIn > 1:
                # case 4.c: there's a constant flow through node, but there are more than 1 line
                return CreateNetworkNodesProcess.NodeOverload
            elif self.attributeChangeCheck(node=nodePoint,
                                           networkLayer=networkLayer):
                # case 4.a: lines do change their attribute set
                return CreateNetworkNodesProcess.AttributeChange
            else:
                # case 4.b: nodes inside the network that are there as an attribute change node but lines connected
                #           to it have the same set of attributes
                return CreateNetworkNodesProcess.AttributeChangeFlag
        else:
            # case 3 "ramification"
            return CreateNetworkNodesProcess.Ramification

    def classifyAllNodes(self,
                         networkLayer,
                         frameLyrContourList,
                         waterBodiesLayers,
                         searchRadius,
                         waterSinkLayer=None,
                         nodeList=None):
        """
        Classifies all identified nodes from the hidrography line layer.
        :param networkLayer: (QgsVectorLayer) network lines layer.
        :param frameLyrContourList: (list-of-QgsFeature) border line for the frame layer.
        :param waterBodiesLayers: (list-of-QgsVectorLayer) list of all classes with water bodies to be compared to.
        :param searchRadius: (float) maximum distance to frame layer such that the feature is considered touching it.
        :param nodeList: a list of nodes (QgsPoint) to be classified. If not given, whole dict is going 
                         to be classified. Node MUST be in dict given, if not, it'll be ignored.
        :param waterSinkLayer: (QgsVectorLayer) water sink layer.
        :return: a (dict) dictionary of node and its node type ( { (QgsPoint)node : (int)nodeType } ). 
        """
        networkLayerGeomType = networkLayer.geometryType()
        nodeTypeDict = dict()
        nodeKeys = self.nodeDict.keys()
        if not nodeList:
            nodeList = nodeKeys
        for node in nodeList:
            if node not in nodeKeys:
                # in case user decides to use a list of nodes to work on, given nodes that are not identified will be ignored
                continue
            nodeTypeDict[node] = self.nodeType(nodePoint=node, networkLayer=networkLayer, frameLyrContourList=frameLyrContourList, \
                                    waterBodiesLayers=waterBodiesLayers, searchRadius=searchRadius, waterSinkLayer=waterSinkLayer, \
                                    nodeTypeDict=nodeTypeDict, networkLayerGeomType=networkLayerGeomType)
        return nodeTypeDict

    def clearHidNodeLayer(self,
                          nodeLayer,
                          nodeIdList=None,
                          commitToLayer=False):
        """
        Clears all (or a given list of points) hidrography nodes on layer.
        :param nodeLayer: (QgsVectorLayer) hidrography nodes layer.
        :param nodeIdList: (list-of-int) list of node IDs to be cleared from layer.
        :param commitToLayer: (bool) indicates whether changes should be commited to layer.
        """
        nodeLayer.beginEditCommand('Clear Nodes')
        if not nodeIdList:
            nodeIdList = [feat.id() for feat in nodeLayer.getFeatures()]
        nodeLayer.deleteFeatures(nodeIdList)
        nodeLayer.endEditCommand()
        # commit changes to LAYER
        if commitToLayer:
            nodeLayer.commitChanges()

    def fillNodeLayer(self,
                      nodeLayer,
                      networkLineLayerName,
                      commitToLayer=False):
        """
        Populate hidrography node layer with all nodes.
        :param nodeLayer: (QgsVectorLayer) hidrography nodes layer.
        :param networkLineLayerName: (str) network line layer name.
        :param commitToLayer: (bool) indicates whether changes should be commited to layer.
        """
        # if table is going to be filled, then it needs to be cleared first
        self.clearHidNodeLayer(nodeLayer=nodeLayer,
                               commitToLayer=commitToLayer)
        # get fields from layer in order to create new feature with the same attribute map
        fields = nodeLayer.fields()
        nodeLayer.beginEditCommand('Create Nodes')
        # to avoid unnecessary calculation inside loop
        nodeTypeKeys = self.nodeTypeDict.keys()
        # initiate new features list
        featList = []
        for node in self.nodeDict:
            # set attribute map
            feat = QgsFeature(fields)
            # set geometry
            feat.setGeometry(QgsGeometry.fromMultiPoint([node]))
            feat['node_type'] = self.nodeTypeDict[
                node] if node in nodeTypeKeys else None
            feat['layer'] = networkLineLayerName
            featList.append(feat)
        nodeLayer.addFeatures(featList)
        nodeLayer.endEditCommand()
        if commitToLayer:
            nodeLayer.commitChanges()

    def loadLayer(self, layerName, uniqueLoad=True):
        """
        Load a given layer to canvas.
        :param layerName: (str) layer name to be loaded.
        :param uniqueLoad: (bool) indicates that layer will be loaded to canvas only if it is not loaded already.
        :return: (QgsVectorLayer) the loaded layer.
        """
        try:
            return self.layerLoader.load([layerName],
                                         uniqueLoad=uniqueLoad)[layerName]
        except Exception as e:
            errorMsg = self.tr(
                'Could not load the class {0}! (If you manually removed {0} from database, reloading QGIS/DSGTools Plugin might sort out the problem.\n'
            ).format(layerName) + ':'.join(e.args)
            QMessageBox.critical(self.canvas, self.tr('Error!'), errorMsg)

    def execute(self):
        """
        Structures and executes the process.
        :return: (int) execution code.
        """
        QgsMessageLog.logMessage(
            self.tr('Starting ') + self.getName() + self.tr(' Process.'),
            "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        self.startTimeCount()
        try:
            self.setStatus(self.tr('Running'), 3)  #now I'm running!
            self.abstractDb.deleteProcessFlags(
                self.getName())  #erase previous flags
            # node type should not be calculated OTF for comparison (db data is the one perpetuated)
            # setting all method variables
            networkLayerKey = self.parameters['Network Layer']
            hidSinkLyrKey = self.parameters['Sink Layer']
            refKey, classesWithElemKeys = self.parameters[
                'Reference and Water Body Layers']
            waterBodyClassesKeys = classesWithElemKeys
            if not refKey:
                self.setStatus(
                    self.tr('One reference must be selected! Stopping.'),
                    1)  #Finished
                return 1
            # preparing reference layer
            refcl = self.classesWithElemDict[refKey]
            frameLayer = self.loadLayerBeforeValidationProcess(refcl)
            # preparing hidrography lines layer
            # remake the key from standard string
            k = ('{},{},{},{},{}').format(
                                          networkLayerKey.split('.')[0],\
                                          networkLayerKey.split('.')[1].split(r' (')[0],\
                                          networkLayerKey.split('(')[1].split(', ')[0],\
                                          networkLayerKey.split('(')[1].split(', ')[1],\
                                          networkLayerKey.split('(')[1].split(', ')[2].replace(')', '')
                                         )
            hidcl = self.networkClassesWithElemDict[k]
            networkLayer = self.loadLayerBeforeValidationProcess(hidcl)
            # preparing the list of water bodies classes
            waterBodyClasses = []
            for key in waterBodyClassesKeys:
                wbc = self.classesWithElemDict[key]
                waterBodyClasses.append(
                    self.loadLayerBeforeValidationProcess(wbc))
            # preparing water sink layer
            if hidSinkLyrKey and hidSinkLyrKey != self.tr('Select Layer'):
                # remake the key from standard string
                k = ('{},{},{},{},{}').format(
                                          hidSinkLyrKey.split('.')[0],\
                                          hidSinkLyrKey.split('.')[1].split(r' (')[0],\
                                          hidSinkLyrKey.split('(')[1].split(', ')[0],\
                                          hidSinkLyrKey.split('(')[1].split(', ')[1],\
                                          hidSinkLyrKey.split('(')[1].split(', ')[2].replace(')', '')
                                         )
                sinkcl = self.sinkClassesWithElemDict[k]
                waterSinkLayer = self.loadLayerBeforeValidationProcess(sinkcl)
            else:
                # if no sink layer is selected, layer should be ignored
                waterSinkLayer = None
            # getting dictionaries of nodes information
            frame = self.getFrameOutterBounds(frameLayer=frameLayer)
            self.nodeDict = self.identifyAllNodes(networkLayer=networkLayer)
            crs = networkLayer.crs().authid()
            # node layer has the same CRS as the hidrography lines layer
            nodeSrid = networkLayer.crs().authid().split(':')[1]
            searchRadius = self.parameters['Search Radius']
            self.nodeTypeDict = self.classifyAllNodes(
                networkLayer=networkLayer,
                frameLyrContourList=frame,
                waterBodiesLayers=waterBodyClasses,
                searchRadius=searchRadius,
                waterSinkLayer=waterSinkLayer)
            # check if node table and node type domain table are created on db
            if not self.abstractDb.checkIfTableExists('validation',
                                                      self.hidNodeLayerName):
                # if it does not exist, it is created
                self.abstractDb.createHidNodeTable(nodeSrid)
            # load node table into canvas
            nodeLayer = self.loadLayer(self.hidNodeLayerName)
            # to be able to update features into new layer
            nodeLayer.startEditing()
            self.fillNodeLayer(nodeLayer=nodeLayer,
                               networkLineLayerName=networkLayer.name())
            msg = self.tr('Network nodes created into layer {}.').format(
                self.hidNodeLayerName)
            self.setStatus(msg, 1)  #Finished
            return 1
        except Exception as e:
            QgsMessageLog.logMessage(': '.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            self.finishedWithError()
            return 0
예제 #13
0
class AssignBandValueTool(QgsMapTool):
    def __init__(self, iface, rasterLayer):
        """
        Tool Behaviours: (all behaviours start edition, except for rectangle one)
        1- Left Click: Creates a new point feature with the value from raster, according to selected attribute. 
        5- Shift + drag and drop: draws a rectangle, then features that intersect this rectangle are selected 
        and their value is set according to raster value and selected attribute.
        """
        self.iface = iface        
        self.canvas = self.iface.mapCanvas()
        QgsMapTool.__init__(self, self.canvas)
        self.toolAction = None
        self.qgsMapToolEmitPoint = QgsMapToolEmitPoint(self.canvas)
        self.dsgGeometryHandler = DsgGeometryHandler(iface)
        self.rasterLayer = rasterLayer
        self.setRubberbandParameters()
        self.reset()
        self.auxList = []
        self.decimals = self.getDecimals()

    def getDecimals(self):
        settings = QSettings()
        settings.beginGroup('PythonPlugins/DsgTools/Options')
        decimals = settings.value('decimals')
        if decimals:
            return int(decimals)
        else:
            return 0
    
    def getSuppressOptions(self):
        qgisSettings = QSettings()
        qgisSettings.beginGroup('Qgis/digitizing')
        setting = qgisSettings.value('disable_enter_attribute_values_dialog')
        qgisSettings.endGroup()
        if not setting:
            return False
        if setting.lower() == u'false':
            return False
        else:
            return True

    def setRubberbandParameters(self):
        self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        self.hoverRubberBand = QgsRubberBand(self.canvas, QGis.Polygon)
        mFillColor = QColor( 254, 178, 76, 63 )
        self.rubberBand.setColor(mFillColor)
        self.hoverRubberBand.setColor(QColor( 255, 0, 0, 90 ))
        self.rubberBand.setWidth(1)
    
    def reset(self):
        """
        Resets rubber band.
        """
        self.startPoint = self.endPoint = None
        self.isEmittingPoint = False
        self.rubberBand.reset(QGis.Polygon)

    def canvasPressEvent(self, e):
        """
        Method used to build rectangle if shift is held, otherwise, feature select/deselect and identify is done.
        :param e: (QgsMouseEvent) mouse event.
        """
        if e.button() == QtCore.Qt.LeftButton:
            self.auxList = []
            if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
                self.isEmittingPoint = True
                self.startPoint = self.toMapCoordinates(e.pos())
                self.endPoint = self.startPoint
                self.isEmittingPoint = True
                self.showRect(self.startPoint, self.endPoint)

    def canvasMoveEvent(self, e):
        """
        Used only on rectangle select.
        """
        if not self.isEmittingPoint:
            return
        self.endPoint = self.toMapCoordinates( e.pos() )
        self.showRect(self.startPoint, self.endPoint)        

    def showRect(self, startPoint, endPoint):
        """
        Builds rubberband rect.
        """
        self.rubberBand.reset(QGis.Polygon)
        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
            return
        point1 = QgsPoint(startPoint.x(), startPoint.y())
        point2 = QgsPoint(startPoint.x(), endPoint.y())
        point3 = QgsPoint(endPoint.x(), endPoint.y())
        point4 = QgsPoint(endPoint.x(), startPoint.y())
    
        self.rubberBand.addPoint(point1, False)
        self.rubberBand.addPoint(point2, False)
        self.rubberBand.addPoint(point3, False)
        self.rubberBand.addPoint(point4, True)    # true to update canvas
        self.rubberBand.show()

    def rectangle(self):
        """
        Builds rectangle from self.startPoint and self.endPoint
        """
        if self.startPoint is None or self.endPoint is None:
            return None
        elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y() == self.endPoint.y():
            return None
        return QgsRectangle(self.startPoint, self.endPoint)

    def setAction(self, action):
        self.toolAction = action
        self.toolAction.setCheckable(True)
    
    def canvasReleaseEvent(self, e):
        """
        After the rectangle is built, here features are selected.
        """
        # tool was planned to work on left click 
        if e.button() == QtCore.Qt.LeftButton:
            layer = self.iface.mapCanvas().currentLayer()
            if QtGui.QApplication.keyboardModifiers() == QtCore.Qt.ShiftModifier:
                self.isEmittingPoint = False
                r = self.rectangle()
                if r is None:
                    return
                bbRect = self.canvas.mapSettings().mapToLayerCoordinates(layer, r)
                self.rubberBand.hide()
                #select all stuff
                layer.setSelectedFeatures([]) #portar para o feature handler
                layer.select(bbRect, True)
                #mudar depois para o dsgmothafucka
                featDict = dict()
                pointDict = dict()
                for feat in layer.selectedFeatures():
                    featDict[feat.id()] = feat
                    pointDict[feat.id()] = feat.geometry()
                pixelValueDict = self.getPixelValueFromPointDict(pointDict, self.rasterLayer)
                for idx in pointDict:
                    value = pixelValueDict[idx]
                    if value:
                        self.auxList.append({'featId':idx, 'feat':featDict[idx], 'value':value})
            else:
                value, pointGeom = self.getPixelValue(self.rasterLayer)
                if value:
                    self.auxList.append({'geom':pointGeom, 'value':value})
            #create context menu to select attribute
            if self.auxList:
                self.createContextMenuOnPosition(e, layer)

    def createContextMenuOnPosition(self, e, layer):
        menu = QMenu()
        callbackDict = dict()
        fieldList = [field.name() for field in layer.fields() if field.isNumeric()]
        for field in fieldList:
            action = menu.addAction(field)
            callback = lambda t = [field, layer] : self.handleFeatures(t[0], t[1])
            action.triggered[()].connect(callback)
        menu.exec_(self.canvas.viewport().mapToGlobal(e.pos()))
    
    def handleFeatures(self, selectedField, layer):
        layer.startEditing()
        for item in self.auxList:
            if 'featId' in item:
                feat = item['feat']
                idx = feat.fieldNameIndex(selectedField)
                feat.setAttribute(idx, item['value'])
                layer.updateFeature(feat)
            else:
                feature = QgsFeature(layer.fields())
                self.dsgGeometryHandler.reprojectFeature(item['geom'], layer.crs())
                feature.setGeometry(item['geom'])
                self.addFeature(feature, layer, selectedField, item['value'])
        self.auxList = []
        self.canvas.refresh()
    
    def addFeature(self, feature, layer, field, pointValue):
        fields = layer.fields()
        feature.initAttributes(fields.count())            
        provider = layer.dataProvider()             
        for i in range(fields.count()):
            value = provider.defaultValue(i) if fields[i].name() != field else pointValue
            if value:
                feature.setAttribute(i, value)                
        form = QgsAttributeDialog(layer, feature, False)
        form.setMode(QgsAttributeForm.AddFeatureMode)
        formSuppress = layer.editFormConfig().suppress()
        if formSuppress == QgsEditFormConfig.SuppressDefault:
            if self.getSuppressOptions(): #this is calculated every time because user can switch options while using tool
                layer.addFeature(feature, True)
            else:
                if not form.exec_():
                    feature.setAttributes(form.feature().attributes())
        elif formSuppress == QgsEditFormConfig.SuppressOff:
            if not form.exec_():
                feature.setAttributes(form.feature().attributes())
        else:
            layer.addFeature(feature, True)

    def getCursorRect(self, e):
        """
        Calculates small cursor rectangle around mouse position. Used to facilitate operations
        """
        p = self.toMapCoordinates(e.pos())
        w = self.canvas.mapUnitsPerPixel() * 10
        return QgsRectangle(p.x()-w, p.y()-w, p.x()+w, p.y()+w)
    
    def deactivate(self):
        """
        Deactivate tool.
        """
        QtGui.QApplication.restoreOverrideCursor()
        self.hoverRubberBand.reset(QGis.Polygon)
        try:
            if self.toolAction:
                self.toolAction.setChecked(False)
            if self is not None:
                QgsMapTool.deactivate(self)
                self.canvas.unsetMapTool(self)
        except:
            pass

    def activate(self):
        """
        Activate tool.
        """
        if self.toolAction:
            self.toolAction.setChecked(True)
        QgsMapTool.activate(self)
        self.iface.mapCanvas().setMapTool(self)
        layer = self.iface.mapCanvas().currentLayer()
        if not layer or not isinstance(layer, QgsVectorLayer):
            self.iface.messageBar().pushMessage(self.tr("Warning"), self.tr("Select a point vector layer as the active layer"),
                                                level=QgsMessageBar.INFO, duration=10)
            self.deactivate()

    def reprojectSearchArea(self, layer, geom):
        """
        Reprojects search area if necessary, according to what is being searched.
        :param layer: (QgsVectorLayer) layer which target rectangle has to have same SRC.
        :param geom: (QgsRectangle) rectangle representing search area.
        """
        #geom always have canvas coordinates
        epsg = self.canvas.mapSettings().destinationCrs().authid()
        #getting srid from something like 'EPSG:31983'
        srid = layer.crs().authid()
        if epsg == srid:
            return geom
        crsSrc = QgsCoordinateReferenceSystem(epsg)
        crsDest = QgsCoordinateReferenceSystem(srid)
        # Creating a transformer
        coordinateTransformer = QgsCoordinateTransform(crsSrc, crsDest) # here we have to put authid, not srid
        auxGeom = QgsGeometry.fromRect(geom)
        auxGeom.transform(coordinateTransformer)
        return auxGeom.boundingBox()

    def getPixelValue(self, rasterLayer):
        mousePos = self.qgsMapToolEmitPoint.toMapCoordinates(self.canvas.mouseLastXY())
        mousePosGeom = QgsGeometry.fromPoint(mousePos)
        return self.getPixelValueFromPoint(mousePosGeom, rasterLayer), mousePosGeom

    def getPixelValueFromPoint(self, mousePosGeom, rasterLayer, fromCanvas = True):
        """
        
        """
        rasterCrs = rasterLayer.crs() if rasterLayer else False
        if rasterCrs:
            if fromCanvas:
                self.dsgGeometryHandler.reprojectFeature(mousePosGeom, rasterCrs, self.canvas.mapRenderer().destinationCrs())
            else:
                mousePosGeom = QgsGeometry(mousePosGeom)
                self.dsgGeometryHandler.reprojectFeature(mousePosGeom, rasterCrs, self.canvas.currentLayer().crs())
            # isMulti = QgsWKBTypes.isMultiType(int(layer.wkbType())) # tem que ver se serve pra QgsGeometry
            mousePos = mousePosGeom.asMultiPoint()[0] if mousePosGeom.isMultipart() else mousePosGeom.asPoint()
            # identify pixel(s) information
            i = rasterLayer.dataProvider().identify( mousePos, QgsRaster.IdentifyFormatValue )
            if i.isValid():
                value = i.results().values()[0]
                if value:
                    value = int(value) if self.decimals == 0 else round(value, self.decimals)
                return value
            else:
                return None
    
    def getPixelValueFromPointDict(self, pointDict, rasterLayer):
        """
        pointDict = {'pointId':QgsGeometry}

        returns {'pointId': value}
        """
        return {key : self.getPixelValueFromPoint(value, rasterLayer, fromCanvas=False) for key, value in pointDict.iteritems()} #no python3 eh items()
예제 #14
0
class BandValueTool(QgsMapTool):
    """
    This class is supposed to help revision operators. It shows, on mouse hovering
    raster layer's band values. For a MDS product, altimetry is, then, given.
    Tool Behaviour:
    1- On hoverring a pixel: expose band value(s)
    2- On mouse click: create a new instance of desired layer (filled on config).
        * behaviour 2 is an extrapolation of first conception
    """
    def __init__(self, iface, parent):
        """
        Class constructor.
        """
        # super(QgsRasterLayer, self).__init__()
        self.canvas = iface.mapCanvas()
        super(BandValueTool, self).__init__(self.canvas)
        self.parent = parent
        self.iface = iface
        self.toolAction = None
        self.QgsMapToolEmitPoint = QgsMapToolEmitPoint(self.canvas)
        self.DsgGeometryHandler = DsgGeometryHandler(iface)
        self.timerMapTips = QTimer( self.canvas )
        self.timerMapTips.timeout.connect( self.showToolTip )
        self.activated = False
    
    def setAction(self, action):
        """
        
        """
        self.toolAction = action

    def activate(self):
        """
        Activates tool.
        """
        if self.toolAction:
            self.activated = True
        QgsMapTool.activate(self)
        self.canvas.setMapTool(self)
    
    def deactivate(self):
        """
        Deactivates tool.
        """
        self.timerMapTips.stop()
        try:
            if self.toolAction:
                self.activated = False
                self.toolAction.setChecked(False)
            if self is not None:
                QgsMapTool.deactivate(self)
        except:
            pass        

    def canvasMoveEvent(self, e):
        QToolTip.hideText()
        self.timerMapTips.start( 500 ) # time in milliseconds
        self.showToolTip()               
    
    def getPixelValue(self, rasterLayer):
        """
        
        """
        rasterCrs = rasterLayer.crs()
        mousePos = self.QgsMapToolEmitPoint.toMapCoordinates(self.canvas.mouseLastXY())
        mousePosGeom = QgsGeometry.fromPoint(mousePos)
        self.DsgGeometryHandler.reprojectFeature(mousePosGeom, rasterCrs, self.canvas.mapRenderer().destinationCrs())
        mousePos = mousePosGeom.asPoint()
        # identify pixel(s) information
        i = rasterLayer.dataProvider().identify( mousePos, QgsRaster.IdentifyFormatValue )
        if i.isValid():
            text = ", ".join(['{0:g}'.format(r) for r in i.results().values() if r is not None] )
        else:
            text = ""
        return text

    def showToolTip(self):
        """
        
        """
        self.timerMapTips.stop()
        if self.canvas.underMouse():
            raster = self.parent.rasterComboBox.currentLayer()
            if raster:
                text = self.getPixelValue(raster)
                p = self.canvas.mapToGlobal( self.canvas.mouseLastXY() )
                QToolTip.showText( p, text, self.canvas )