Ejemplo n.º 1
0
 def loadFrameLayer(self):
     """
     Loads the frame layer case it is not loaded yet.
     """
     loader = LayerLoaderFactory().makeLoader(self.iface,
                                              self.widget.abstractDb)
     if loader.provider == 'postgres':
         layerMeta = {
             'cat': 'aux',
             'geom': 'geom',
             'geomType': 'MULTIPOLYGON',
             'lyrName': 'moldura_a',
             'tableName': 'aux_moldura_a',
             'tableSchema': 'public',
             'tableType': 'BASE TABLE'
         }
     elif loader.provider == 'spatialite':
         layerMeta = {
             'cat': 'aux',
             'geom': 'GEOMETRY',
             'geomType': 'MULTIPOLYGON',
             'lyrName': 'moldura_a',
             'tableName': 'aux_moldura_a',
             'tableSchema': 'public',
             'tableType': 'BASE TABLE'
         }
     else:
         layerMeta = None
     layerDict = loader.load([layerMeta], uniqueLoad=True)
     if layerMeta['lyrName'] in layerDict.keys():
         layer = layerDict[layerMeta['lyrName']]
     else:
         layer = None
     return layer
Ejemplo n.º 2
0
 def __init__(self, iface, parent=None):
     """Constructor."""
     super(self.__class__, self).__init__(parent)
     self.iface = iface
     self.utils = Utils()
     self.setupUi(self)
     self.layerFactory = LayerLoaderFactory()
     self.customServerConnectionWidget.postgisCustomSelector.setTitle(
         self.tr('Select Databases'))
     self.customServerConnectionWidget.spatialiteCustomSelector.setTitle(
         self.tr('Selected Spatialites'))
     self.layersCustomSelector.setTitle(
         self.tr('Select layers to be loaded'))
     self.customServerConnectionWidget.dbDictChanged.connect(
         self.updateLayersFromDbs)
     self.customServerConnectionWidget.resetAll.connect(self.resetInterface)
     self.customServerConnectionWidget.styleChanged.connect(
         self.populateStyleCombo)
     self.headerList = [
         self.tr('Category'),
         self.tr('Layer Name'),
         self.tr('Geometry\nColumn'),
         self.tr('Geometry\nType'),
         self.tr('Layer\nType')
     ]
     self.layersCustomSelector.setHeaders(self.headerList)
     self.customServerConnectionWidget.postgisEdgvComboFilter.currentIndexChanged.connect(
         self.layersCustomSelector.setInitialState)
     self.customServerConnectionWidget.spatialiteEdgvComboFilter.currentIndexChanged.connect(
         self.layersCustomSelector.setInitialState)
     self.customServerConnectionWidget.serverConnectionTab.currentChanged.connect(
         self.layersCustomSelector.setInitialState)
     self.lyrDict = dict()
Ejemplo n.º 3
0
 def loadFlagLyr(self, layer):
     """
     Loads the flag layer. It checks if the flag layer is already loaded, case not, it loads the flag layer into the TOC
     layer: layer name
     """
     self.layerLoader = LayerLoaderFactory().makeLoader(
         self.iface, self.configWindow.widget.abstractDb)
     return self.layerLoader.load([layer],
                                  uniqueLoad=True)[layer['lyrName']]
Ejemplo n.º 4
0
 def defineFactory(self, abstractDb):
     """
     Defines the layer loader by its type
     :param abstractDb:
     :return:
     """
     self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, abstractDb)
     try:
         self.populateConfigFromDb()
     except Exception as e:
         QgsMessageLog.logMessage(self.tr('Error getting stored configuration.\n')+':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
Ejemplo n.º 5
0
 def __init__(self, postgisDb, iface, instantiating=False):
     """
     Constructor
     """
     super(ValidationProcess, self).__init__()
     self.abstractDb = postgisDb
     if self.getStatus() == None:
         self.setStatus(self.tr('Instantianting process'), 0)
     self.classesToBeDisplayedAfterProcess = []
     self.parameters = None
     self.iface = iface
     self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, self.abstractDb)
     self.processAlias = self.tr('Validation Process')
     self.instantiating = instantiating
