Example #1
0
class RasterLegendSensitive(QObject):
    def __init__(self, iface, treeView, ckEnabled):
        super(RasterLegendSensitive, self).__init__()
        self.tree = TreeLegend(treeView)
        self.ckEnabled = ckEnabled
        #
        self.layer = self.worker = self.thread = self.transparencyLayer = None
        self.isExtentLayer = self.valuesFullExtent = None
        self.hasConnect = self.hasConnectTree = None
        self.iface = iface
        self.legend = iface.legendInterface()
        self.canvas = iface.mapCanvas()
        self.msgBar = iface.messageBar()
        self.nameModulus = "RasterLegendSensitive"
        #
        self.initThread()
        self._connect()
        self._connectTree()

    def __del__(self):
        del self.tree
        self.finishThread()
        if not self.hasConnect:
            self._connect(False)
        if not self.layer is None:
            self.setTransparenceLayer([])

    def initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName(self.nameModulus)
        self.worker = WorkerRasterLegendSensitive()
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [{
            'signal': self.thread.started,
            'slot': self.worker.run
        }, {
            'signal': self.worker.finished,
            'slot': self.finishedWorker
        }, {
            'signal': self.worker.messageStatus,
            'slot': self.messageStatusWorker
        }]
        if isConnect:
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _connect(self, isConnect=True):
        ss = [{
            'signal': self.legend.currentLayerChanged,
            'slot': self.selectLayer
        }, {
            'signal': QgsMapLayerRegistry.instance().layerWillBeRemoved,
            'slot': self.removeLayer
        }, {
            'signal': self.canvas.extentsChanged,
            'slot': self.changeSensitiveLegend
        }]
        if isConnect:
            self.hasConnect = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnect = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _connectTree(self, isConnect=True):
        ss = [{
            'signal': self.tree.toggledLegend,
            'slot': self.setTransparenceLayer
        }, {
            'signal': self.tree.descriptionLegend,
            'slot': self.sendClipboard
        }]
        if isConnect:
            self.hasConnectTree = True
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            self.hasConnectTree = False
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def _resetLayer(self):
        if self.thread.isRunning():
            self.worker.isKilled = True
        self.layer = None
        self.tree.setHeader()
        self.tree.layer = None

    def setEnabled(self, isEnabled=True):
        if not isEnabled and self.thread.isRunning():
            self.worker.isKilled = True
        #
        self._connect(isEnabled)
        self._connectTree(isEnabled)
        self.tree.setEnabled(isEnabled)
        #
        if isEnabled:
            activeLayer = self.iface.activeLayer()
            if activeLayer == self.layer:
                if activeLayer is None:
                    return
                self.changeSensitiveLegend()
            else:
                self.selectLayer(activeLayer)

    @pyqtSlot(list)
    def finishedWorker(self, values):
        self.thread.quit()
        self.msgBar.popWidget()
        if not self.worker.isKilled:
            if len(values) > 0:  # Never Happing otherwise...
                self.tree.setLegend(values)
                if self.isExtentLayer:
                    self.valuesFullExtent = values
        else:  # When PAN/ZOOM/...
            self.thread.wait()
            if self.ckEnabled.checkState() == Qt.Checked:
                self.changeSensitiveLegend()

    @pyqtSlot(str)
    def messageStatusWorker(self, msg):
        self.msgBar.popWidget()
        self.msgBar.pushMessage(self.nameModulus, msg, QgsMessageBar.INFO)

    @pyqtSlot(list)
    def setTransparenceLayer(self, visibleItems):
        def refreshLayer():
            if hasattr(self.layer, "setCacheImage"):
                self.layer.setCacheImage(None)  # Refresh
            else:
                self.layer.triggerRepaint()

        def setTransparence(value, visible):
            t = QgsRasterTransparency.TransparentSingleValuePixel()
            t.min = t.max = value
            percent = 100.0 if not visible else 0.0
            t.percentTransparent = percent

            return t

        valuesTransparent = []
        item = 0
        for visible in visibleItems:
            valuesTransparent.append(setTransparence(item, visible))
            item += 1
        self.transparencyLayer.setTransparentSingleValuePixelList(
            valuesTransparent)
        refreshLayer()
        del valuesTransparent[:]

    @pyqtSlot(str)
    def sendClipboard(self, description):

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()
        extentLayer = self.layer.extent()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(
                self.layer, extentCanvas)

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            msg = "Calculate for all extent of layer '%s'\n\n%s" % (
                self.layer.name(), description)
        else:
            msg = "Calculate for subset of layer '%s' (extend: %s)\n\n%s" % (
                self.layer.name(), extentCanvas.toString(), description)
        clip = QApplication.clipboard()
        clip.setText(msg)
        self.msgBar.pushMessage(self.nameModulus, "Copy to Clipboard",
                                QgsMessageBar.INFO, 5)

    @pyqtSlot('QgsMapLayer')
    def selectLayer(self, layer):
        def processLegend():
            self.tree.setLayer(layer)
            if not self.valuesFullExtent is None:  # Save legend with all extent layer
                del self.valuesFullExtent[:]
                self.valuesFullExtent = None
            (self.layer,
             self.transparencyLayer) = (layer,
                                        layer.renderer().rasterTransparency())
            self.worker.setLegendReadBlock(layer)
            self.changeSensitiveLegend()

        if not self.layer is None:
            if not self.layer in self.legend.layers():
                self.removeLayer(self.layer.id())
            else:
                self.setTransparenceLayer([])
                self._resetLayer()

        if not layer is None and layer.type() == QgsMapLayer.RasterLayer:
            legendItems = layer.legendSymbologyItems()
            total = len(legendItems)
            if total > 0:  # Had a classification
                processLegend()

    @pyqtSlot(str)
    def removeLayer(self, idLayer):
        if not self.layer is None and self.layer.id() == idLayer:
            msg = "Layer '%s' was removed" % self.tree.getLayerName()
            self.msgBar.pushMessage(self.nameModulus, msg,
                                    QgsMessageBar.WARNING, 5)
            self._resetLayer()

    @pyqtSlot()
    def changeSensitiveLegend(self):
        if self.layer is None:
            return

        if self.thread.isRunning():
            self.worker.isKilled = True
            return

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()

        extentLayer = self.layer.extent()
        resX = self.layer.rasterUnitsPerPixelX()
        resY = self.layer.rasterUnitsPerPixelY()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(
                self.layer, extentCanvas)

        if not extentCanvas.intersects(extentLayer):
            self.msgBar.popWidget()
            self.msgBar.pushMessage(
                self.nameModulus,
                "View not intersects Raster '%s'" % self.layer.name(),
                QgsMessageBar.WARNING, 5)
            self.tree.setLegend([])
            return

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            self.isExtentLayer = True
            if not self.valuesFullExtent is None:
                self.tree.setLegend(self.valuesFullExtent)
                return
            extent = extentLayer
            delta = 0
        else:
            self.isExtentLayer = False
            extent = extentCanvas.intersect(extentLayer)
            delta = 1

        widthRead = int(extent.width() / resX) + delta
        heightRead = int(extent.height() / resY) + delta

        self.worker.setProcessImage(extent, widthRead, heightRead)
        self.thread.start()