Ejemplo n.º 6
0
 def defineFactory(self, abstractDb):
     """
     Defines the layer loader by its type
     :param abstractDb:
     :return:
     """
     self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, abstractDb)
     try:
         self.populateConfigFromDb()
     except Exception as e:
         QgsMessageLog.logMessage(self.tr('Error getting stored configuration.\n')+':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
Ejemplo n.º 7
0
    def __init__(self, iface, parent=None):
        """
        Constructor
        """
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface
        self.layerFactory = LayerLoaderFactory()
        self.selectedClasses = []
        self.widget.tabWidget.setTabEnabled(0,True)
        self.widget.tabWidget.setTabEnabled(1,False)
        self.widget.tabWidget.setCurrentIndex(0)
        self.bar = QgsMessageBar()
        self.setLayout(QtGui.QGridLayout(self))
        self.layout().setContentsMargins(0,0,0,0)
        self.layout().setAlignment(QtCore.Qt.AlignTop)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
        self.bar.setSizePolicy(sizePolicy)
        self.layout().addWidget(self.bar, 0,0,1,1)

        QtCore.QObject.connect(self.widget, QtCore.SIGNAL(("problemOccurred()")), self.pushMessage)
        self.widget.dbChanged.connect(self.widgetConv.setDatabase)
Ejemplo n.º 8
0
 def __init__(self, iface, parent=None):
     """Constructor."""
     super(self.__class__, self).__init__(parent)
     self.iface = iface
     self.utils = Utils()
     self.setupUi(self)
     self.layerFactory = LayerLoaderFactory()
     self.customServerConnectionWidget.postgisCustomSelector.setTitle(self.tr('Select Databases'))
     self.customServerConnectionWidget.spatialiteCustomSelector.setTitle(self.tr('Selected Spatialites'))
     self.layersCustomSelector.setTitle(self.tr('Select layers to be loaded'))
     self.customServerConnectionWidget.dbDictChanged.connect(self.updateLayersFromDbs)
     self.customServerConnectionWidget.resetAll.connect(self.resetInterface)
     self.customServerConnectionWidget.styleChanged.connect(self.populateStyleCombo)
     self.headerList = [self.tr('Category'), self.tr('Layer Name'), self.tr('Geometry\nColumn'), self.tr('Geometry\nType'), self.tr('Layer\nType')]
     self.layersCustomSelector.setHeaders(self.headerList)
     self.customServerConnectionWidget.postgisEdgvComboFilter.currentIndexChanged.connect(self.layersCustomSelector.setInitialState)
     self.customServerConnectionWidget.spatialiteEdgvComboFilter.currentIndexChanged.connect(self.layersCustomSelector.setInitialState)
     self.customServerConnectionWidget.serverConnectionTab.currentChanged.connect(self.layersCustomSelector.setInitialState)
     self.lyrDict = dict()
Ejemplo n.º 9
0
class ValidationProcess(QObject):
    def __init__(self, postgisDb, iface, instantiating=False):
        """
        Constructor
        """
        super(ValidationProcess, self).__init__()
        self.abstractDb = postgisDb
        if self.getStatus() == None:
            self.setStatus(self.tr('Instantianting process'), 0)
        self.classesToBeDisplayedAfterProcess = []
        self.parameters = None
        self.iface = iface
        self.layerLoader = LayerLoaderFactory().makeLoader(
            self.iface, self.abstractDb)
        self.processAlias = self.tr('Validation Process')
        self.instantiating = instantiating
        self.totalTime = 0
        self.startTime = 0
        self.endTime = 0
        self.dbUserName = None
        self.logMsg = None
        self.processName = None

    def getFlagLyr(self, dimension):
        if dimension == 0:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOINT',
                'lyrName': 'flags_validacao_p',
                'tableName': 'aux_flags_validacao_p',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 1:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTILINESTRING',
                'lyrName': 'flags_validacao_l',
                'tableName': 'aux_flags_validacao_l',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 2:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOLYGON',
                'lyrName': 'flags_validacao_a',
                'tableName': 'aux_flags_validacao_a',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        return self.loadLayerBeforeValidationProcess(layer)

    def setProcessName(self, processName):
        """
        Identifies the process name as it is.
        """
        self.processName = processName

    def setDbUserName(self, userName):
        """
        Identifies the database username.
        """
        self.dbUserName = userName

    def setParameters(self, params):
        """
        Define the process parameteres
        """
        self.parameters = params

    def execute(self):
        """
        Abstract method. MUST be reimplemented.
        """
        pass

    def shouldBeRunAgain(self):
        """
        Defines if the method should run again later
        """
        return False

    def getName(self):
        """
        Gets the process name
        """
        return str(self.__class__).split('.')[-1].replace('\'>', '')

    def getProcessGroup(self):
        """
        Returns the process group
        """
        return 'Ungrouped'

    def getClassesToBeDisplayedAfterProcess(self):
        """
        Returns classes to be loaded to TOC after executing this process.
        """
        return self.classesToBeDisplayedAfterProcess

    def addClassesToBeDisplayedList(self, className):
        """
        Add a class into the class list that will be displayed after the process
        """
        if className not in self.classesToBeDisplayedAfterProcess:
            self.classesToBeDisplayedAfterProcess.append(className)

    def clearClassesToBeDisplayedAfterProcess(self):
        """
        Clears the class list to be displayed
        """
        self.classesToBeDisplayedAfterProcess = []

    def preProcess(self):
        """
        Returns the name of the pre process that must run before, must be reimplemented in each process
        """
        return None

    def postProcess(self):
        """
        Returns the name of the post process that must run after, must be reimplemented in each process
        """
        return None

    def addFlag(self, flagTupleList):
        """
        Adds flags
        flagTUpleList: list of tuples to be added as flag
        """
        try:
            return self.abstractDb.insertFlags(flagTupleList, self.getName())
        except Exception as e:
            QMessageBox.critical(
                None, self.tr('Critical!'),
                self.
                tr('A problem occurred inserting flags! Check log for details.'
                   ))
            QgsMessageLog.logMessage(str(e.args[0]), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)

    def removeFeatureFlags(self, layer, featureId):
        """
        Removes specific flags from process
        layer: Name of the layer that should be remove from the flags
        featureId: Feature id from layer name that must be removed
        """
        try:
            return self.abstractDb.removeFeatureFlags(layer, featureId,
                                                      self.getName())
        except Exception as e:
            QMessageBox.critical(
                None, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)

    def getStatus(self):
        """
        Gets the process status
        """
        try:
            return self.abstractDb.getValidationStatus(self.getName())
        except Exception as e:
            QMessageBox.critical(
                None, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)

    def getStatusMessage(self):
        """
        Gets the status message text
        """
        try:
            return self.abstractDb.getValidationStatusText(self.getName())
        except Exception as e:
            QMessageBox.critical(
                None, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)

    def setStatus(self, msg, status):
        """
        Sets the status message
        status: Status number
        msg: Status text message
        """
        try:
            if status not in [
                    0, 3
            ]:  # neither running nor instatiating status should be logged
                self.logProcess()
                if self.logMsg:
                    msg += "\n" + self.logMsg
                elif not self.dbUserName:
                    msg += self.tr("Database username: {}\n").format(
                        self.abstractDb.db.userName())
            self.abstractDb.setValidationProcessStatus(self.getName(), msg,
                                                       status)
        except Exception as e:
            QMessageBox.critical(
                None, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)

    def finishedWithError(self):
        """
        Sets the finished with error status (status number 2)
        Clears the classes to be displayed
        """
        self.setStatus(self.tr('Process finished with errors.'),
                       2)  #Failed status
        #drop temps
        try:
            classesWithElem = self.parameters['Classes']
            for cl in classesWithElem:
                tempName = cl.split(':')[0] + '_temp'
                self.abstractDb.dropTempTable(tempName)
        except:
            pass
        self.clearClassesToBeDisplayedAfterProcess()

    def inputData(self):
        """
        Returns current active layers
        """
        return self.iface.mapCanvas().layers()

    def getTableNameFromLayer(self, lyr):
        """
        Gets the layer name as present in the rules
        """
        uri = lyr.dataProvider().dataSourceUri()
        dsUri = QgsDataSourceURI(uri)
        name = '.'.join([dsUri.schema(), dsUri.table()])
        return name

    def mapInputLayer(self, inputLyr, selectedFeatures=False):
        """
        Gets the layer features considering the edit buffer in the case
        the layer is already in edition mode
        """
        #return dict
        featureMap = dict()
        #getting only selected features
        if selectedFeatures:
            for feat in inputLyr.selectedFeatures():
                featureMap[feat.id()] = feat
        #getting all features
        else:
            for feat in inputLyr.getFeatures():
                featureMap[feat.id()] = feat
        return featureMap

    def updateOriginalLayer(self,
                            pgInputLayer,
                            qgisOutputVector,
                            featureList=None,
                            featureTupleList=None):
        """
        Updates the original layer using the grass output layer
        pgInputLyr: postgis input layer
        qgisOutputVector: qgis output layer
        Speed up tips: http://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/
        """
        provider = pgInputLayer.dataProvider()
        # getting keyColumn because we want to be generic
        uri = QgsDataSourceURI(pgInputLayer.dataProvider().dataSourceUri())
        keyColumn = uri.keyColumn()
        # starting edition mode
        pgInputLayer.startEditing()
        addList = []
        idsToRemove = []
        #making the changes and inserts
        for feature in pgInputLayer.getFeatures():
            id = feature.id()
            outFeats = []
            #getting the output features with the specific id
            if qgisOutputVector:
                for gf in qgisOutputVector.dataProvider().getFeatures(
                        QgsFeatureRequest(
                            QgsExpression("{0}={1}".format(keyColumn, id)))):
                    outFeats.append(gf)
            elif featureTupleList:
                for gfid, gf in featureTupleList:
                    if gfid == id and gf['classname'] == pgInputLayer.name():
                        outFeats.append(gf)
            else:
                for gf in [gf for gf in featureList if gf.id() == id]:
                    outFeats.append(gf)
            #starting to make changes
            for i in range(len(outFeats)):
                if i == 0:
                    #let's update this feature
                    newGeom = outFeats[i].geometry()
                    newGeom.convertToMultiType()
                    feature.setGeometry(newGeom)
                    pgInputLayer.updateFeature(feature)
                else:
                    #for the rest, let's add them
                    newFeat = QgsFeature(feature)
                    newGeom = outFeats[i].geometry()
                    newGeom.convertToMultiType()
                    newFeat.setGeometry(newGeom)
                    idx = newFeat.fieldNameIndex(keyColumn)
                    newFeat.setAttribute(idx, provider.defaultValue(idx))
                    addList.append(newFeat)
            #in the case we don't find features in the output we should mark them to be removed
            if len(outFeats) == 0:
                idsToRemove.append(id)
        #pushing the changes into the edit buffer
        pgInputLayer.addFeatures(addList, True)
        #removing features from the layer.
        pgInputLayer.deleteFeatures(idsToRemove)

    def updateOriginalLayerV2(self,
                              pgInputLayer,
                              qgisOutputVector,
                              featureList=None,
                              featureTupleList=None,
                              deleteFeatures=True):
        """
        Updates the original layer using the grass output layer
        pgInputLyr: postgis input layer
        qgisOutputVector: qgis output layer
        Speed up tips: http://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/
        1- Make pgIdList, by querying it with flag QgsFeatureRequest.NoGeometry
        2- Build output dict
        3- Perform operation
        """
        provider = pgInputLayer.dataProvider()
        # getting keyColumn because we want to be generic
        uri = QgsDataSourceURI(pgInputLayer.dataProvider().dataSourceUri())
        keyColumn = uri.keyColumn()
        # starting edition mode
        pgInputLayer.startEditing()
        pgInputLayer.beginEditCommand('Updating layer')
        addList = []
        idsToRemove = []
        inputDict = dict()
        #this is done to work generically with output layers that are implemented different from ours
        isMulti = QgsWKBTypes.isMultiType(int(pgInputLayer.wkbType()))  #
        #making the changes and inserts
        #this request only takes ids to build inputDict
        request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry)
        for feature in pgInputLayer.getFeatures(request):
            inputDict[feature.id()] = dict()
            inputDict[feature.id()]['featList'] = []
            inputDict[feature.id()]['featWithoutGeom'] = feature
        inputDictKeys = inputDict.keys()
        if qgisOutputVector:
            for feat in qgisOutputVector.dataProvider().getFeatures():
                if keyColumn == '':
                    featid = feat.id()
                else:
                    featid = feat[keyColumn]
                if featid in inputDictKeys:  #verificar quando keyColumn = ''
                    inputDict[featid]['featList'].append(feat)
        elif featureTupleList:
            for gfid, gf in featureTupleList:
                if gfid in inputDictKeys and gf[
                        'classname'] == pgInputLayer.name():
                    inputDict[gfid]['featList'].append(gf)
        else:
            for feat in featureList:
                if keyColumn == '':
                    featid = feat.id()
                else:
                    featid = feat[keyColumn]
                if featid in inputDictKeys:
                    inputDict[featid]['featList'].append(feat)
        #finally, do what must be done
        for id in inputDictKeys:
            outFeats = inputDict[id]['featList']
            #starting to make changes
            for i in range(len(outFeats)):
                if i == 0:
                    #let's update this feature
                    newGeom = outFeats[i].geometry()
                    if newGeom:
                        if isMulti:
                            newGeom.convertToMultiType()
                        pgInputLayer.changeGeometry(
                            id, newGeom)  #It is faster according to the api
                    else:
                        if id not in idsToRemove:
                            idsToRemove.append(id)
                else:
                    #for the rest, let's add them
                    newFeat = QgsFeature(inputDict[id]['featWithoutGeom'])
                    newGeom = outFeats[i].geometry()
                    if newGeom:
                        if isMulti and newGeom:
                            newGeom.convertToMultiType()
                        newFeat.setGeometry(newGeom)
                        if keyColumn != '':
                            idx = newFeat.fieldNameIndex(keyColumn)
                            newFeat.setAttribute(idx,
                                                 provider.defaultValue(idx))
                        addList.append(newFeat)
                    else:
                        if id not in idsToRemove:
                            idsToRemove.append(id)
            #in the case we don't find features in the output we should mark them to be removed
            if len(outFeats) == 0 and deleteFeatures:
                idsToRemove.append(id)
        #pushing the changes into the edit buffer
        pgInputLayer.addFeatures(addList, True)
        #removing features from the layer.
        pgInputLayer.deleteFeatures(idsToRemove)
        pgInputLayer.endEditCommand()

    def getProcessingErrors(self, layer):
        """
        Gets processing errors
        layer: error layer (QgsVectorLayer) output made by grass
        """
        recordList = []
        for feature in layer.getFeatures():
            recordList.append(
                (feature.id(), binascii.hexlify(feature.geometry().asWkb())))
        return recordList

    def loadLayerBeforeValidationProcess(self, cl):
        """
        Loads all layers to QGIS' TOC prior the validation process
        """
        #creating vector layer
        if self.abstractDb.getDatabaseVersion() == 'Non_EDGV':
            isEdgv = False
        else:
            isEdgv = True
        if isinstance(cl, dict):
            lyr = self.layerLoader.load([cl], uniqueLoad=True,
                                        isEdgv=isEdgv)[cl['lyrName']]
        else:
            schema, layer_name = self.abstractDb.getTableSchema(cl)
            lyr = self.layerLoader.load([layer_name],
                                        uniqueLoad=True,
                                        isEdgv=isEdgv)[layer_name]
        return lyr

    def prepareExecution(self,
                         cl,
                         geometryColumn='geom',
                         selectedFeatures=False):
        """
        Prepare the process to be executed
        cl: table name
        """
        # loading layer prior to execution
        lyr = self.loadLayerBeforeValidationProcess(cl)
        # getting keyColumn because we want to be generic
        uri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
        keyColumn = uri.keyColumn()
        #getting feature map including the edit buffer
        featureMap = self.mapInputLayer(lyr, selectedFeatures=selectedFeatures)
        #getting table name with schema
        if isinstance(cl, dict):
            tableSchema = cl['tableSchema']
            tableName = cl['tableName']
            geometryColumn = cl['geom']
            fullTableName = '''{0}.{1}'''.format(cl['tableSchema'],
                                                 cl['tableName'])
        else:
            fullTableName = cl
            tableSchema, tableName = cl.split('.')

        #setting temp table name
        processTableName = fullTableName + '_temp'
        # specific EPSG search
        parameters = {
            'tableSchema': tableSchema,
            'tableName': tableName,
            'geometryColumn': geometryColumn
        }
        srid = self.abstractDb.findEPSG(parameters=parameters)
        #creating temp table
        self.abstractDb.createAndPopulateTempTableFromMap(
            fullTableName, featureMap, geometryColumn, keyColumn, srid)
        return processTableName, lyr, keyColumn

    def postProcessSteps(self, processTableName, lyr):
        """
        Execute the final steps after the actual process
        """
        #getting the output as a QgsVectorLayer
        outputLayer = QgsVectorLayer(
            self.abstractDb.getURI(processTableName, True).uri(),
            processTableName, "postgres")
        #updating the original layer (lyr)
        self.updateOriginalLayerV2(lyr, outputLayer)
        #dropping the temp table as we don't need it anymore
        self.abstractDb.dropTempTable(processTableName)

    def getGeometryTypeText(self, geomtype):
        if geomtype == QGis.WKBPoint:
            return 'Point'
        elif geomtype == QGis.WKBMultiPoint:
            return 'MultiPoint'
        elif geomtype == QGis.WKBLineString:
            return 'Linestring'
        elif geomtype == QGis.WKBMultiLineString:
            return 'MultiLinestring'
        elif geomtype == QGis.WKBPolygon:
            return 'Polygon'
        elif geomtype == QGis.WKBMultiPolygon:
            return 'MultiPolygon'
        else:
            raise Exception(
                self.tr('Operation not defined with provided geometry type!'))

    def createUnifiedLineLayer(self, layerList, onlySelected=False):
        """
        For each layer in layerList, transforms it into lines if lyrType 
        is polygon and adds features into layerList.
        Duplicates can happen in this process.
        """
        srid = layerList[0].crs().authid().split(':')[-1]
        coverage = self.iface.addVectorLayer(
            "{0}?crs=epsg:{1}".format('Linestring', srid), "coverage_lines",
            "memory")
        provider = coverage.dataProvider()
        coverage.startEditing()
        coverage.beginEditCommand('Creating coverage lines layer')

        self.localProgress = ProgressWidget(
            1,
            len(layerList) - 1,
            self.tr('Building unified layers with  ') +
            ', '.join([i.name() for i in layerList]) + '.',
            parent=self.iface.mapCanvas())
        addFeatureList = []
        for lyr in layerList:
            isPolygon = True if lyr.dataProvider().geometryType() in [
                QGis.WKBPolygon, QGis.WKBMultiPolygon
            ] else False
            isMulti = QgsWKBTypes.isMultiType(int(lyr.wkbType()))
            featureIterator = lyr.getFeatures(
            ) if not onlySelected else lyr.selectedFeatures()
            for feature in featureIterator:
                geom = feature.geometry()
                if geom:
                    parts = geom.asGeometryCollection()
                    if parts:
                        for part in parts:
                            if isPolygon:
                                linestrings = part.asPolygon()
                                for line in linestrings:
                                    newfeat = QgsFeature(
                                        coverage.pendingFields())
                                    newfeat.setGeometry(
                                        QgsGeometry.fromPolyline(line))
                                    addFeatureList.append(newfeat)
                            else:
                                newfeat = QgsFeature(coverage.pendingFields())
                                newfeat.setGeometry(part)
                                addFeatureList.append(newfeat)
            self.localProgress.step()
        coverage.addFeatures(addFeatureList, True)
        coverage.endEditCommand()
        coverage.commitChanges()
        return coverage

    def createUnifiedLayer(self,
                           layerList,
                           attributeTupple=False,
                           attributeBlackList='',
                           onlySelected=False):
        """
        Creates a unified layer from a list of layers
        """
        #getting srid from something like 'EPSG:31983'
        srid = layerList[0].crs().authid().split(':')[
            -1]  #quem disse que tudo tem que ter mesmo srid? TODO: mudar isso
        # creating the layer
        geomtype = layerList[0].dataProvider().geometryType()
        for lyr in layerList:
            if lyr.dataProvider().geometryType() != geomtype:
                raise Exception(
                    self.tr('Error! Different geometry primitives!'))

        coverage = self.iface.addVectorLayer(
            "{0}?crs=epsg:{1}".format(self.getGeometryTypeText(geomtype),
                                      srid), "coverage", "memory")
        provider = coverage.dataProvider()
        coverage.startEditing()
        coverage.beginEditCommand('Creating coverage layer')  #speedup

        #defining fields
        if not attributeTupple:
            fields = [
                QgsField('featid', QVariant.Int),
                QgsField('classname', QVariant.String)
            ]
        else:
            fields = [
                QgsField('featid', QVariant.Int),
                QgsField('classname', QVariant.String),
                QgsField('tupple', QVariant.String),
                QgsField('blacklist', QVariant.String)
            ]
        provider.addAttributes(fields)
        coverage.updateFields()

        totalCount = 0
        for layer in layerList:
            if onlySelected:
                totalCount += layer.selectedFeatureCount()
            else:
                totalCount += layer.pendingFeatureCount()
        self.localProgress = ProgressWidget(
            1,
            totalCount - 1,
            self.tr('Building unified layers with  ') +
            ', '.join([i.name() for i in layerList]) + '.',
            parent=self.iface.mapCanvas())
        featlist = []
        if attributeBlackList != '':
            bList = attributeBlackList.replace(' ', '').split(',')
        else:
            bList = []
        for layer in layerList:
            # recording class name
            classname = layer.name()
            uri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
            keyColumn = uri.keyColumn()
            if onlySelected:
                featureList = layer.selectedFeatures()
            else:
                featureList = layer.getFeatures()
            for feature in featureList:
                newfeat = QgsFeature(coverage.pendingFields())
                newfeat.setGeometry(feature.geometry())
                newfeat['featid'] = feature.id()
                newfeat['classname'] = classname
                if attributeTupple:
                    attributeList = []
                    attributes = [
                        field.name() for field in feature.fields()
                        if (field.type() != 6 and field.name() != keyColumn)
                    ]
                    attributes.sort()
                    for attribute in attributes:
                        if attribute not in bList:
                            attributeList.append(
                                u'{0}'.format(feature[attribute]
                                              ))  #done due to encode problems
                    tup = ','.join(attributeList)
                    newfeat['tupple'] = tup
                featlist.append(newfeat)
                self.localProgress.step()

        #inserting new features into layer
        coverage.addFeatures(featlist, False)
        coverage.endEditCommand()
        coverage.commitChanges()
        return coverage

    def splitUnifiedLayer(self, outputLayer, layerList):
        """
        Updates all original layers making requests with the class name
        """
        for layer in layerList:
            classname = layer.name()
            tupplelist = [(feature['featid'], feature)
                          for feature in outputLayer.getFeatures()
                          if feature['classname'] == classname]
            self.updateOriginalLayerV2(layer,
                                       None,
                                       featureTupleList=tupplelist)

    def getGeometryColumnFromLayer(self, layer):
        uri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
        geomColumn = uri.geometryColumn()
        return geomColumn

    def startTimeCount(self):
        self.startTime = datetime.now()

    def endTimeCount(self, cummulative=True):
        self.endTime = datetime.now()
        elapsedTime = (self.endTime - self.startTime)
        if cummulative:
            if self.totalTime == 0:
                self.totalTime = elapsedTime
            else:
                self.totalTime += elapsedTime
        return elapsedTime

    def logLayerTime(self, lyr):
        time = self.endTimeCount()
        if self.startTime != 0 and self.endTime != 0:
            QgsMessageLog.logMessage(
                self.tr(
                    'Elapsed time for process {0} on layer {1}: {2}').format(
                        self.processAlias, lyr, str(time)), "DSG Tools Plugin",
                QgsMessageLog.CRITICAL)

    def logTotalTime(self):
        if self.startTime != 0 and self.endTime != 0 and self.totalTime != 0:
            QgsMessageLog.logMessage(
                self.tr('Elapsed time for process {0}: {1}').format(
                    self.processAlias, str(self.totalTime)),
                "DSG Tools Plugin", QgsMessageLog.CRITICAL)

    def jsonifyParameters(self, params):
        """
        Sets a dict type feature to a json structure in order to make it visually better
        both to expose on log and to save it on validation history table.
        parameter params: a dict type variable
        returns: a json structured text
        """
        return json.dumps(params, sort_keys=True, indent=4)

    def logProcess(self):
        """
        Returns information to user:
        -userName (get information from abstractDb.db.userName())
        -parameters (get parameters from parameter dict) ***
        -layersRun (the layers that were used)
        -flagNumber (number of flags)
        -elapsedTime
        """
        # logging username
        logMsg = ""
        if self.dbUserName:
            logMsg += self.tr("\nDatabase username: {0}").format(
                self.dbUserName)
        else:
            logMsg += self.tr("\nUnable to get database username.")
        # logging process parameters
        if self.parameters:
            parametersString = self.tr(
                "\nParameters used on this execution of process {}\n").format(
                    self.processAlias)
            parametersString += self.jsonifyParameters(self.parameters)
            logMsg += parametersString
        else:
            logMsg += self.tr(
                "\nUnable to get database parameters for process {}.").format(
                    self.processAlias)
        # logging #Flag
        logMsg += self.tr("\nNumber of flags raised by the process: {}").format(\
                        str(self.abstractDb.getNumberOfFlagsByProcess(self.processName)))
        # logging total time elapsed
        self.endTimeCount()
        if self.totalTime:
            logMsg += self.tr(
                "\nTotal elapsed time for process {0}: {1}\n").format(
                    self.processAlias, self.totalTime)
        else:
            logMsg += self.tr("\nUnable to get total elapsed time.")
        self.logMsg = logMsg
        QgsMessageLog.logMessage(logMsg, "DSG Tools Plugin",
                                 QgsMessageLog.CRITICAL)

    def raiseVectorFlags(self, flagLyr, featFlagList):
        flagLyr.startEditing()
        flagLyr.beginEditCommand('Raising flags')  #speedup
        flagLyr.addFeatures(featFlagList, False)
        flagLyr.endEditCommand()
        flagLyr.commitChanges()
        return len(featFlagList)

    def buildFlagFeature(self, flagLyr, processName, tableSchema, tableName,
                         feat_id, geometry_column, geom, reason):
        newFeat = QgsFeature(flagLyr.pendingFields())
        newFeat['process_name'] = processName
        newFeat['layer'] = '{0}.{1}'.format(tableSchema, tableName)
        newFeat['feat_id'] = feat_id
        newFeat['reason'] = reason
        newFeat['geometry_column'] = geometry_column
        newFeat['user_fixed'] = False
        newFeat['dimension'] = geom.type()
        newFeat.setGeometry(geom)
        return newFeat

    def getFeatures(self,
                    lyr,
                    onlySelected=False,
                    returnIterator=True,
                    returnSize=True):
        if onlySelected:
            featureList = lyr.selectedFeatures()
            size = len(featureList)
        else:
            featureList = [i for i in lyr.getFeatures()
                           ] if not returnIterator else lyr.getFeatures()
            size = len(lyr.allFeatureIds())
        if returnIterator:
            return featureList, size
        else:
            return featureList
Ejemplo n.º 10
0
class LoadLayersFromServer(QtGui.QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        self.iface = iface
        self.utils = Utils()
        self.setupUi(self)
        self.layerFactory = LayerLoaderFactory()
        self.customServerConnectionWidget.postgisCustomSelector.setTitle(
            self.tr('Select Databases'))
        self.customServerConnectionWidget.spatialiteCustomSelector.setTitle(
            self.tr('Selected Spatialites'))
        self.layersCustomSelector.setTitle(
            self.tr('Select layers to be loaded'))
        self.customServerConnectionWidget.dbDictChanged.connect(
            self.updateLayersFromDbs)
        self.customServerConnectionWidget.resetAll.connect(self.resetInterface)
        self.customServerConnectionWidget.styleChanged.connect(
            self.populateStyleCombo)
        self.headerList = [
            self.tr('Category'),
            self.tr('Layer Name'),
            self.tr('Geometry\nColumn'),
            self.tr('Geometry\nType'),
            self.tr('Layer\nType')
        ]
        self.layersCustomSelector.setHeaders(self.headerList)
        self.customServerConnectionWidget.postgisEdgvComboFilter.currentIndexChanged.connect(
            self.layersCustomSelector.setInitialState)
        self.customServerConnectionWidget.spatialiteEdgvComboFilter.currentIndexChanged.connect(
            self.layersCustomSelector.setInitialState)
        self.customServerConnectionWidget.serverConnectionTab.currentChanged.connect(
            self.layersCustomSelector.setInitialState)
        self.lyrDict = dict()

    def resetInterface(self):
        """
        Sets the initial state again
        """
        self.layersCustomSelector.clearAll()
        self.styleComboBox.clear()
        #TODO: refresh optional parameters
        self.checkBoxOnlyWithElements.setCheckState(0)
        self.onlyParentsCheckBox.setCheckState(0)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Closes the dialog
        """
        self.close()

    def updateLayersFromDbs(self, type, dbList, showViews=False):
        """
        
        """
        errorDict = dict()
        if type == 'added':
            progress = ProgressWidget(
                1,
                len(dbList),
                self.tr('Reading selected databases... '),
                parent=self)
            progress.initBar()
            for dbName in dbList:
                try:
                    QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                    geomList = self.customServerConnectionWidget.selectedDbsDict[
                        dbName].getGeomColumnTupleList(showViews=showViews)
                    for tableSchema, tableName, geom, geomType, tableType in geomList:
                        if self.customServerConnectionWidget.edgvType == 'Non_EDGV':
                            lyrName = tableName
                            cat = tableSchema
                        else:
                            lyrName = '_'.join(tableName.split('_')[1::])
                            cat = tableName.split('_')[0]
                        key = ','.join(
                            [cat, lyrName, geom, geomType, tableType])
                        if key not in self.lyrDict.keys():
                            self.lyrDict[key] = dict()
                        self.lyrDict[key][dbName] = {
                            'tableSchema': tableSchema,
                            'tableName': tableName,
                            'geom': geom,
                            'geomType': geomType,
                            'tableType': tableType,
                            'lyrName': lyrName,
                            'cat': cat
                        }
                except Exception as e:
                    errorDict[dbName] = ':'.join(e.args)
                    QApplication.restoreOverrideCursor()
                progress.step()
                QApplication.restoreOverrideCursor()

        elif type == 'removed':
            for key in self.lyrDict.keys():
                for db in self.lyrDict[key].keys():
                    if db in dbList:
                        self.lyrDict[key].pop(db)
                if self.lyrDict[key] == dict():
                    self.lyrDict.pop(key)
        interfaceDictList = []
        for key in self.lyrDict.keys():
            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.layersCustomSelector.setInitialState(interfaceDictList,
                                                  unique=True)

    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Loads the selected classes/categories
        """
        #1- filter classes if categories is checked and build list.
        selectedKeys = self.layersCustomSelector.getSelectedNodes()
        if len(selectedKeys) == 0:
            QMessageBox.information(
                self, self.tr('Error!'),
                self.tr('Select at least one layer to be loaded!'))
            return

        #2- get parameters
        withElements = self.checkBoxOnlyWithElements.isChecked()
        selectedStyle = None
        if self.customServerConnectionWidget.edgvType == 'Non_EDGV':
            isEdgv = False
        else:
            isEdgv = True
        if self.styleComboBox.currentIndex() != 0:
            selectedStyle = self.customServerConnectionWidget.stylesDict[
                self.styleComboBox.currentText()]
        uniqueLoad = self.uniqueLoadCheckBox.isChecked()
        onlyParents = self.onlyParentsCheckBox.isChecked()
        #3- Build factory dict
        factoryDict = dict()
        dbList = self.customServerConnectionWidget.selectedDbsDict.keys()
        for dbName in dbList:
            factoryDict[dbName] = self.layerFactory.makeLoader(
                self.iface,
                self.customServerConnectionWidget.selectedDbsDict[dbName])
        #4- load for each db
        exceptionDict = dict()
        progress = ProgressWidget(
            1,
            len(dbList),
            self.tr('Loading layers from selected databases... '),
            parent=self)
        for dbName in factoryDict.keys():
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            try:
                selectedClasses = []
                for i in selectedKeys:
                    if i in self.lyrDict.keys():
                        if dbName in self.lyrDict[i].keys():
                            selectedClasses.append(self.lyrDict[i][dbName])
                factoryDict[dbName].load(selectedClasses,
                                         uniqueLoad=uniqueLoad,
                                         onlyWithElements=withElements,
                                         stylePath=selectedStyle,
                                         useInheritance=onlyParents,
                                         isEdgv=isEdgv,
                                         parent=self)
                progress.step()
            except Exception as e:
                exceptionDict[dbName] = ':'.join(e.args)
                QApplication.restoreOverrideCursor()
                progress.step()
            QApplication.restoreOverrideCursor()
            if factoryDict[dbName].errorLog != '':
                if dbName in exceptionDict.keys():
                    exceptionDict[
                        dbName] += '\n' + factoryDict[dbName].errorLog
                else:
                    exceptionDict[dbName] = factoryDict[dbName].errorLog
        QApplication.restoreOverrideCursor()
        self.logInternalError(exceptionDict)
        self.close()

    def logInternalError(self, exceptionDict):
        """
        Logs internal errors during the load process in QGIS' log
        """
        msg = ''
        errorDbList = exceptionDict.keys()
        if len(errorDbList) > 0:
            msg += self.tr('\nDatabases with error:')
            msg += ', '.join(errorDbList)
            msg += self.tr(
                '\nError messages for each database were output in qgis log.')
            for errorDb in errorDbList:
                QgsMessageLog.logMessage(
                    self.tr('Error for database ') + errorDb + ': ' +
                    exceptionDict[errorDb], "DSG Tools Plugin",
                    QgsMessageLog.CRITICAL)
        return msg

    def populateStyleCombo(self, styleDict):
        """
        Loads styles saved in the database
        """
        self.styleComboBox.clear()
        styleList = styleDict.keys()
        numberOfStyles = len(styleList)
        if numberOfStyles > 0:
            self.styleComboBox.addItem(self.tr('Select Style'))
            for i in range(numberOfStyles):
                self.styleComboBox.addItem(styleList[i])
        else:
            self.styleComboBox.addItem(self.tr('No available styles'))
Ejemplo n.º 11
0
class LoadLayersFromServer(QtGui.QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        self.iface = iface
        self.utils = Utils()
        self.setupUi(self)
        self.layerFactory = LayerLoaderFactory()
        self.customServerConnectionWidget.postgisCustomSelector.setTitle(self.tr('Select Databases'))
        self.customServerConnectionWidget.spatialiteCustomSelector.setTitle(self.tr('Selected Spatialites'))
        self.layersCustomSelector.setTitle(self.tr('Select layers to be loaded'))
        self.customServerConnectionWidget.dbDictChanged.connect(self.updateLayersFromDbs)
        self.customServerConnectionWidget.resetAll.connect(self.resetInterface)
        self.customServerConnectionWidget.styleChanged.connect(self.populateStyleCombo)
        self.headerList = [self.tr('Category'), self.tr('Layer Name'), self.tr('Geometry\nColumn'), self.tr('Geometry\nType'), self.tr('Layer\nType')]
        self.layersCustomSelector.setHeaders(self.headerList)
        self.customServerConnectionWidget.postgisEdgvComboFilter.currentIndexChanged.connect(self.layersCustomSelector.setInitialState)
        self.customServerConnectionWidget.spatialiteEdgvComboFilter.currentIndexChanged.connect(self.layersCustomSelector.setInitialState)
        self.customServerConnectionWidget.serverConnectionTab.currentChanged.connect(self.layersCustomSelector.setInitialState)
        self.lyrDict = dict()
    
    def resetInterface(self):
        """
        Sets the initial state again
        """
        self.layersCustomSelector.clearAll()
        self.styleComboBox.clear()
        #TODO: refresh optional parameters
        self.checkBoxOnlyWithElements.setCheckState(0)
        self.onlyParentsCheckBox.setCheckState(0)

    @pyqtSlot()
    def on_buttonBox_rejected(self):
        """
        Closes the dialog
        """
        self.close()
    
    def updateLayersFromDbs(self, type, dbList, showViews = False):
        """
        
        """
        errorDict = dict()
        if type == 'added':
            progress = ProgressWidget(1, len(dbList), self.tr('Reading selected databases... '), parent=self)
            progress.initBar()
            for dbName in dbList:
                try:
                    QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
                    geomList = self.customServerConnectionWidget.selectedDbsDict[dbName].getGeomColumnTupleList(showViews = showViews)
                    for tableSchema, tableName, geom, geomType, tableType in geomList:
                        if self.customServerConnectionWidget.edgvType == 'Non_EDGV':
                            lyrName = tableName
                            cat = tableSchema
                        else:
                            lyrName = '_'.join(tableName.split('_')[1::])
                            cat = tableName.split('_')[0]
                        key = ','.join([cat, lyrName, geom, geomType, tableType])
                        if key not in self.lyrDict.keys():
                            self.lyrDict[key] = dict()
                        self.lyrDict[key][dbName] = {'tableSchema':tableSchema, 'tableName':tableName, 'geom':geom, 'geomType':geomType, 'tableType':tableType, 'lyrName':lyrName, 'cat':cat}
                except Exception as e:
                    errorDict[dbName] = ':'.join(e.args)
                    QApplication.restoreOverrideCursor()
                progress.step()
                QApplication.restoreOverrideCursor()
                
        elif type == 'removed':
            for key in self.lyrDict.keys():
                for db in self.lyrDict[key].keys():
                    if db in dbList:
                        self.lyrDict[key].pop(db)
                if self.lyrDict[key] == dict():
                    self.lyrDict.pop(key)
        interfaceDictList = []
        for key in self.lyrDict.keys():
            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.layersCustomSelector.setInitialState(interfaceDictList,unique = True)
            
    @pyqtSlot()
    def on_buttonBox_accepted(self):
        """
        Loads the selected classes/categories
        """
        #1- filter classes if categories is checked and build list.
        selectedKeys = self.layersCustomSelector.getSelectedNodes()
        if len(selectedKeys) == 0:
            QMessageBox.information(self, self.tr('Error!'), self.tr('Select at least one layer to be loaded!'))
            return

        #2- get parameters
        withElements = self.checkBoxOnlyWithElements.isChecked()
        selectedStyle = None
        if self.customServerConnectionWidget.edgvType == 'Non_EDGV':
            isEdgv = False
        else:
            isEdgv = True
        if self.styleComboBox.currentIndex() != 0:
            selectedStyle = self.customServerConnectionWidget.stylesDict[self.styleComboBox.currentText()]
        uniqueLoad = self.uniqueLoadCheckBox.isChecked()
        onlyParents = self.onlyParentsCheckBox.isChecked()
        #3- Build factory dict
        factoryDict = dict()
        dbList = self.customServerConnectionWidget.selectedDbsDict.keys()
        for dbName in dbList:
            factoryDict[dbName] = self.layerFactory.makeLoader(self.iface, self.customServerConnectionWidget.selectedDbsDict[dbName])
        #4- load for each db
        exceptionDict = dict()
        progress = ProgressWidget(1, len(dbList), self.tr('Loading layers from selected databases... '), parent=self)
        for dbName in factoryDict.keys():
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            try:
                selectedClasses = []
                for i in selectedKeys:
                    if i in self.lyrDict.keys():
                        if dbName in self.lyrDict[i].keys():
                            selectedClasses.append(self.lyrDict[i][dbName])
                factoryDict[dbName].load(selectedClasses, uniqueLoad=uniqueLoad, onlyWithElements=withElements, stylePath=selectedStyle, useInheritance=onlyParents, isEdgv=isEdgv, parent=self)
                progress.step()
            except Exception as e:
                exceptionDict[dbName] = ':'.join(e.args)
                QApplication.restoreOverrideCursor()
                progress.step()
            QApplication.restoreOverrideCursor()
            if factoryDict[dbName].errorLog != '':
                if dbName in exceptionDict.keys():
                    exceptionDict[dbName] += '\n'+factoryDict[dbName].errorLog
                else:
                    exceptionDict[dbName] = factoryDict[dbName].errorLog
        QApplication.restoreOverrideCursor()
        self.logInternalError(exceptionDict)
        self.close()
    
    def logInternalError(self, exceptionDict):
        """
        Logs internal errors during the load process in QGIS' log
        """
        msg = ''
        errorDbList = exceptionDict.keys()
        if len(errorDbList) > 0:
            msg += self.tr('\nDatabases with error:')
            msg += ', '.join(errorDbList)
            msg += self.tr('\nError messages for each database were output in qgis log.')
            for errorDb in errorDbList:
                QgsMessageLog.logMessage(self.tr('Error for database ') + errorDb + ': ' +exceptionDict[errorDb], "DSG Tools Plugin", QgsMessageLog.CRITICAL)
        return msg 

    def populateStyleCombo(self, styleDict):
        """
        Loads styles saved in the database
        """
        self.styleComboBox.clear()
        styleList = styleDict.keys()
        numberOfStyles = len(styleList)
        if numberOfStyles > 0:
            self.styleComboBox.addItem(self.tr('Select Style'))
            for i in range(numberOfStyles):
                self.styleComboBox.addItem(styleList[i])
        else:
            self.styleComboBox.addItem(self.tr('No available styles'))
Ejemplo n.º 12
0
class ValidationProcess(QObject):
    def __init__(self, postgisDb, iface, instantiating=False):
        """
        Constructor
        """
        super(ValidationProcess, self).__init__()
        self.abstractDb = postgisDb
        if self.getStatus() == None:
            self.setStatus(self.tr('Instantianting process'), 0)
        self.classesToBeDisplayedAfterProcess = []
        self.parameters = None
        self.iface = iface
        self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, self.abstractDb)
        self.processAlias = self.tr('Validation Process')
        self.instantiating = instantiating
        
    def setParameters(self, params):
        """
        Define the process parameteres
        """
        self.parameters = params

    def execute(self):
        """
        Abstract method. MUST be reimplemented.
        """
        pass
    
    def shouldBeRunAgain(self):
        """
        Defines if the method should run again later
        """
        return False
    
    def getName(self):
        """
        Gets the process name
        """
        return str(self.__class__).split('.')[-1].replace('\'>', '')
    
    def getProcessGroup(self):
        """
        Returns the process group
        """
        return 'Ungrouped'
    
    def getClassesToBeDisplayedAfterProcess(self):
        """
        Returns classes to be loaded to TOC after executing this process.
        """
        return self.classesToBeDisplayedAfterProcess
    
    def addClassesToBeDisplayedList(self,className):
        """
        Add a class into the class list that will be displayed after the process
        """
        if className not in self.classesToBeDisplayedAfterProcess:
            self.classesToBeDisplayedAfterProcess.append(className)
    
    def clearClassesToBeDisplayedAfterProcess(self):
        """
        Clears the class list to be displayed
        """
        self.classesToBeDisplayedAfterProcess = []
    
    def preProcess(self):
        """
        Returns the name of the pre process that must run before, must be reimplemented in each process
        """
        return None
    
    def postProcess(self):
        """
        Returns the name of the post process that must run after, must be reimplemented in each process
        """        
        return None
    
    def addFlag(self, flagTupleList):
        """
        Adds flags
        flagTUpleList: list of tuples to be added as flag
        """
        try:
            return self.abstractDb.insertFlags(flagTupleList, self.getName())
        except Exception as e:
            QMessageBox.critical(None, self.tr('Critical!'), self.tr('A problem occurred inserting flags! Check log for details.'))
            QgsMessageLog.logMessage(str(e.args[0]), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            
    def removeFeatureFlags(self, layer, featureId):
        """
        Removes specific flags from process
        layer: Name of the layer that should be remove from the flags
        featureId: Feature id from layer name that must be removed
        """
        try:
            return self.abstractDb.removeFeatureFlags(layer, featureId, self.getName())
        except Exception as e:
            QMessageBox.critical(None, self.tr('Critical!'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def getStatus(self):
        """
        Gets the process status
        """
        try:
            return self.abstractDb.getValidationStatus(self.getName())
        except Exception as e:
            QMessageBox.critical(None, self.tr('Critical!'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def getStatusMessage(self):
        """
        Gets the status message text
        """
        try:
            return self.abstractDb.getValidationStatusText(self.getName())
        except Exception as e:
            QMessageBox.critical(None, self.tr('Critical!'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def setStatus(self, msg, status):
        """
        Sets the status message
        status: Status number
        msg: Status text message
        """
        try:
            self.abstractDb.setValidationProcessStatus(self.getName(), msg, status)
        except Exception as e:
            QMessageBox.critical(None, self.tr('Critical!'), self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def finishedWithError(self):
        """
        Sets the finished with error status (status number 2)
        Clears the classes to be displayed
        """
        self.setStatus(self.tr('Process finished with errors.'), 2) #Failed status
        #drop temps
        try:
            classesWithElem = self.parameters['Classes']
            for cl in classesWithElem:
                tempName = cl.split(':')[0]+'_temp'
                self.abstractDb.dropTempTable(tempName)
        except:
            pass
        self.clearClassesToBeDisplayedAfterProcess()
    
    def inputData(self):
        """
        Returns current active layers
        """
        return self.iface.mapCanvas().layers()

    def getTableNameFromLayer(self, lyr):
        """
        Gets the layer name as present in the rules
        """
        uri = lyr.dataProvider().dataSourceUri()
        dsUri = QgsDataSourceURI(uri)
        name = '.'.join([dsUri.schema(), dsUri.table()])
        return name

    def mapInputLayer(self, inputLyr, selectedFeatures = False):
        """
        Gets the layer features considering the edit buffer in the case
        the layer is already in edition mode
        """
        #return dict
        featureMap = dict()
        #getting only selected features
        if selectedFeatures:
            for feat in inputLyr.selectedFeatures():
                featureMap[feat.id()] = feat
        #getting all features
        else:
            for feat in inputLyr.getFeatures():
                featureMap[feat.id()] = feat
        return featureMap
    
    def updateOriginalLayer(self, pgInputLayer, qgisOutputVector, featureList=None, featureTupleList=None):
        """
        Updates the original layer using the grass output layer
        pgInputLyr: postgis input layer
        grassOutputLyr: grass output layer
        """
        provider = pgInputLayer.dataProvider()
        # getting keyColumn because we want to be generic
        uri = QgsDataSourceURI(pgInputLayer.dataProvider().dataSourceUri())
        keyColumn = uri.keyColumn()
        # starting edition mode
        pgInputLayer.startEditing()
        addList = []
        idsToRemove = []
        #making the changes and inserts
        for feature in pgInputLayer.getFeatures():
            id = feature.id()
            outFeats = []
            #getting the output features with the specific id
            if qgisOutputVector:
                for gf in qgisOutputVector.dataProvider().getFeatures(QgsFeatureRequest(QgsExpression("{0}={1}".format(keyColumn, id)))):
                    outFeats.append(gf)
            elif featureTupleList:
                for gfid, gf in featureTupleList:
                    if gfid == id and gf['classname'] == pgInputLayer.name():
                        outFeats.append(gf)
            else:
                for gf in [gf for gf in featureList if gf.id() == id]:
                    outFeats.append(gf)
            #starting to make changes
            for i in range(len(outFeats)):
                if i == 0:
                    #let's update this feature
                    newGeom = outFeats[i].geometry()
                    newGeom.convertToMultiType()
                    feature.setGeometry(newGeom)
                    pgInputLayer.updateFeature(feature)
                else:
                    #for the rest, let's add them
                    newFeat = QgsFeature(feature)
                    newGeom = outFeats[i].geometry()
                    newGeom.convertToMultiType()
                    newFeat.setGeometry(newGeom)
                    idx = newFeat.fieldNameIndex(keyColumn)
                    newFeat.setAttribute(idx, provider.defaultValue(idx))
                    addList.append(newFeat)
            #in the case we don't find features in the output we should mark them to be removed
            if len(outFeats) == 0:
                idsToRemove.append(id)
        #pushing the changes into the edit buffer
        pgInputLayer.addFeatures(addList, True)
        #removing features from the layer.
        pgInputLayer.deleteFeatures(idsToRemove)

    def getProcessingErrors(self, layer):
        """
        Gets processing errors
        layer: error layer (QgsVectorLayer) output made by grass
        """
        recordList = []
        for feature in layer.getFeatures():
            recordList.append((feature.id(), binascii.hexlify(feature.geometry().asWkb())))
        return recordList
    
    def loadLayerBeforeValidationProcess(self, cl):
        """
        Loads all layers to QGIS' TOC prior the validation process
        """
        #creating vector layer
        if self.abstractDb.getDatabaseVersion() == 'Non_EDGV':
            isEdgv = False
        else:
            isEdgv = True
        if isinstance(cl, dict):
            lyr = self.layerLoader.load([cl], uniqueLoad=True, isEdgv=isEdgv)[cl['lyrName']]
        else:
            schema, layer_name = self.abstractDb.getTableSchema(cl)
            lyr = self.layerLoader.load([layer_name], uniqueLoad=True, isEdgv=isEdgv)[layer_name]
        return lyr
    
    def prepareExecution(self, cl, geometryColumn='geom', selectedFeatures = False):
        """
        Prepare the process to be executed
        cl: table name
        """
        # loading layer prior to execution
        lyr = self.loadLayerBeforeValidationProcess(cl)
        # getting keyColumn because we want to be generic
        uri = QgsDataSourceURI(lyr.dataProvider().dataSourceUri())
        keyColumn = uri.keyColumn()
        #getting feature map including the edit buffer
        featureMap = self.mapInputLayer(lyr, selectedFeatures = selectedFeatures)
        #getting table name with schema
        if isinstance(cl, dict):
            tableSchema = cl['tableSchema']
            tableName = cl['tableName']
            geometryColumn = cl['geom']
            fullTableName = '''{0}.{1}'''.format(cl['tableSchema'], cl['tableName'])
        else:
            fullTableName = cl
            tableSchema, tableName = cl.split('.')

        #setting temp table name
        processTableName = fullTableName+'_temp'
        # specific EPSG search
        parameters = {'tableSchema':tableSchema, 'tableName':tableName, 'geometryColumn':geometryColumn}
        srid = self.abstractDb.findEPSG(parameters=parameters)
        #creating temp table
        self.abstractDb.createAndPopulateTempTableFromMap(fullTableName, featureMap, geometryColumn, keyColumn, srid)
        return processTableName, lyr, keyColumn
    
    def postProcessSteps(self, processTableName, lyr):
        """
        Execute the final steps after the actual process
        """
        #getting the output as a QgsVectorLayer
        outputLayer = QgsVectorLayer(self.abstractDb.getURI(processTableName, True).uri(), processTableName, "postgres")
        #updating the original layer (lyr)
        self.updateOriginalLayer(lyr, outputLayer)
        #dropping the temp table as we don't need it anymore
        self.abstractDb.dropTempTable(processTableName)
    
    def getGeometryTypeText(self, geomtype):
        if geomtype == QGis.WKBPoint:
            return 'Point'
        elif geomtype == QGis.WKBMultiPoint:
            return 'MultiPoint'
        elif geomtype == QGis.WKBLineString:
            return 'Linestring'
        elif geomtype == QGis.WKBMultiLineString:
            return 'MultiLinestring'
        elif geomtype == QGis.WKBPolygon:
            return 'Polygon'
        elif geomtype == QGis.WKBMultiPolygon:
            return 'MultiPolygon'
        else:
            raise Exception(self.tr('Operation not defined with provided geometry type!'))

    def createUnifiedLayer(self, layerList, attributeTupple = False, attributeBlackList = ''):
        """
        Creates a unified layer from a list of layers
        """
        #getting srid from something like 'EPSG:31983'
        srid = layerList[0].crs().authid().split(':')[-1]
        # creating the layer
        geomtype = layerList[0].dataProvider().geometryType()
        for lyr in layerList:
            if lyr.dataProvider().geometryType() != geomtype:
                raise Exception(self.tr('Error! Different geometry primitives!'))

        coverage = self.iface.addVectorLayer("{0}?crs=epsg:{1}".format(self.getGeometryTypeText(geomtype),srid), "coverage", "memory")
        provider = coverage.dataProvider()
        coverage.startEditing()

        #defining fields
        if not attributeTupple:
            fields = [QgsField('featid', QVariant.Int), QgsField('classname', QVariant.String)]
        else:
            fields = [QgsField('featid', QVariant.Int), QgsField('classname', QVariant.String), QgsField('tupple', QVariant.String), QgsField('blacklist', QVariant.String)]
        provider.addAttributes(fields)
        coverage.updateFields()

        totalCount = 0
        for layer in layerList:
            totalCount += layer.pendingFeatureCount()
        self.localProgress = ProgressWidget(1, totalCount - 1, self.tr('Building unified layers with  ') + ', '.join([i.name() for i in layerList])+'.', parent=self.iface.mapCanvas())
        featlist = []
        if attributeBlackList != '':
            bList = attributeBlackList.replace(' ','').split(',')
        else:
            bList = []
        for layer in layerList:
            # recording class name
            classname = layer.name()
            uri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
            keyColumn = uri.keyColumn()
            for feature in layer.getFeatures():
                newfeat = QgsFeature(coverage.pendingFields())
                newfeat.setGeometry(feature.geometry())
                newfeat['featid'] = feature.id()
                newfeat['classname'] = classname
                if attributeTupple:
                    attributeList = []
                    attributes = [field.name() for field in feature.fields() if (field.type() != 6 and field.name() != keyColumn)]
                    for attribute in attributes:
                        if attribute not in bList:
                            attributeList.append(u'{0}'.format(feature[attribute])) #done due to encode problems
                    tup = ','.join(attributeList)
                    newfeat['tupple'] = tup
                featlist.append(newfeat)
                self.localProgress.step()
        
        #inserting new features into layer
        coverage.addFeatures(featlist, True)
        coverage.commitChanges()
        return coverage

    def splitUnifiedLayer(self, outputLayer, layerList):
        """
        Updates all original layers making requests with the class name
        """
        for layer in layerList:
            classname = layer.name()
            tupplelist = [(feature['featid'], feature) for feature in outputLayer.getFeatures()]
            self.updateOriginalLayer(layer, None, featureTupleList=tupplelist)

    def getGeometryColumnFromLayer(self, layer):
        uri = QgsDataSourceURI(layer.dataProvider().dataSourceUri())
        geomColumn = uri.geometryColumn()
        return geomColumn
        
                    
                    
Ejemplo n.º 13
0
class FieldToolbox(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.iface = iface
        self.buttons = []
        self.prevLayer = None
        self.buttonName = ''
        self.category = ''
        self.widget.dbChanged.connect(self.defineFactory)
        self.releaseButtonConected = False
        self.addedFeatures = []
        self.configFromDbDict = dict()
    
    def defineFactory(self, abstractDb):
        """
        Defines the layer loader by its type
        :param abstractDb:
        :return:
        """
        self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, abstractDb)
        try:
            self.populateConfigFromDb()
        except Exception as e:
            QgsMessageLog.logMessage(self.tr('Error getting stored configuration.\n')+':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def setEditButtonEnabled(self, enabled):
        """
        Edits the current configuration settings
        :param enabled:
        :return:
        """
        self.editCurrentConfigButton.setEnabled(enabled)
    
    @pyqtSlot(bool, name='on_setupButton_clicked')
    @pyqtSlot(bool, name='on_editCurrentConfigButton_clicked')
    def populateWindow(self):
        """
        Creates the buttons according to the field setup
        """
        if self.widget.abstractDb == None:
            QtGui.QMessageBox.critical(self, self.tr('Error!'), self.tr('First select a database!'))
            return
        if isinstance(self.sender(), QPushButton):
            sender = self.sender().text()
        else:
            sender = ''
        dlg = FieldSetup(self.widget.abstractDb)
        if sender != self.tr('Setup'):
            dlg.loadReclassificationConf(self.reclassificationDict)
        if sender != '':
            result = dlg.exec_()
        else:
            result = 1
        
        if result == 1:
            self.createButtonsOnInterface(dlg)
            self.setEditButtonEnabled(True)
            
    def createButtonsOnInterface(self, dlg):
        """
        Creates button according to what is set in the configuration
        :param dlg:
        :return:
        """
        #reclassification dictionary made from the field setup file
        self.reclassificationDict = dlg.makeReclassificationDict()
        #button size defined by the user
        self.size = dlg.slider.value()
        #check if the button must be grouped by category
        withTabs = dlg.checkBox.isChecked()
        #actual button creation step
        self.createButtons(self.reclassificationDict, withTabs)
            
    @pyqtSlot(bool, name = 'on_newFeatureRadioButton_toggled')
    def turnButtonsOn(self, enabled):
        """
        Adjusts tool behavior. The default state makes the tool work with selected features
        but the user can choose to acquire a feature in real time. When working in real time the buttons must be checkable.
        """
        if enabled:
            #connecting iface signals
            self.iface.currentLayerChanged.connect(self.acquire)
            for button in self.buttons:
                #disconnecting the clicked signal
                button.clicked.disconnect(self.reclassify)
                #changing button behavior
                button.setCheckable(True)
        else:
            #disconnecting iface signals
            self.disconnectLayerSignals()
            try:self.iface.currentLayerChanged.disconnect(self.acquire)
            except:pass
            for button in self.buttons:
                #connecting the clicked signal
                button.clicked.connect(self.reclassify)
                #changing button behavior
                button.setCheckable(False)            
        
    def createWidgetWithoutTabs(self, formLayout):
        """
        Adjusts the scroll area to receive the buttons directly (not grouped by category)
        formLayout: Layout used to receive all the buttons
        """
        w = QtGui.QWidget()
        w.setLayout(formLayout)
        self.scrollArea.setWidget(w)

    def createWidgetWithTabs(self, formLayout):
        """
        Creates a scroll area for each form layout.
        formLayout: Layout used to receive the buttons in each tab
        """
        scrollArea = QtGui.QScrollArea()
        scrollArea.setWidgetResizable(True)
        scrollArea.setFrameShape(QtGui.QFrame.Shape(0))  # no frame
        w = QtGui.QWidget()
        w.setLayout(formLayout)
        scrollArea.setWidget(w)
        return scrollArea

    def on_filterLineEdit_textChanged(self, text):
        for i in self.buttons:
            if text.lower() in i.text().lower():
                i.show()
            else:
                i.hide()
    
    def createButton(self, button):
        """
        Creates the buttons according to the user size definition
        button: Button name
        """
        pushButton = QtGui.QPushButton(button)
        pushButton.clicked.connect(self.reclassify)
        pushButton.toggled.connect(self.acquire)
        if self.size == 0:
            pushButton.setMinimumSize(100, 25)
            pushButton.setStyleSheet('font-size:12px')
        elif self.size == 1:            
            pushButton.setMinimumSize(100, 40)
            pushButton.setStyleSheet('font-size:20px')
        elif self.size == 2:            
            pushButton.setMinimumSize(100, 80)
            pushButton.setStyleSheet('font-size:30px')
        self.buttons.append(pushButton)
        return pushButton        
        
    def createButtons(self, reclassificationDict, createTabs=False):
        """
        Convenience method to create buttons
        createTabs: Indicates if the buttons must be created within tabs
        """
        self.buttons = []
        widget = self.scrollArea.takeWidget()
        if createTabs:
            self.createButtonsWithTabs(reclassificationDict)
        else:
            self.createButtonsWithoutTabs(reclassificationDict)
        self.turnButtonsOn(self.newFeatureRadioButton.isChecked())
            
    def createButtonsWithoutTabs(self, reclassificationDict):
        """
        Specific method to create buttons without tabs
        reclassificationDict: dictionary used to create the buttons
        """
        formLayout = QtGui.QFormLayout()
        self.createWidgetWithoutTabs(formLayout)
        sortedButtonNames = []
        for category in reclassificationDict.keys():
            if category in ['version', 'uiParameterJsonDict']:
                continue
            for edgvClass in reclassificationDict[category].keys():
                for button in reclassificationDict[category][edgvClass].keys():
                    sortedButtonNames.append(button)
        sortedButtonNames.sort()
        for button in sortedButtonNames:       
            pushButton = self.createButton(button)
            formLayout.addRow(pushButton)

    def createButtonsWithTabs(self, reclassificationDict):
        """
        Specific method to create buttons with tabs
        reclassificationDict: dictionary used to create the buttons
        """
        gridLayout = QtGui.QGridLayout()
        tabWidget = QtGui.QTabWidget()
        tabWidget.setTabPosition(QtGui.QTabWidget.West)
        gridLayout.addWidget(tabWidget)
        self.scrollArea.setWidget(tabWidget)
        
        for category in reclassificationDict.keys():
            if category in ['version', 'uiParameterJsonDict']:
                continue
            sortedButtonNames = []
            formLayout = QtGui.QFormLayout()
            scrollArea = self.createWidgetWithTabs(formLayout)
            tabWidget.addTab(scrollArea, category)
            for edgvClass in reclassificationDict[category].keys():
                for button in reclassificationDict[category][edgvClass].keys():
                    sortedButtonNames.append(button)
            sortedButtonNames.sort()
            for button in sortedButtonNames:       
                pushButton = self.createButton(button)
                formLayout.addRow(pushButton)
                    
    def loadLayer(self, layer):
        """
        Loads the layer used in the actual reclassification
        layer: Layer name
        """
        try:
            return self.layerLoader.load([layer], uniqueLoad=True)[layer]
        except Exception as e:
            QtGui.QMessageBox.critical(self, self.tr('Error!'), self.tr('Could not load the selected classes!\n')+':'.join(e.args))
            
    def checkConditions(self):
        """
        Check the conditions to see if the tool can be used
        """
        if not self.widget.abstractDb:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Please, select a database.'))
            return False
        
        try:
            version = self.widget.abstractDb.getDatabaseVersion()
        except Exception as e:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Problem obtaining database version! Please, check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            return False
            
        if self.reclassificationDict['version'] != version:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Database version does not match the field toolbox version.'))
            return False
        return True
    
    def getLayerFromButton(self, button):
        """
        Gets the correct layer to be used in the tool
        """
        #edgvClass found in the dictionary (this is made using the sqlite seed)
        (category, edgvClass) = self.findReclassificationClass(button)
        
        driverName = self.widget.abstractDb.getType()
        if driverName == "QSQLITE":
            #reclassification layer name
            reclassificationClass = '_'.join(edgvClass.split('_')[1::])
        if driverName == "QPSQL":
            #reclassification layer name
            reclassificationClass = '_'.join(edgvClass.split('.')[1::])
            
        #getting the QgsVectorLayer to perform the reclassification
        reclassificationLayer = self.loadLayer(reclassificationClass)
            
        if reclassificationLayer:
            self.iface.setActiveLayer(reclassificationLayer)
            #entering in editing mode
            if not reclassificationLayer.isEditable():
                reclassificationLayer.startEditing()

        return (reclassificationLayer, category, edgvClass)
    
    @pyqtSlot(int)
    def setAttributesFromButton(self, featureId):
        """
        Sets the attributes for the newly added feature
        featureId: added feature
        """
        layer = self.sender()
        if isinstance(layer, QgsVectorLayer):
            layer.beginEditCommand(self.tr('DsgTools reclassification'))
            self.addedFeatures.append(featureId)

    def updateAttributesAfterAdding(self):
        """
        Updates feature attributes according to the button configuration
        :return:
        """
        layer = self.sender()
        while self.addedFeatures:
            featureId = self.addedFeatures.pop()
            #begining the edit command
            # layer.beginEditCommand(self.tr("DSG Tools reclassification tool: adjusting feature's attributes"))
            #accessing added features
            editBuffer = layer.editBuffer()
            features = editBuffer.addedFeatures()
            for key in features.keys():
                #just checking the newly added feature, the other I don't care
                if key == featureId:
                    feature = features[key]
                    #setting the attributes using the reclassification dictionary
                    self.setFeatureAttributes(feature, editBuffer)
            layer.endEditCommand()

    def setFeatureAttributes(self, newFeature, editBuffer=None):
        """
        Changes attribute values according to the reclassification dict using the edit buffer
        newFeature: newly added
        editBuffer: layer edit buffer
        """
        #setting the attributes using the reclassification dictionary
        for attribute in self.reclassificationDict[self.category][self.edgvClass][self.buttonName].keys():
            idx = newFeature.fieldNameIndex(attribute)
            #value to be changed
            value = self.reclassificationDict[self.category][self.edgvClass][self.buttonName][attribute]
            if value == '':
                continue
            #actual attribute change
            if editBuffer:
                #this way we are working with the edit buffer
                editBuffer.changeAttributeValue(newFeature.id(), idx, value)
            else:
                #this way are working with selected features and inserting a new one in the layer
                newFeature.setAttribute(idx, value)
                
        if not editBuffer:
            # we should return when under the normal behavior
            return newFeature
        
    def disconnectLayerSignals(self):
        """
        Disconnecting the signals from the previous layer
        """
        if self.prevLayer:
            try:
                self.prevLayer.featureAdded.disconnect(self.setAttributesFromButton)
                self.prevLayer.editCommandEnded.disconnect(self.updateAttributesAfterAdding)
                self.prevLayer.editFormConfig().setSuppress(QgsEditFormConfig.SuppressOff)
            except:
                pass

    @pyqtSlot(bool)
    def acquire(self, pressed):
        """
        Performs the actual reclassification, moving the geometry to the correct layer along with the specified attributes.
        The difference here is the use of real time editing to make the reclassification
        """
        if pressed:
            if not self.checkConditions():
                return
            
            #getting the object that sent the signal
            sender = self.sender()
            
            #if the sender is the iface object, this means that the user made the click and changed the current layer
            #when this happens we should untoggle all buttons
            if isinstance(sender, QgisInterface):
                #checking if another button is checked
                for button in self.buttons:
                    button.setChecked(False)
                #return and do nothing else
                return

            #button that sent the signal
            self.buttonName = sender.text()
    
            #checking if another button is checked
            for button in self.buttons:
                if button.text() != self.buttonName and button.isChecked():
                    button.setChecked(False)
                    
            #disconnecting the previous layer
            self.disconnectLayerSignals()
    
            (reclassificationLayer, self.category, self.edgvClass) = self.getLayerFromButton(self.buttonName)

            #suppressing the form dialog
            reclassificationLayer.editFormConfig().setSuppress(QgsEditFormConfig.SuppressOn)
            #connecting addedFeature signal
            reclassificationLayer.featureAdded.connect(self.setAttributesFromButton)
            reclassificationLayer.editCommandEnded.connect(self.updateAttributesAfterAdding)
            #triggering the add feature tool
            self.iface.actionAddFeature().trigger()            
            #setting the previous layer             
            self.prevLayer = reclassificationLayer        
        else:
            #disconnecting the previous layer
            self.disconnectLayerSignals()
            
    @pyqtSlot()
    def reclassify(self):
        """
        Performs the actual reclassification, moving the geometry to the correct layer along with the specified attributes
        """
        if not self.checkConditions():
            return
        
        somethingMade = False
        reclassifiedFeatures = 0
        
        #button that sent the signal
        self.buttonName = self.sender().text()
        (reclassificationLayer, self.category, self.edgvClass) = self.getLayerFromButton(self.buttonName)
        geomType = reclassificationLayer.geometryType()
        hasMValues =  QgsWKBTypes.hasM(int(reclassificationLayer.wkbType()))    #generic check (not every database is implemented as ours)
        hasZValues =  QgsWKBTypes.hasZ(int(reclassificationLayer.wkbType()))    #
        isMulti = QgsWKBTypes.isMultiType(int(reclassificationLayer.wkbType())) #
        mapLayers = self.iface.mapCanvas().layers()
        crsSrc = QgsCoordinateReferenceSystem(self.widget.crs.authid())
        deleteList = []
        for mapLayer in mapLayers:
            if mapLayer.type() != QgsMapLayer.VectorLayer:
                continue
            
            #iterating over selected features
            featList = []
            mapLayerCrs = mapLayer.crs()
            #creating a coordinate transformer (mapLayerCrs to crsSrc)
            coordinateTransformer = QgsCoordinateTransform(mapLayerCrs, crsSrc)
            for feature in mapLayer.selectedFeatures():
                geomList = []
                geom = feature.geometry()
                if geom.type() != geomType:
                    continue
                if 'geometry' in dir(geom):
                    if not hasMValues:
                        geom.geometry().dropMValue()
                    if not hasZValues:
                        geom.geometry().dropZValue()
                if isMulti and not geom.isMultipart():
                    geom.convertToMultiType()
                    geomList.append(geom)
                elif not isMulti and geom.isMultipart():
                    #deaggregate here
                    parts = geom.asGeometryCollection()
                    for part in parts:
                        part.convertToSingleType()
                        geomList.append(part)
                else:
                    geomList.append(geom)
                for newGeom in geomList:
                    #creating a new feature according to the reclassification layer
                    newFeature = QgsFeature(reclassificationLayer.pendingFields())
                    #transforming the geometry to the correct crs
                    geom.transform(coordinateTransformer)
                    #setting the geometry
                    newFeature.setGeometry(newGeom)
                    #setting the attributes using the reclassification dictionary
                    newFeature = self.setFeatureAttributes(newFeature)
                    #adding the newly created feature to the addition list
                    featList.append(newFeature)
                    somethingMade = True
                    deleteList.append({'originalLyr':mapLayer,'featid':feature.id()})
            #actual feature insertion
            reclassificationLayer.addFeatures(featList, False)
            reclassifiedFeatures += len(featList)
        
        for item in deleteList:
            item['originalLyr'].startEditing()
            item['originalLyr'].deleteFeature(item['featid'])
        
        if somethingMade:
            self.iface.messageBar().pushMessage(self.tr('Information!'), self.tr('{} features reclassified with success!').format(reclassifiedFeatures), level=QgsMessageBar.INFO, duration=3)

    def findReclassificationClass(self, button):
        """
        Finds the reclassification class according to the button
        button: Button clicked by the user to perform the reclassification
        """
        for category in self.reclassificationDict.keys():
            if category == 'version' or category == 'uiParameterJsonDict':
                continue
            for edgvClass in self.reclassificationDict[category].keys():
                for buttonName in self.reclassificationDict[category][edgvClass].keys():
                    if button == buttonName:
                        #returning the desired edgvClass
                        return (category, edgvClass)
        return ()
    
    def populateConfigFromDb(self):
        """
        Populates configFromDbComboBox with config from public.field_toolbox_config
        """
        driverName = self.widget.abstractDb.getType()
        self.configFromDbComboBox.clear()
        self.configFromDbComboBox.addItem(self.tr('Select Stored Config (optional)'))
        if driverName == 'QPSQL':
            self.configFromDbComboBox.setEnabled(True)
            propertyDict = self.widget.abstractDb.getPropertyDict('FieldToolBoxConfig')
            dbVersion = self.widget.abstractDb.getDatabaseVersion()
            if dbVersion in propertyDict.keys():
                self.configFromDbDict = propertyDict[dbVersion]
            nameList = self.configFromDbDict.keys()
            nameList.sort()
            for name in nameList:
                self.configFromDbComboBox.addItem(name)
        else:
            self.configFromDbComboBox.setEnabled(False)
            self.configFromDbDict = dict()
    
    @pyqtSlot(int)
    def on_configFromDbComboBox_currentIndexChanged(self, idx):
        if idx != 0 and idx != -1:
            self.reclassificationDict = self.configFromDbDict[self.configFromDbComboBox.currentText()]
            self.populateWindow()
        if idx == 0:
            self.reclassificationDict = {}
            self.createButtonsWithTabs(self.reclassificationDict)
Ejemplo n.º 14
0
class ValidationToolbox(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface):
        """
        Constructor
        """
        super(ValidationToolbox, self).__init__()
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.edgvLayer = None
        self.flagLyr = None
        self.iface = iface
        self.databaseLineEdit.setReadOnly(True)
        self.configWindow = ValidationConfig()
        self.configWindow.widget.connectionChanged.connect(
            self.updateDbLineEdit)
        self.validationManager = None
        self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableView.customContextMenuRequested.connect(
            self.createContextMenu)
        self.ruleEnforcer = None
        self.attributeRulesEditorPushButton.hide()
        self.itemList = []
        self.filterDict = {
            self.tr('Process Name'): DsgEnums.ProcessName,
            self.tr('Class Name'): DsgEnums.ClassName
        }
        self.processChanged = False  # for processes filtering classes mechanics

    def createContextMenu(self, position):
        """
        Creates the flag menu
        """
        menu = QMenu()
        item = self.tableView.indexAt(position)
        if item:
            menu.addAction(self.tr('Zoom to flag'), self.zoomToFlag)
            menu.addAction(self.tr('Remove flag'), self.removeCurrentFlag)
            # menu.addAction(self.tr('Set Visited'), self.setFlagVisited)
            # menu.addAction(self.tr('Set Unvisited'), self.setFlagUnvisited)
        menu.exec_(self.tableView.viewport().mapToGlobal(position))

    def removeCurrentFlag(self):
        """
        Creates the remove flag menu
        """
        try:
            flagId = self.tableView.selectionModel().selection().indexes(
            )[0].data()
        except:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'),
                                       self.tr('No flags were selected!'))
            return
        self.configWindow.widget.abstractDb.deleteProcessFlags(flagId=flagId)
        self.refreshFlags()

    @pyqtSlot()
    def on_theSelectionModel_selectionChanged(self):
        """
        To do.
        """
        print 'mudou'

    def setFlagVisited(self):
        """
        To do
        """
        print 'visited'

    def setFlagUnvisited(self):
        """
        To do
        """
        print 'unvisited'

    def zoomToFlag(self):
        """
        Zooms the map canvas to the current selected flag
        """
        try:
            idx = self.tableView.selectionModel().selection().indexes(
            )[0].data()
        except:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'),
                                       self.tr('No flags were selected!'))
            return
        dimension = self.tableView.selectionModel().selection().indexes(
        )[6].data()
        if dimension == 0:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOINT',
                'lyrName': 'flags_validacao_p',
                'tableName': 'aux_flags_validacao_p',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 1:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTILINESTRING',
                'lyrName': 'flags_validacao_l',
                'tableName': 'aux_flags_validacao_l',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 2:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOLYGON',
                'lyrName': 'flags_validacao_a',
                'tableName': 'aux_flags_validacao_a',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        flagLyr = self.loadFlagLyr(layer)
        flagLyr.setLayerTransparency(50)
        flagLyr.removeSelection()
        self.iface.mapCanvas().refresh()
        flagLyr.select(idx)
        bbox = flagLyr.boundingBoxOfSelected()
        mapbox = self.iface.mapCanvas().mapSettings().layerToMapCoordinates(
            flagLyr, bbox)
        self.iface.mapCanvas().setExtent(mapbox)
        self.iface.mapCanvas().refresh()

    def loadAllFlagLayers(self):
        """
        Loads all flags
        """
        pointLayer = {
            'cat': 'aux',
            'geom': 'geom',
            'geomType': 'MULTIPOINT',
            'lyrName': 'flags_validacao_p',
            'tableName': 'aux_flags_validacao_p',
            'tableSchema': 'validation',
            'tableType': 'BASE TABLE'
        }
        lineLayer = {
            'cat': 'aux',
            'geom': 'geom',
            'geomType': 'MULTILINESTRING',
            'lyrName': 'flags_validacao_l',
            'tableName': 'aux_flags_validacao_l',
            'tableSchema': 'validation',
            'tableType': 'BASE TABLE'
        }
        areaLayer = {
            'cat': 'aux',
            'geom': 'geom',
            'geomType': 'MULTIPOLYGON',
            'lyrName': 'flags_validacao_a',
            'tableName': 'aux_flags_validacao_a',
            'tableSchema': 'validation',
            'tableType': 'BASE TABLE'
        }
        self.loadFlagLyr([pointLayer, lineLayer, areaLayer])

    def loadFlagLyr(self, layer):
        """
        Loads the flag layer. It checks if the flag layer is already loaded, case not, it loads the flag layer into the TOC
        layer: layer name
        """
        self.layerLoader = LayerLoaderFactory().makeLoader(
            self.iface, self.configWindow.widget.abstractDb)
        if isinstance(layer, list):
            return self.layerLoader.load(layer, uniqueLoad=True)
        else:
            return self.layerLoader.load([layer],
                                         uniqueLoad=True)[layer['lyrName']]

    def checkFlagsLoaded(self, layer):
        """
        Checks if the flag layer is already loaded
        layer: layer name
        """
        loadedLayers = self.iface.mapCanvas().layers()
        candidateLyrs = []
        for lyr in loadedLayers:
            if lyr.name() == layer:
                candidateLyrs.append(lyr)
        for lyr in candidateLyrs:
            if self.configWindow.widget.abstractDb.isLyrInDb(lyr):
                return True
        return False

    @pyqtSlot(bool)
    def on_openDbPushButton_clicked(self):
        """
        Opend dialog for database connection
        """
        self.configWindow.show()

    @pyqtSlot(bool)
    def on_historyButton_clicked(self):
        """
        Shows the validation history
        """
        historyWindow = ValidationHistory(self.configWindow.widget.abstractDb)
        historyWindow.exec_()

    @pyqtSlot()
    def updateDbLineEdit(self):
        """
        Updates the database information
        """
        database = ''
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            database = self.configWindow.widget.comboBoxPostgis.currentText()
            self.databaseLineEdit.setText(database)
            self.validationManager = ValidationManager(
                self.configWindow.widget.abstractDb, self.iface)
            self.populateProcessList()
            self.databaseLineEdit.setText(database)
            # adjusting flags table model
            self.projectModel = QSqlTableModel(
                None, self.configWindow.widget.abstractDb.db)
            self.projectModel.setTable('validation.aux_flags_validacao')
            self.projectModel.select()
            self.tableView.setModel(self.projectModel)
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(
                self.tr('Error loading db: ') + ':'.join(e.args),
                "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            self.processTreeWidget.clear()

    def on_filterLineEdit_textChanged(self, text):
        for i in self.itemList:
            if text.lower() in i.text(1).lower():
                i.setHidden(False)
            else:
                i.setHidden(True)

    def populateProcessList(self):
        """
        Populates the process list. It also checks the status of each available process
        """
        self.processTreeWidget.clear()
        self.edgvLayer = None
        self.flagLyr = None
        self.itemList = []
        rootItem = self.processTreeWidget.invisibleRootItem()
        procList = sorted(self.validationManager.processDict)
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        for i in range(len(procList)):
            item = QtGui.QTreeWidgetItem(rootItem)
            item.setText(0, str(i + 1))
            item.setText(1, procList[i])

            status = None
            try:
                status = self.configWindow.widget.abstractDb.getValidationStatusText(
                    self.validationManager.processDict[procList[i]])
            except Exception as e:
                QtGui.QMessageBox.critical(
                    self, self.tr('Critical!'),
                    self.tr('A problem occurred! Check log for details.'))
                QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                         QgsMessageLog.CRITICAL)
                status = 'Error! Check log!'

            if not status:
                item.setText(2, 'Not yet ran')
            else:
                item.setText(2, status)
            self.itemList.append(item)
        for i in range(3):
            self.processTreeWidget.resizeColumnToContents(i)
        self.filterLineEdit.clear()
        QApplication.restoreOverrideCursor()

    @pyqtSlot(bool)
    def on_reRunButton_clicked(self):
        """
        Re-runs last process with the same attributes.
        """
        if not self.validationManager:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Select a database to run process!'))
            return
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            procReturn = self.validationManager.runLastProcess()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            procReturn = 0
            QApplication.restoreOverrideCursor()
        QApplication.restoreOverrideCursor()
        self.populateProcessList()
        if procReturn == 0:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Process error. Check log for details.'))
        elif procReturn == -1:
            QtGui.QMessageBox.information(self, self.tr('Information!'),
                                          self.tr('Process canceled by user!'))
        elif procReturn == -2:
            QtGui.QMessageBox.information(
                self, self.tr('Information!'),
                self.tr('No previous process run this session.'))
        else:
            QtGui.QMessageBox.warning(
                self, self.tr('Success!'),
                self.tr('Process successfully executed!'))
            #executou! show!

    @pyqtSlot(bool)
    def on_runButton_clicked(self):
        """
        Runs the current selected process
        """
        selectedItems = self.processTreeWidget.selectedItems()
        if len(selectedItems) == 0:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'),
                                       self.tr('Select a process to run!'))
            return
        processName = selectedItems[0].text(1)
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            procReturn = self.validationManager.executeProcessV2(processName)
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            procReturn = 0
            QApplication.restoreOverrideCursor()
        QApplication.restoreOverrideCursor()
        self.populateProcessList()
        if procReturn == 0:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Process error. Check log for details.'))
        elif procReturn == -1:
            QtGui.QMessageBox.information(self, self.tr('Information!'),
                                          self.tr('Process canceled by user!'))
        else:
            QtGui.QMessageBox.warning(
                self, self.tr('Success!'),
                self.tr('Process successfully executed!'))
            #executou! show!
            #load flag layers
            self.loadAllFlagLayers()
        self.iface.mapCanvas().refresh(
        )  #atualizar canvas para mostrar resultado para o usuário

    @pyqtSlot(int, name='on_validationTabWidget_currentChanged')
    def refreshFlags(self):
        """
        Changes the current tab in the validation tool box
        """
        if self.validationTabWidget.currentIndex(
        ) == 1 and self.configWindow.widget.abstractDb != None:
            self.projectModel.setTable('validation.filtered_flags')
            self.projectModel.select()
            self.configWindow.widget.abstractDb.createFilteredFlagsViewTable()
        # populates the comboBoxes
        self.classFilterComboBox.clear()
        self.processFilterComboBox.clear()
        if self.configWindow.widget.abstractDb:
            listProcesses = self.configWindow.widget.abstractDb.fillComboBoxProcessOrClasses(
                "process")
            listClasses = self.configWindow.widget.abstractDb.fillComboBoxProcessOrClasses(
                "class")
            self.classFilterComboBox.addItems(listClasses)
            self.processFilterComboBox.addItems(listProcesses)

    @pyqtSlot(bool)
    def on_rulesEditorButton_clicked(self):
        """
        Opens the spatial rule editor
        """
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            dlg = RulesEditor(self.configWindow.widget.abstractDb)
            dlg.exec_()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Database not loaded or a problem occurred.\n') +
                ':'.join(e.args))

    @pyqtSlot(int, name='on_processFilterComboBox_currentIndexChanged')
    def refreshFlagListOnProcessSelection(self):
        """
        Refreshs the list of processes available for filtering the view
        """
        # marking as a change of process change for on_classFilterComboBox_currentIndexChanged
        self.processChanged = True
        className = self.classFilterComboBox.currentText()
        processName = self.processFilterComboBox.currentText()
        # classes should be repopulated for filtering purposes
        # clear classes available
        self.classFilterComboBox.clear()
        listClasses = self.configWindow.widget.abstractDb.fillComboBoxProcessOrClasses(
            "class", filteringProcess=processName)
        # repopulate classes available for selected class
        self.classFilterComboBox.addItems(listClasses)
        # getting text index for resetting after repopulation
        idx = max(0, self.classFilterComboBox.findText(className))
        self.classFilterComboBox.setCurrentIndex(idx)
        className = self.classFilterComboBox.currentText()
        self.configWindow.widget.abstractDb.createFilteredFlagsViewTable(
            className=className, processName=processName)
        self.projectModel.select()

    @pyqtSlot(int, name='on_classFilterComboBox_currentIndexChanged')
    def refreshFlagListOnClassSelection(self):
        """
        Refreshs the list of classes available for filtering the view
        """
        if self.processChanged:
            # if index change is due to class change, the table update
            # is set by process changing signal. Nothing to be done here.
            self.processChanged = (not self.processChanged)
            return
        className = self.classFilterComboBox.currentText()
        processName = self.processFilterComboBox.currentText()
        self.configWindow.widget.abstractDb.createFilteredFlagsViewTable(
            className=className, processName=processName)
        self.projectModel.select()

    @pyqtSlot(bool)
    def on_ruleEnforcerRadio_toggled(self, checked):
        """
        Toggles the spatial rule enforcer
        """
        if checked:
            self.ruleEnforcer = SpatialRuleEnforcer(
                self.validationManager.postgisDb, self.iface)
            self.ruleEnforcer.connectEditingSignals()
            self.ruleEnforcer.ruleTested.connect(self.refreshFlags)
        else:
            self.ruleEnforcer.disconnectEditingSignals()
            self.ruleEnforcer.ruleTested.disconnect(self.refreshFlags)

    @pyqtSlot(bool)
    def on_attributeRulesEditorPushButton_clicked(self):
        """
        Opens the attribute rule editor
        """
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            dlg = AttributeRulesEditor(self.configWindow.widget.abstractDb)
            dlg.exec_()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Database not loaded or a problem occurred.\n') +
                ':'.join(e.args))

    @pyqtSlot(bool)
    def on_clearAllPushButton_clicked(self):
        """
        Deletes all flags from validation.aux_flags
        1- Get abstractDb
        2- Delete flag
        """
        try:
            if QtGui.QMessageBox.question(
                    self, self.tr('Question'),
                    self.tr('Do you really want to clear all flags?'),
                    QtGui.QMessageBox.Ok
                    | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel:
                return
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            self.configWindow.widget.abstractDb.deleteProcessFlags()
            QApplication.restoreOverrideCursor()
            #refresh
            self.refreshFlags()
            self.iface.mapCanvas().refresh()
        except Exception as e:
            QApplication.restoreOverrideCursor()
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Flags not deleted.\n') + ':'.join(e.args))

    @pyqtSlot(bool)
    def on_clearSelectedPushButton_clicked(self):
        """
        Deletes selected flags on the panel from validation.aux_flags
        """
        try:
            if QtGui.QMessageBox.question(
                    self, self.tr('Question'),
                    self.tr('Do you really want to clear those flags?'),
                    QtGui.QMessageBox.Ok
                    | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel:
                return
            QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
            # check what is filtered
            processName = self.processFilterComboBox.currentText()
            layerName = self.classFilterComboBox.currentText()
            if (processName or layerName):
                self.configWindow.widget.abstractDb.deleteProcessFlags(
                    processName, layerName)
            else:
                QApplication.restoreOverrideCursor()
                QtGui.QMessageBox.critical(
                    self, self.tr('Critical!'),
                    self.
                    tr('Flags not deleted as no Process nor Class was chosen.\n'
                       ))
                return
            QApplication.restoreOverrideCursor()
            # refresh View Table with lasting flags
            self.refreshFlags()
            self.iface.mapCanvas().refresh()
        except Exception as e:
            QApplication.restoreOverrideCursor()
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Flags not deleted.\n') + ':'.join(e.args))
Ejemplo n.º 15
0
class ValidationToolbox(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface):
        """
        Constructor
        """
        super(ValidationToolbox, self).__init__()
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.edgvLayer = None
        self.flagLyr = None
        self.iface = iface
        self.databaseLineEdit.setReadOnly(True)
        self.configWindow = ValidationConfig()
        self.configWindow.widget.connectionChanged.connect(
            self.updateDbLineEdit)
        self.validationManager = None
        self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableView.customContextMenuRequested.connect(
            self.createMenuEditFlagStatus)
        self.ruleEnforcer = None
        self.attributeRulesEditorPushButton.hide()
        self.itemList = []

    def createMenuEditFlagStatus(self, position):
        """
        Creates the flag menu
        """
        menu = QMenu()
        item = self.tableView.indexAt(position)
        if item:
            menu.addAction(self.tr('Zoom to flag'), self.zoomToFlag)
#             menu.addAction(self.tr('Set Visited'), self.setFlagVisited)
#             menu.addAction(self.tr('Set Unvisited'), self.setFlagUnvisited)
        menu.exec_(self.tableView.viewport().mapToGlobal(position))

    @pyqtSlot()
    def on_theSelectionModel_selectionChanged(self):
        """
        To do.
        """
        print 'mudou'

    def setFlagVisited(self):
        """
        To do
        """
        print 'visited'

    def setFlagUnvisited(self):
        """
        To do
        """
        print 'unvisited'

    def zoomToFlag(self):
        """
        Zooms the map canvas to the current selected flag
        """
        idx = self.tableView.selectionModel().selection().indexes()[0].data()

        dimension = self.tableView.selectionModel().selection().indexes(
        )[6].data()
        if dimension == 0:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOINT',
                'lyrName': 'flags_validacao_p',
                'tableName': 'aux_flags_validacao_p',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 1:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTILINESTRING',
                'lyrName': 'flags_validacao_l',
                'tableName': 'aux_flags_validacao_l',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }
        elif dimension == 2:
            layer = {
                'cat': 'aux',
                'geom': 'geom',
                'geomType': 'MULTIPOLYGON',
                'lyrName': 'flags_validacao_a',
                'tableName': 'aux_flags_validacao_a',
                'tableSchema': 'validation',
                'tableType': 'BASE TABLE'
            }

        flagLyr = self.loadFlagLyr(layer)
        flagLyr.setLayerTransparency(50)
        flagLyr.removeSelection()
        self.iface.mapCanvas().refresh()
        flagLyr.select(idx)
        bbox = flagLyr.boundingBoxOfSelected()
        mapbox = self.iface.mapCanvas().mapSettings().layerToMapCoordinates(
            flagLyr, bbox)
        self.iface.mapCanvas().setExtent(mapbox)
        self.iface.mapCanvas().refresh()

    def loadFlagLyr(self, layer):
        """
        Loads the flag layer. It checks if the flag layer is already loaded, case not, it loads the flag layer into the TOC
        layer: layer name
        """
        self.layerLoader = LayerLoaderFactory().makeLoader(
            self.iface, self.configWindow.widget.abstractDb)
        return self.layerLoader.load([layer],
                                     uniqueLoad=True)[layer['lyrName']]

    def checkFlagsLoaded(self, layer):
        """
        Checks if the flag layer is already loaded
        layer: layer name
        """
        loadedLayers = self.iface.mapCanvas().layers()
        candidateLyrs = []
        for lyr in loadedLayers:
            if lyr.name() == layer:
                candidateLyrs.append(lyr)
        for lyr in candidateLyrs:
            if self.configWindow.widget.abstractDb.isLyrInDb(lyr):
                return True
        return False

    @pyqtSlot(bool)
    def on_openDbPushButton_clicked(self):
        """
        Opend dialog for database connection
        """
        self.configWindow.show()

    @pyqtSlot(bool)
    def on_historyButton_clicked(self):
        """
        Shows the validation history
        """
        historyWindow = ValidationHistory(self.configWindow.widget.abstractDb)
        historyWindow.exec_()

    @pyqtSlot()
    def updateDbLineEdit(self):
        """
        Updates the database information
        """
        database = ''
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            database = self.configWindow.widget.comboBoxPostgis.currentText()
            self.databaseLineEdit.setText(database)
            self.validationManager = ValidationManager(
                self.configWindow.widget.abstractDb, self.iface)
            self.populateProcessList()
            self.databaseLineEdit.setText(database)

            # adjusting flags table model
            self.projectModel = QSqlTableModel(
                None, self.configWindow.widget.abstractDb.db)
            self.projectModel.setTable('validation.aux_flags_validacao')
            self.projectModel.select()
            self.tableView.setModel(self.projectModel)
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(
                self.tr('Error loading db: ') + ':'.join(e.args),
                "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            self.processTreeWidget.clear()

    def on_filterLineEdit_textChanged(self, text):
        for i in self.itemList:
            if text.lower() in i.text(1).lower():
                i.setHidden(False)
            else:
                i.setHidden(True)

    def populateProcessList(self):
        """
        Populates the process list. It also checks the status of each available process
        """
        self.processTreeWidget.clear()
        self.edgvLayer = None
        self.flagLyr = None
        self.itemList = []
        rootItem = self.processTreeWidget.invisibleRootItem()
        procList = sorted(self.validationManager.processDict)
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        for i in range(len(procList)):
            item = QtGui.QTreeWidgetItem(rootItem)
            item.setText(0, str(i + 1))
            item.setText(1, procList[i])

            status = None
            try:
                status = self.configWindow.widget.abstractDb.getValidationStatusText(
                    self.validationManager.processDict[procList[i]])
            except Exception as e:
                QtGui.QMessageBox.critical(
                    self, self.tr('Critical!'),
                    self.tr('A problem occurred! Check log for details.'))
                QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                         QgsMessageLog.CRITICAL)
                status = 'Error! Check log!'

            if not status:
                item.setText(2, 'Not yet ran')
            else:
                item.setText(2, status)
            self.itemList.append(item)
        for i in range(3):
            self.processTreeWidget.resizeColumnToContents(i)
        self.filterLineEdit.clear()
        QApplication.restoreOverrideCursor()

    @pyqtSlot(bool)
    def on_runButton_clicked(self):
        """
        Runs the current selected process
        """
        selectedItems = self.processTreeWidget.selectedItems()
        if len(selectedItems) == 0:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'),
                                       self.tr('Select a process to run!'))
            return
        processName = selectedItems[0].text(1)
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        try:
            procReturn = self.validationManager.executeProcess(processName)
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('A problem occurred! Check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin",
                                     QgsMessageLog.CRITICAL)
            procReturn = 0
            QApplication.restoreOverrideCursor()
        QApplication.restoreOverrideCursor()
        self.populateProcessList()
        if procReturn == 0:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Process error. Check log for details.'))
        elif procReturn == -1:
            QtGui.QMessageBox.information(self, self.tr('Information!'),
                                          self.tr('Process canceled by user!'))
        else:
            QtGui.QMessageBox.warning(
                self, self.tr('Success!'),
                self.tr('Process successfully executed!'))
            #executou! show!

    @pyqtSlot(int, name='on_validationTabWidget_currentChanged')
    def refreshFlags(self):
        """
        Changes the current tab in the validation tool box
        """
        if self.validationTabWidget.currentIndex(
        ) == 1 and self.configWindow.widget.abstractDb <> None:
            self.projectModel.select()

    @pyqtSlot(bool)
    def on_rulesEditorButton_clicked(self):
        """
        Opens the spatial rule editor
        """
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            dlg = RulesEditor(self.configWindow.widget.abstractDb)
            dlg.exec_()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Database not loaded or a problem occurred.\n') +
                ':'.join(e.args))

    @pyqtSlot(bool)
    def on_ruleEnforcerRadio_toggled(self, checked):
        """
        Toggles the spatial rule enforcer
        """
        if checked:
            self.ruleEnforcer = SpatialRuleEnforcer(
                self.validationManager.postgisDb, self.iface)
            self.ruleEnforcer.connectEditingSignals()
            self.ruleEnforcer.ruleTested.connect(self.refreshFlags)
        else:
            self.ruleEnforcer.disconnectEditingSignals()
            self.ruleEnforcer.ruleTested.disconnect(self.refreshFlags)

    @pyqtSlot(bool)
    def on_attributeRulesEditorPushButton_clicked(self):
        """
        Opens the attribute rule editor
        """
        try:
            self.configWindow.widget.abstractDb.checkAndOpenDb()
            dlg = AttributeRulesEditor(self.configWindow.widget.abstractDb)
            dlg.exec_()
        except Exception as e:
            QtGui.QMessageBox.critical(
                self, self.tr('Critical!'),
                self.tr('Database not loaded or a problem occurred.\n') +
                ':'.join(e.args))
Ejemplo n.º 16
0
class LoadAuxStruct(QtGui.QDialog, FORM_CLASS):
    def __init__(self, iface, parent=None):
        """
        Constructor
        """
        super(self.__class__, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface
        self.layerFactory = LayerLoaderFactory()
        self.selectedClasses = []
        self.widget.tabWidget.setTabEnabled(0,True)
        self.widget.tabWidget.setTabEnabled(1,False)
        self.widget.tabWidget.setCurrentIndex(0)
        self.bar = QgsMessageBar()
        self.setLayout(QtGui.QGridLayout(self))
        self.layout().setContentsMargins(0,0,0,0)
        self.layout().setAlignment(QtCore.Qt.AlignTop)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed)
        self.bar.setSizePolicy(sizePolicy)
        self.layout().addWidget(self.bar, 0,0,1,1)

        QtCore.QObject.connect(self.widget, QtCore.SIGNAL(("problemOccurred()")), self.pushMessage)
        self.widget.dbChanged.connect(self.widgetConv.setDatabase)

    @pyqtSlot(bool)
    def on_pushButtonCancel_clicked(self):
        """
        Closes the dialog
        """
        self.close()
        
    def pushMessage(self, msg):
        """
        Pushes a message into message bar
        """
        self.bar.pushMessage("", msg, level=QgsMessageBar.CRITICAL)

    @pyqtSlot(bool)
    def on_pushButtonOk_clicked(self):
        """
        Checks the linee-centroid structure and loads the correspondent layers 
        """
        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
        if self.widgetConv.settingDict == dict():
            QApplication.restoreOverrideCursor()
            self.bar.pushMessage(self.tr("Error!"), self.tr("Could not load auxiliary classes! Check log for details!"), level=QgsMessageBar.CRITICAL)
        else:
            self.loadLayers()
        QApplication.restoreOverrideCursor()
        self.close()

    def loadLayers(self):
        """
        Loads the layers defined in the line-centroid structure
        """
        try:
            if self.widget.abstractDb.getDatabaseVersion() == 'Non_EDGV':
                isEdgv = False
            else:
                isEdgv = True
            auxClassesDict = self.widgetConv.settingDict['earthCoverageDict']
            auxClasses = []
            for key in auxClassesDict.keys():
                for cl in auxClassesDict[key]:
                    if cl not in auxClasses:
                        if '.' in cl:
                            classToLoad = cl.split('.')[-1]
                        else:
                            classToLoad = cl
                        auxClasses.append(classToLoad)
            auxCentroids = self.widgetConv.abstractDb.getEarthCoverageCentroids()
            auxClasses = auxClasses + auxCentroids
            auxClasses.sort(reverse=True)
            auxClasses = [self.widgetConv.settingDict['frameLayer'].split('.')[-1]]+auxClasses
            factory = self.layerFactory.makeLoader(self.iface, self.widget.abstractDb, loadCentroids=True)
            factory.load(auxClasses, uniqueLoad = True, isEdgv = isEdgv)
        except Exception as e:
                QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
                self.bar.pushMessage(self.tr("Error!"), self.tr("Could not load auxiliary classes! Check log for details!"), level=QgsMessageBar.CRITICAL)
                
Ejemplo n.º 17
0
class FieldToolbox(QtGui.QDockWidget, FORM_CLASS):
    def __init__(self, iface, parent = None):
        """Constructor."""
        super(self.__class__, self).__init__(parent)
        # Set up the user interface from Designer.
        # After setupUI you can access any designer object by doing
        # self.<objectname>, and you can use autoconnect slots - see
        # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
        # #widgets-and-dialogs-with-auto-connect
        self.setupUi(self)
        self.iface = iface
        self.buttons = []
        self.prevLayer = None
        self.buttonName = ''
        self.category = ''
        self.widget.dbChanged.connect(self.defineFactory)
        self.releaseButtonConected = False
        self.addedFeatures = []
        self.configFromDbDict = dict()
    
    def defineFactory(self, abstractDb):
        """
        Defines the layer loader by its type
        :param abstractDb:
        :return:
        """
        self.layerLoader = LayerLoaderFactory().makeLoader(self.iface, abstractDb)
        try:
            self.populateConfigFromDb()
        except Exception as e:
            QgsMessageLog.logMessage(self.tr('Error getting stored configuration.\n')+':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
    
    def setEditButtonEnabled(self, enabled):
        """
        Edits the current configuration settings
        :param enabled:
        :return:
        """
        self.editCurrentConfigButton.setEnabled(enabled)
    
    @pyqtSlot(bool, name='on_setupButton_clicked')
    @pyqtSlot(bool, name='on_editCurrentConfigButton_clicked')
    def populateWindow(self):
        """
        Creates the buttons according to the field setup
        """
        if self.widget.abstractDb == None:
            QtGui.QMessageBox.critical(self, self.tr('Error!'), self.tr('First select a database!'))
            return
        if isinstance(self.sender(), QPushButton):
            sender = self.sender().text()
        else:
            sender = ''
        dlg = FieldSetup(self.widget.abstractDb)
        if sender != self.tr('Setup'):
            dlg.loadReclassificationConf(self.reclassificationDict)
        if sender != '':
            result = dlg.exec_()
        else:
            result = 1
        
        if result == 1:
            self.createButtonsOnInterface(dlg)
            self.setEditButtonEnabled(True)
            
    def createButtonsOnInterface(self, dlg):
        """
        Creates button according to what is set in the configuration
        :param dlg:
        :return:
        """
        #reclassification dictionary made from the field setup file
        self.reclassificationDict = dlg.makeReclassificationDict()
        #button size defined by the user
        self.size = dlg.slider.value()
        #check if the button must be grouped by category
        withTabs = dlg.checkBox.isChecked()
        #actual button creation step
        self.createButtons(self.reclassificationDict, withTabs)
            
    @pyqtSlot(bool, name = 'on_newFeatureRadioButton_toggled')
    def turnButtonsOn(self, enabled):
        """
        Adjusts tool behavior. The default state makes the tool work with selected features
        but the user can choose to acquire a feature in real time. When working in real time the buttons must be checkable.
        """
        if enabled:
            #connecting iface signals
            self.iface.currentLayerChanged.connect(self.acquire)
            for button in self.buttons:
                #disconnecting the clicked signal
                button.clicked.disconnect(self.reclassify)
                #changing button behavior
                button.setCheckable(True)
        else:
            #disconnecting iface signals
            self.disconnectLayerSignals()
            try:self.iface.currentLayerChanged.disconnect(self.acquire)
            except:pass
            for button in self.buttons:
                #connecting the clicked signal
                button.clicked.connect(self.reclassify)
                #changing button behavior
                button.setCheckable(False)            
        
    def createWidgetWithoutTabs(self, formLayout):
        """
        Adjusts the scroll area to receive the buttons directly (not grouped by category)
        formLayout: Layout used to receive all the buttons
        """
        w = QtGui.QWidget()
        w.setLayout(formLayout)
        self.scrollArea.setWidget(w)

    def createWidgetWithTabs(self, formLayout):
        """
        Creates a scroll area for each form layout.
        formLayout: Layout used to receive the buttons in each tab
        """
        scrollArea = QtGui.QScrollArea()
        scrollArea.setWidgetResizable(True)
        scrollArea.setFrameShape(QtGui.QFrame.Shape(0))  # no frame
        w = QtGui.QWidget()
        w.setLayout(formLayout)
        scrollArea.setWidget(w)
        return scrollArea

    def on_filterLineEdit_textChanged(self, text):
        for i in self.buttons:
            if text.lower() in i.text().lower():
                i.show()
            else:
                i.hide()
    
    def createButton(self, button, propertyDict = dict()):
        """
        Creates the buttons according to the user size definition
        button: Button name
        propertyDict: optional dict parameters that may contain other properties to button, such as color, tooltip and custom category
        """

        pushButton = QtGui.QPushButton(button)
        keys = propertyDict.keys()
        styleSheet = ''
        if 'buttonColor' in keys:
            r, g, b, a = propertyDict['buttonColor'].split(',')
            styleSheet += "background-color:rgba({0},{1},{2},{3});".format(r, g, b, a)
        if 'buttonToolTip' in keys:
            pushButton.setToolTip(propertyDict['buttonToolTip'])
        if 'buttonShortcut' in keys:
            keySequence = QKeySequence(propertyDict['buttonShortcut'])
            pushButton.setText('{0} [{1}]'.format(button, keySequence.toString(format = QKeySequence.NativeText)))
            pushButton.setShortcut(keySequence)

        pushButton.clicked.connect(self.reclassify)
        pushButton.toggled.connect(self.acquire)
        if self.size == 0:
            pushButton.setMinimumSize(100, 25)
            styleSheet += 'font-size:12px;'
        elif self.size == 1:            
            pushButton.setMinimumSize(100, 40)
            styleSheet += 'font-size:20px;'
        elif self.size == 2:            
            pushButton.setMinimumSize(100, 80)
            styleSheet += 'font-size:30px;'
        pushButton.setStyleSheet(styleSheet)
        self.buttons.append(pushButton)
        return pushButton        
        
    def createButtons(self, reclassificationDict, createTabs=False):
        """
        Convenience method to create buttons
        createTabs: Indicates if the buttons must be created within tabs
        """
        self.buttons = []
        widget = self.scrollArea.takeWidget()
        if createTabs:
            self.createButtonsWithTabs(reclassificationDict)
        else:
            self.createButtonsWithoutTabs(reclassificationDict)
        self.turnButtonsOn(self.newFeatureRadioButton.isChecked())
            
    def createButtonsWithoutTabs(self, reclassificationDict):
        """
        Specific method to create buttons without tabs
        reclassificationDict: dictionary used to create the buttons
        """
        formLayout = QtGui.QFormLayout()
        self.createWidgetWithoutTabs(formLayout)
        sortedButtonNames = []
        propertyDict = dict()
        for category in reclassificationDict.keys():
            if category in ['version', 'uiParameterJsonDict']:
                continue
            for edgvClass in reclassificationDict[category].keys():
                for button in reclassificationDict[category][edgvClass].keys():
                    item = reclassificationDict[category][edgvClass][button]
                    propertyDict[button] = dict()
                    if isinstance(item, dict):
                        if 'buttonProp' in item.keys():
                            propertyDict[button] = item['buttonProp']
                    sortedButtonNames.append(button)
        sortedButtonNames.sort()
        for button in sortedButtonNames:       
            pushButton = self.createButton(button, propertyDict = propertyDict[button])
            formLayout.addRow(pushButton)

    def createButtonsWithTabs(self, reclassificationDict):
        """
        Specific method to create buttons with tabs
        reclassificationDict: dictionary used to create the buttons
        """
        gridLayout = QtGui.QGridLayout()
        tabWidget = QtGui.QTabWidget()
        tabWidget.setTabPosition(QtGui.QTabWidget.West)
        gridLayout.addWidget(tabWidget)
        self.scrollArea.setWidget(tabWidget)
        propertyDict = dict()
        for category in reclassificationDict.keys():
            if category in ['version', 'uiParameterJsonDict']:
                continue
            sortedButtonNames = []
            formLayout = QtGui.QFormLayout()
            scrollArea = self.createWidgetWithTabs(formLayout)
            tabWidget.addTab(scrollArea, category)
            for edgvClass in reclassificationDict[category].keys():
                for button in reclassificationDict[category][edgvClass].keys():
                    item = reclassificationDict[category][edgvClass][button]
                    propertyDict[button] = dict()
                    if isinstance(item, dict):
                        if 'buttonProp' in item.keys():
                            propertyDict[button] = item['buttonProp']
                    sortedButtonNames.append(button)
            sortedButtonNames.sort()
            for button in sortedButtonNames:       
                pushButton = self.createButton(button, propertyDict = propertyDict[button])
                formLayout.addRow(pushButton)
                    
    def loadLayer(self, layer):
        """
        Loads the layer used in the actual reclassification
        layer: Layer name
        """
        try:
            return self.layerLoader.load([layer], uniqueLoad=True)[layer]
        except Exception as e:
            QtGui.QMessageBox.critical(self, self.tr('Error!'), self.tr('Could not load the selected classes!\n')+':'.join(e.args))
            
    def checkConditions(self):
        """
        Check the conditions to see if the tool can be used
        """
        if not self.widget.abstractDb:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Please, select a database.'))
            return False
        
        try:
            version = self.widget.abstractDb.getDatabaseVersion()
        except Exception as e:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Problem obtaining database version! Please, check log for details.'))
            QgsMessageLog.logMessage(':'.join(e.args), "DSG Tools Plugin", QgsMessageLog.CRITICAL)
            return False

        if 'version' not in self.reclassificationDict.keys():
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('File not formated propperly.'))
            return False
            
        if self.reclassificationDict['version'] != version:
            QtGui.QMessageBox.critical(self, self.tr('Critical!'), self.tr('Database version does not match the field toolbox version.'))
            return False
        return True
    
    def getLayerFromButton(self, buttonText):
        """
        Gets the correct layer to be used in the tool
        """
        #edgvClass found in the dictionary (this is made using the sqlite seed)
        button = buttonText.split(' [')[0]
        (category, edgvClass) = self.findReclassificationClass(button)
        
        driverName = self.widget.abstractDb.getType()
        if driverName == "QSQLITE":
            #reclassification layer name
            reclassificationClass = '_'.join(edgvClass.split('_')[1::])
        if driverName == "QPSQL":
            #reclassification layer name
            reclassificationClass = '_'.join(edgvClass.split('.')[1::])
            
        #getting the QgsVectorLayer to perform the reclassification
        reclassificationLayer = self.loadLayer(reclassificationClass)
        
        if reclassificationLayer:
            self.iface.setActiveLayer(reclassificationLayer)
            #entering in editing mode
            if not reclassificationLayer.isEditable():
                reclassificationLayer.startEditing()
            lyrAttributes = [i.name() for i in reclassificationLayer.pendingFields()]
            for attr in self.reclassificationDict[category][edgvClass][button].keys():
                if attr == 'buttonProp':
                    continue
                candidateDict = self.reclassificationDict[category][edgvClass][button][attr]
                if isinstance(candidateDict, dict):
                    if candidateDict['isEditable'] == '0' and attr in lyrAttributes:
                        attrIdx = lyrAttributes.index(attr)
                        reclassificationLayer.setFieldEditable(attrIdx, False)

        return (reclassificationLayer, category, edgvClass)
    
    @pyqtSlot(int)
    def setAttributesFromButton(self, featureId):
        """
        Sets the attributes for the newly added feature
        featureId: added feature
        """
        layer = self.sender()
        if isinstance(layer, QgsVectorLayer):
            layer.beginEditCommand(self.tr('DsgTools reclassification'))
            self.addedFeatures.append(featureId)

    def updateAttributesAfterAdding(self):
        """
        Updates feature attributes according to the button configuration
        :return:
        """
        layer = self.sender()
        while self.addedFeatures:
            featureId = self.addedFeatures.pop()
            #begining the edit command
            # layer.beginEditCommand(self.tr("DSG Tools reclassification tool: adjusting feature's attributes"))
            #accessing added features
            editBuffer = layer.editBuffer()
            features = editBuffer.addedFeatures()
            for key in features.keys():
                #just checking the newly added feature, the other I don't care
                if key == featureId:
                    feature = features[key]
                    #setting the attributes using the reclassification dictionary
                    self.setFeatureAttributes(feature, editBuffer)
            layer.endEditCommand()

    def setFeatureAttributes(self, newFeature, editBuffer=None, oldFeat = None):
        """
        Changes attribute values according to the reclassification dict using the edit buffer
        newFeature: newly added
        editBuffer: layer edit buffer
        """
        #setting the attributes using the reclassification dictionary
        for attribute in self.reclassificationDict[self.category][self.edgvClass][self.buttonName].keys():
            if attribute == 'buttonProp':
                continue
            idx = newFeature.fieldNameIndex(attribute)
            #value to be changed
            reclass = self.reclassificationDict[self.category][self.edgvClass][self.buttonName][attribute]
            if isinstance(reclass, dict):
                value = reclass['value']
                if reclass['isIgnored'] == '1': #ignore clause
                    if oldFeat:
                        value = oldFeat[attribute]
            else:
                value = reclass
            if value == '':
                continue
            #actual attribute change
            if editBuffer:
                #this way we are working with the edit buffer
                editBuffer.changeAttributeValue(newFeature.id(), idx, value)
            else:
                #this way are working with selected features and inserting a new one in the layer
                newFeature.setAttribute(idx, value)
                
        if not editBuffer:
            # we should return when under the normal behavior
            return newFeature
        
    def disconnectLayerSignals(self):
        """
        Disconnecting the signals from the previous layer
        """
        if self.prevLayer:
            try:
                self.prevLayer.featureAdded.disconnect(self.setAttributesFromButton)
                self.prevLayer.editCommandEnded.disconnect(self.updateAttributesAfterAdding)
                self.prevLayer.editFormConfig().setSuppress(QgsEditFormConfig.SuppressOff)
            except:
                pass

    @pyqtSlot(bool)
    def acquire(self, pressed):
        """
        Performs the actual reclassification, moving the geometry to the correct layer along with the specified attributes.
        The difference here is the use of real time editing to make the reclassification
        """
        if pressed:
            if not self.checkConditions():
                return
            
            #getting the object that sent the signal
            sender = self.sender()
            
            #if the sender is the iface object, this means that the user made the click and changed the current layer
            #when this happens we should untoggle all buttons
            if isinstance(sender, QgisInterface):
                #checking if another button is checked
                for button in self.buttons:
                    button.setChecked(False)
                #return and do nothing else
                return

            #button that sent the signal
            self.buttonName = sender.text().split(' [')[0]
    
            #checking if another button is checked
            for button in self.buttons:
                if button.text().split(' [')[0] != self.buttonName and button.isChecked():
                    button.setChecked(False)
                    
            #disconnecting the previous layer
            self.disconnectLayerSignals()
    
            (reclassificationLayer, self.category, self.edgvClass) = self.getLayerFromButton(self.buttonName)

            #suppressing the form dialog
            if 'buttonProp' in self.reclassificationDict[self.category][self.edgvClass][self.buttonName].keys() and \
                'openForm' in self.reclassificationDict[self.category][self.edgvClass][self.buttonName]['buttonProp'].keys():
                reclassificationLayer.editFormConfig().setSuppress(QgsEditFormConfig.SuppressOff)
            else:
                reclassificationLayer.editFormConfig().setSuppress(QgsEditFormConfig.SuppressOn)
            #connecting addedFeature signal
            reclassificationLayer.featureAdded.connect(self.setAttributesFromButton)
            reclassificationLayer.editCommandEnded.connect(self.updateAttributesAfterAdding)
            #triggering the add feature tool
            self.iface.actionAddFeature().trigger()            
            #setting the previous layer             
            self.prevLayer = reclassificationLayer        
        else:
            #disconnecting the previous layer
            self.disconnectLayerSignals()
            
    @pyqtSlot()
    def reclassify(self):
        """
        Performs the actual reclassification, moving the geometry to the correct layer along with the specified attributes
        """
        if not self.checkConditions():
            return
        
        somethingMade = False
        reclassifiedFeatures = 0
        
        #button that sent the signal
        self.buttonName = self.sender().text().split(' [')[0]
        (reclassificationLayer, self.category, self.edgvClass) = self.getLayerFromButton(self.buttonName)
        geomType = reclassificationLayer.geometryType()
        hasMValues =  QgsWKBTypes.hasM(int(reclassificationLayer.wkbType()))    #generic check (not every database is implemented as ours)
        hasZValues =  QgsWKBTypes.hasZ(int(reclassificationLayer.wkbType()))    #
        isMulti = QgsWKBTypes.isMultiType(int(reclassificationLayer.wkbType())) #
        mapLayers = self.iface.mapCanvas().layers()
        #we need to get the authid that thefines the ref system of destination layer
        crsSrc = QgsCoordinateReferenceSystem(reclassificationLayer.crs().authid())
        deleteList = []
        for mapLayer in mapLayers:
            if mapLayer.type() != QgsMapLayer.VectorLayer:
                continue
            
            #iterating over selected features
            featList = []
            mapLayerCrs = mapLayer.crs()
            #creating a coordinate transformer (mapLayerCrs to crsSrc)
            coordinateTransformer = QgsCoordinateTransform(mapLayerCrs, crsSrc)
            for feature in mapLayer.selectedFeatures():
                geomList = []
                geom = feature.geometry()
                if geom.type() != geomType:
                    continue
                if 'geometry' in dir(geom):
                    if not hasMValues:
                        geom.geometry().dropMValue()
                    if not hasZValues:
                        geom.geometry().dropZValue()
                if isMulti and not geom.isMultipart():
                    geom.convertToMultiType()
                    geomList.append(geom)
                if not isMulti and geom.isMultipart():
                    #deaggregate here
                    parts = geom.asGeometryCollection()
                    for part in parts:
                        part.convertToSingleType()
                        geomList.append(part)
                else:
                    geomList.append(geom)
                for newGeom in geomList:
                    #creating a new feature according to the reclassification layer
                    newFeature = QgsFeature(reclassificationLayer.pendingFields())
                    #transforming the geometry to the correct crs
                    geom.transform(coordinateTransformer)
                    #setting the geometry
                    newFeature.setGeometry(newGeom)
                    #setting the attributes using the reclassification dictionary
                    newFeature = self.setFeatureAttributes(newFeature, oldFeat = feature)
                    #adding the newly created feature to the addition list
                    featList.append(newFeature)
                    somethingMade = True
                    deleteList.append({'originalLyr':mapLayer,'featid':feature.id()})
            #actual feature insertion
            reclassificationLayer.addFeatures(featList, False)
            reclassifiedFeatures += len(featList)
        
        for item in deleteList:
            item['originalLyr'].startEditing()
            item['originalLyr'].deleteFeature(item['featid'])
        
        if somethingMade:
            self.iface.messageBar().pushMessage(self.tr('Information!'), self.tr('{} features reclassified with success!').format(reclassifiedFeatures), level=QgsMessageBar.INFO, duration=3)

    def findReclassificationClass(self, button):
        """
        Finds the reclassification class according to the button
        button: Button clicked by the user to perform the reclassification
        """
        for category in self.reclassificationDict.keys():
            if category == 'version' or category == 'uiParameterJsonDict':
                continue
            for edgvClass in self.reclassificationDict[category].keys():
                for buttonName in self.reclassificationDict[category][edgvClass].keys():
                    if button == buttonName:
                        #returning the desired edgvClass
                        return (category, edgvClass)
        return ()
    
    def populateConfigFromDb(self):
        """
        Populates configFromDbComboBox with config from public.field_toolbox_config
        """
        driverName = self.widget.abstractDb.getType()
        self.configFromDbComboBox.clear()
        self.configFromDbComboBox.addItem(self.tr('Select Stored Config (optional)'))
        if driverName == 'QPSQL':
            self.configFromDbComboBox.setEnabled(True)
            propertyDict = self.widget.abstractDb.getPropertyDict('FieldToolBoxConfig')
            dbVersion = self.widget.abstractDb.getDatabaseVersion()
            if dbVersion in propertyDict.keys():
                self.configFromDbDict = propertyDict[dbVersion]
            nameList = self.configFromDbDict.keys()
            nameList.sort()
            for name in nameList:
                self.configFromDbComboBox.addItem(name)
        else:
            self.configFromDbComboBox.setEnabled(False)
            self.configFromDbDict = dict()
    
    @pyqtSlot(int)
    def on_configFromDbComboBox_currentIndexChanged(self, idx):
        if idx != 0 and idx != -1:
            self.reclassificationDict = self.configFromDbDict[self.configFromDbComboBox.currentText()]
            self.populateWindow()
        if idx == 0:
            self.reclassificationDict = {}
            self.createButtonsWithTabs(self.reclassificationDict)