class SensitiveLegendRaster(QObject):
    def __init__(self):
        super(SensitiveLegendRaster, self).__init__()  # QThread
        self.layer = self.worker = self.thread = None
        self.canvas = iface.mapCanvas()
        self.msgBar = iface.messageBar()
        self.legend = iface.legendInterface()
        self.nameModulus = "Script_Sensitive_Legend"
        #
        self.initThread()
        self._connect()
        #
        self.selectLayer(iface.activeLayer())

    def __del__(self):
        self.finishThread()
        self._connect(False)

    def printMsgBar(self, msg, typeMsg=QgsMessageBar.INFO):
        self.msgBar.popWidget()
        if typeMsg == QgsMessageBar.INFO:
            self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg)
        else:
            self.msgBar.pushMessage("SensitiveLegendRaster Script", msg, typeMsg, 5)

    def initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName(self.nameModulus)
        self.worker = WorkerSensitiveLegendRaster()
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [
            {"signal": self.thread.started, "slot": self.worker.run},
            {"signal": self.worker.finished, "slot": self.finishedWorker},
            {"signal": self.worker.messageResult, "slot": self.messageResultWorker},
            {"signal": self.worker.messageStatus, "slot": self.messageStatusWorker},
        ]
        if isConnect:
            for item in ss:
                item["signal"].connect(item["slot"])
        else:
            for item in ss:
                item["signal"].disconnect(item["slot"])

    def _connect(self, isConnect=True):
        ss = [
            {"signal": self.legend.currentLayerChanged, "slot": self.selectLayer},
            {"signal": QgsMapLayerRegistry.instance().layerWillBeRemoved, "slot": self.unselectLayer},
            {"signal": self.canvas.extentsChanged, "slot": self.changeSensitiveLegend},
        ]
        if isConnect:
            for item in ss:
                item["signal"].connect(item["slot"])
        else:
            for item in ss:
                item["signal"].disconnect(item["slot"])

    @pyqtSlot()
    def finishedWorker(self):
        self.thread.quit()
        if self.worker.isKilled:  # When PAN/ZOOM/...
            self.thread.wait()
            self.changeSensitiveLegend()

    @pyqtSlot(str)
    def messageResultWorker(self, msg):
        self.printMsgBar(msg)

    @pyqtSlot(str)
    def messageStatusWorker(self, msg):
        self.printMsgBar(msg)

    @pyqtSlot("QgsMapLayer")
    def selectLayer(self, layer):

        if self.thread.isRunning():
            return

        isOk = True
        msg = ""
        typeMsg = QgsMessageBar.WARNING
        if not layer is None and layer.type() == QgsMapLayer.RasterLayer:
            legendColorAll = layer.legendSymbologyItems()
            if len(legendColorAll) > 0:  # Had a classification
                self.layer = layer
                self.worker.setLegendReadBlock(layer)
                msg = "Raster Layer '%s' actived" % layer.name()
                typeMsg = QgsMessageBar.INFO
            else:
                msg = "Raster Layer '%s' need be a classification" % layer.name()
                isOk = False
        else:
            if layer is None:
                msg = "Active a Raster layer"
            else:
                msg = "Layer '%s' need be a Raster" % layer.name()
            isOk = False

        self.printMsgBar(msg, typeMsg)

        return isOk

    @pyqtSlot(str)
    def unselectLayer(self, idLayer):
        if idLayer == self.layer.id():
            if self.thread.isRunning():
                self.worker.isKilled = True
            msg = "Raster Layer '%s' was removed" % self.layer.name()
            self.printMsgBar(msg, QgsMessageBar.WARNING)
            self.layer = None

    @pyqtSlot()
    def changeSensitiveLegend(self):

        if self.layer is None:
            return

        if self.thread.isRunning():
            self.worker.isKilled = True
            return

        mapSettings = self.canvas.mapSettings()
        crsCanvas = mapSettings.destinationCrs()
        extentCanvas = self.canvas.extent()

        extentLayer = self.layer.extent()
        resX = self.layer.rasterUnitsPerPixelX()
        resY = self.layer.rasterUnitsPerPixelY()

        if self.layer.crs() != crsCanvas:
            extentCanvas = mapSettings.mapToLayerCoordinates(self.layer, extentCanvas)

        if not extentCanvas.intersects(extentLayer):
            self.printMsgBar("View not intersects Raster '%s'" % self.layer.name, QgsMessageBar.WARNING)
            return

        keysLegend = range(len(self.worker.legendAll))  # [ idClass1, idClass2, ... ] [ 0..N-1]

        if extentCanvas == extentLayer or extentCanvas.contains(extentLayer):
            legendsView = map(lambda x: "(%s)%s" % (x, self.worker.legendAll[x]), keysLegend)
            msg = "[%d] = %s" % (len(legendsView), " ".join(legendsView))
            self.printMsgBar(msg)
            return

        extent = extentCanvas.intersect(extentLayer)
        widthRead = int(extent.width() / resX) + 1
        heightRead = int(extent.height() / resY) + 1

        self.worker.setProcessImage(extent, widthRead, heightRead)
        self.thread.start()
class CatalogOTF(QObject):

    # Signals
    settedLayer = pyqtSignal("QgsVectorLayer")
    removedLayer = pyqtSignal(str)
    killed = pyqtSignal(str)
    changedNameLayer = pyqtSignal(str, str)
    changedTotal = pyqtSignal(str, str)
    changedIconRun = pyqtSignal(str, bool)

    def __init__(self, iface, tableCOTF):
        def connecTableCOTF():
            self.settedLayer.connect(tableCOTF.insertRow)
            self.removedLayer.connect(tableCOTF.removeRow)
            self.changedNameLayer.connect(tableCOTF.changedNameLayer)
            self.changedTotal.connect(tableCOTF.changedTotal)
            self.changedIconRun.connect(tableCOTF.changedIconRun)
            self.killed.connect(tableCOTF.killed)

        super(CatalogOTF, self).__init__()
        self.iface = iface
        self.canvas = iface.mapCanvas()
        self.ltv = iface.layerTreeView()
        self.model = self.ltv.layerTreeModel()
        self.ltgRoot = QgsProject.instance().layerTreeRoot()
        self.msgBar = iface.messageBar()
        self.legendTMS = LegendTMS('Catalog OTF')
        self.legendRaster = LegendRaster('Catalog OTF')

        self._initThread()

        connecTableCOTF()
        self.model.dataChanged.connect(self.dataChanged)
        QgsMapLayerRegistry.instance().layersWillBeRemoved.connect(
            self.layersWillBeRemoved)  # Catalog layer removed

        self.layer = self.layerName = self.nameFieldSource = self.nameFieldDate = None
        self.ltgCatalog = self.ltgCatalogName = self.visibleSourceLayers = self.hasCanceled = None

    def __del__(self):
        self._finishThread()
        del self.legendTMS
        del self.legendRaster
        QgsMapLayerRegistry.instance().layersWillBeRemoved.disconnect(
            self.layersWillBeRemoved)  # Catalog layer removed

    def _initThread(self):
        self.thread = QThread(self)
        self.thread.setObjectName("QGIS_Plugin_%s" %
                                  NAME_PLUGIN.replace(' ', '_'))
        self.worker = WorkerPopulateGroup(self.addLegendLayerWorker)
        self.worker.moveToThread(self.thread)
        self._connectWorker()

    def _finishThread(self):
        self._connectWorker(False)
        self.worker.deleteLater()
        self.thread.wait()
        self.thread.deleteLater()
        self.thread = self.worker = None

    def _connectWorker(self, isConnect=True):
        ss = [{
            'signal': self.thread.started,
            'slot': self.worker.run
        }, {
            'signal': self.worker.finished,
            'slot': self.finishedPG
        }, {
            'signal': self.worker.messageStatus,
            'slot': self.messageStatusPG
        }, {
            'signal': self.worker.messageError,
            'slot': self.messageErrorPG
        }]
        if isConnect:
            for item in ss:
                item['signal'].connect(item['slot'])
        else:
            for item in ss:
                item['signal'].disconnect(item['slot'])

    def addLegendLayerWorker(self, layer):
        if layer.type() == QgsMapLayer.RasterLayer:
            metadata = layer.metadata()
            if metadata.find("GDAL provider") != -1:
                if metadata.find("OGC Web Map Service") != -1:
                    if self.legendTMS.hasTargetWindows(layer):
                        self.legendTMS.setLayer(layer)
                else:
                    self.legendRaster.setLayer(layer)

    def run(self):
        self.hasCanceled = False  # Check in finishedPG

        if self.thread.isRunning():
            self.worker.kill()
            self.hasCanceled = True
            msgtrans = QCoreApplication.translate(
                "CatalogOTF", "Canceled search for image from layer %s")
            msg = msgtrans % self.layerName
            self.msgBar.pushMessage(NAME_PLUGIN, msg, QgsMessageBar.WARNING, 2)
            self.changedTotal.emit(self.layer.id(), "Canceling processing")
            self.killed.emit(self.layer.id())
            return

        if self.layer is None:
            msgtrans = QCoreApplication.translate("CatalogOTF",
                                                  "Need define layer catalog")
            self.msgBar.pushMessage(NAME_PLUGIN, msgtrans,
                                    QgsMessageBar.WARNING, 2)
            return

        self._setGroupCatalog()
        self.ltgCatalogName = self.ltgCatalog.name()

        renderFlag = self.canvas.renderFlag()
        if renderFlag:
            self.canvas.setRenderFlag(False)
            self.canvas.stopRendering()

        self._populateGroupCatalog()

        if renderFlag:
            self.canvas.setRenderFlag(True)
            self.canvas.refresh()

    def _populateGroupCatalog(self):
        def getSourceVisibleLayers():
            def hasVisibleRaster(ltl):
                return ltl.isVisible() == Qt.Checked and ltl.layer().type(
                ) == QgsMapLayer.RasterLayer

            l_ltlVisible = filter(lambda item: hasVisibleRaster(item),
                                  self.ltgCatalog.findLayers())
            return map(lambda item: item.layer().source(), l_ltlVisible)

        def runWorker():
            data = {}
            data['nameFieldDate'] = self.nameFieldDate
            data['nameFieldSource'] = self.nameFieldSource
            data['layer'] = self.layer
            data['ltgCatalog'] = self.ltgCatalog
            self.worker.setData(data)
            self.thread.start()
            #self.worker.run() # DEBUG

        self.visibleSourceLayers = getSourceVisibleLayers()
        self.ltgCatalog.removeAllChildren()
        runWorker()  # See finishPG

    def _setGroupCatalog(self):
        self.ltgCatalogName = "%s - Catalog" % self.layer.name()
        self.ltgCatalog = self.ltgRoot.findGroup(self.ltgCatalogName)
        if self.ltgCatalog is None:
            self.ltgCatalog = self.ltgRoot.addGroup(self.ltgCatalogName)

    @pyqtSlot(bool)
    def finishedPG(self, isKilled):
        def setSourceVisibleLayers():
            l_ltlVisible = filter(
                lambda item: item.layer().source() in self.visibleSourceLayers,
                self.ltgCatalog.findLayers())
            map(lambda item: item.setVisible(Qt.Checked), l_ltlVisible)

        self.thread.quit()

        if not self.layer is None:
            self.changedIconRun.emit(self.layer.id(),
                                     self.layer.selectedFeatureCount() > 0)
            if self.hasCanceled:
                self.changedTotal.emit(self.layer.id(), '0')
            else:
                setSourceVisibleLayers()

        del self.visibleSourceLayers[:]

    @pyqtSlot(str)
    def messageStatusPG(self, msg):
        self.changedTotal.emit(self.layer.id(), msg)

    @pyqtSlot(str)
    def messageErrorPG(self, msg):
        self.msgBar.pushMessage(NAME_PLUGIN, msg, QgsMessageBar.CRITICAL, 8)

    @pyqtSlot('QModelIndex', 'QModelIndex')
    def dataChanged(self, idTL, idBR):
        if idTL != idBR:
            return

        if not self.ltgCatalog is None and self.ltgCatalog == self.model.index2node(
                idBR):
            name = self.ltgCatalog.name()
            if self.ltgCatalogName != name:
                self.ltgCatalogName = name
                return

        if not self.layer is None and self.ltgRoot.findLayer(
                self.layer.id()) == self.model.index2node(idBR):
            name = self.layer.name()
            if self.layerName != name:
                self.changedNameLayer.emit(self.layer.id(), name)
                self.layerName = name

    @pyqtSlot(list)
    def layersWillBeRemoved(self, layerIds):
        if self.layer is None:
            return
        if self.layer.id() in layerIds:
            self.removedLayer.emit(self.layer.id())
            self.removeLayerCatalog()

    @staticmethod
    def getNameFieldsCatalog(layer):
        def getFirstFeature():
            f = QgsFeature()
            #
            fr = QgsFeatureRequest(
            )  # First FID can be 0 or 1 depend of provider type
            it = layer.getFeatures(fr)
            isOk = it.nextFeature(f)
            it.close()
            #
            if not isOk or not f.isValid():
                del f
                return None
            else:
                return f

        def hasAddress(feature, nameField):
            def asValidUrl(url):
                isOk = True
                try:
                    urllib2.urlopen(url)
                except urllib2.HTTPError, e:
                    isOk = False
                except urllib2.URLError, e:
                    isOk = False
                #
                return isOk

            value = feature.attribute(nameField)
            if value is None or type(value) == QPyNullVariant:
                return False

            isUrl = value.find('http://') == 0 or value.find('https://') == 0
            lenSource = len(value)
            isUrl = isUrl and value.rfind(
                'xml', lenSource - len('xml')) == lenSource - len('xml')
            if isUrl:
                return asValidUrl(value)
            #
            fileInfo = QFileInfo(value)
            return fileInfo.isFile()