def __init__(self, iface): QtGui.QDialog.__init__(self) self.iface = iface # Set up the user interface from Designer. self.ui = Ui_DissolveWithStats() self.ui.setupUi(self) # connect changed index signal in comboLayerList self.ui.comboLayerList.currentIndexChanged[int].connect(self.onChangedValueLayer) # connect changed index signal in comboFieldList self.ui.comboFieldList.currentIndexChanged[int].connect(self.onChangedValueField) # connect click on browse button, to display file dialog for output shapefile self.ui.outButton.clicked.connect(self.outFile) # connect OK button to validation function self.ui.buttonBox.accepted.connect(self.validation) # connect Cancel button to reject function self.ui.buttonBox.rejected.connect(self.reject) # to get all the vector layers names to populate combo box comboLayerList legendInterface = self.iface.legendInterface() listLayerName = [i.name() for i in legendInterface.layers() if i.type() == QgsMapLayer.VectorLayer] # add all these layer names to combo box comboLayerList self.ui.comboLayerList.addItems(listLayerName) # populate the field table header listHeaders = ["name", "type", "keep", "stat"] self.ui.tableFields.setHorizontalHeaderLabels(listHeaders) # set column widths for field table self.ui.tableFields.setColumnWidth(0,120) self.ui.tableFields.setColumnWidth(1,80) self.ui.tableFields.setColumnWidth(2,80) self.ui.tableFields.setColumnWidth(3,100) # Run the dialog event loop result = self.exec_() # if Cancel was pressed if result == QtGui.QFileDialog.Rejected: return # If OK was pressed if result == QtGui.QFileDialog.Accepted: # try: # get selected layer in combo box comboLayerList selectedLayerName = self.ui.comboLayerList.currentText() # get selected field in combo box comboFieldList selectedFieldName = self.ui.comboFieldList.currentText() # get fields to keep and stats to calculate listKeep = [] listStats = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append(self.ui.tableFields.cellWidget(row, 2).checkState()) listStats.append(self.ui.tableFields.cellWidget(row,3).currentText()) # get output shape output = self.ui.outShape.text() # run qgis:dissolve algorithm from processing module processing.runalg("qgis:dissolve", selectedLayerName, "false", selectedFieldName, output) # calculate new field values listRes = self.calculateFields(listKeep, listStats, output) # integrates these new values in the output attribute table, and remove fields if necessary self.setAttributes(listRes, listKeep, output) # add layer to map if checkBoxAddFile is checked if self.ui.checkBoxAddFile.checkState() == 2: self.addFile(output)
def __init__(self, iface): QtGui.QDialog.__init__(self) self.iface = iface # Set up the user interface from Designer. self.ui = Ui_DissolveWithStats() self.ui.setupUi(self) # connect changed index signal in comboLayerList self.ui.comboLayerList.currentIndexChanged[int].connect( self.onChangedValueLayer) # connect changed index signal in comboFieldList self.ui.comboFieldList.currentIndexChanged[int].connect( self.onChangedValueField) # connect click on browse button, to display file dialog for output shapefile self.ui.outButton.clicked.connect(self.outFile) # connect OK button to validation function self.ui.buttonBox.accepted.connect(self.validation) # connect Cancel button to reject function self.ui.buttonBox.rejected.connect(self.reject) # to get all the vector layers names to populate combo box comboLayerList legendInterface = self.iface.legendInterface() listLayerName = [ i.name() for i in legendInterface.layers() if i.type() == QgsMapLayer.VectorLayer ] # add all these layer names to combo box comboLayerList self.ui.comboLayerList.addItems(listLayerName) # populate the field table header listHeaders = ["name", "type", "keep", "stat"] self.ui.tableFields.setHorizontalHeaderLabels(listHeaders) # set column widths for field table self.ui.tableFields.setColumnWidth(0, 120) self.ui.tableFields.setColumnWidth(1, 80) self.ui.tableFields.setColumnWidth(2, 80) self.ui.tableFields.setColumnWidth(3, 100) # Run the dialog event loop result = self.exec_() # if Cancel was pressed if result == QtGui.QFileDialog.Rejected: return # If OK was pressed if result == QtGui.QFileDialog.Accepted: # try: # get selected layer in combo box comboLayerList selectedLayerName = self.ui.comboLayerList.currentText() # get selected field in combo box comboFieldList selectedFieldName = self.ui.comboFieldList.currentText() # get fields to keep and stats to calculate listKeep = [] listStats = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append( self.ui.tableFields.cellWidget(row, 2).checkState()) listStats.append( self.ui.tableFields.cellWidget(row, 3).currentText()) # get output shape output = self.ui.outShape.text() # run qgis:dissolve algorithm from processing module processing.runalg("qgis:dissolve", selectedLayerName, "false", selectedFieldName, output) # calculate new field values listRes = self.calculateFields(listKeep, listStats, output) # integrates these new values in the output attribute table, and remove fields if necessary self.setAttributes(listRes, listKeep, output) # add layer to map if checkBoxAddFile is checked if self.ui.checkBoxAddFile.checkState() == 2: self.addFile(output)
class DissolveWithStatsDialog(QtGui.QDialog, Ui_DissolveWithStats): def __init__(self, iface): QtGui.QDialog.__init__(self) self.iface = iface # Set up the user interface from Designer. self.ui = Ui_DissolveWithStats() self.ui.setupUi(self) # connect changed index signal in comboLayerList self.ui.comboLayerList.currentIndexChanged[int].connect(self.onChangedValueLayer) # connect changed index signal in comboFieldList self.ui.comboFieldList.currentIndexChanged[int].connect(self.onChangedValueField) # connect click on browse button, to display file dialog for output shapefile self.ui.outButton.clicked.connect(self.outFile) # connect OK button to validation function self.ui.buttonBox.accepted.connect(self.validation) # connect Cancel button to reject function self.ui.buttonBox.rejected.connect(self.reject) # to get all the vector layers names to populate combo box comboLayerList legendInterface = self.iface.legendInterface() listLayerName = [i.name() for i in legendInterface.layers() if i.type() == QgsMapLayer.VectorLayer] # add all these layer names to combo box comboLayerList self.ui.comboLayerList.addItems(listLayerName) # populate the field table header listHeaders = ["name", "type", "keep", "stat"] self.ui.tableFields.setHorizontalHeaderLabels(listHeaders) # set column widths for field table self.ui.tableFields.setColumnWidth(0,120) self.ui.tableFields.setColumnWidth(1,80) self.ui.tableFields.setColumnWidth(2,80) self.ui.tableFields.setColumnWidth(3,100) # Run the dialog event loop result = self.exec_() # if Cancel was pressed if result == QtGui.QFileDialog.Rejected: return # If OK was pressed if result == QtGui.QFileDialog.Accepted: # try: # get selected layer in combo box comboLayerList selectedLayerName = self.ui.comboLayerList.currentText() # get selected field in combo box comboFieldList selectedFieldName = self.ui.comboFieldList.currentText() # get fields to keep and stats to calculate listKeep = [] listStats = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append(self.ui.tableFields.cellWidget(row, 2).checkState()) listStats.append(self.ui.tableFields.cellWidget(row,3).currentText()) # get output shape output = self.ui.outShape.text() # run qgis:dissolve algorithm from processing module processing.runalg("qgis:dissolve", selectedLayerName, "false", selectedFieldName, output) # calculate new field values listRes = self.calculateFields(listKeep, listStats, output) # integrates these new values in the output attribute table, and remove fields if necessary self.setAttributes(listRes, listKeep, output) # add layer to map if checkBoxAddFile is checked if self.ui.checkBoxAddFile.checkState() == 2: self.addFile(output) # except: # QtGui.QMessageBox.warning(self, 'Oops', 'Sorry, something went wrong', QtGui.QMessageBox.Ok) # check if all the dialog parameters are valid def validation(self): message = '' # get list of kept fields if self.ui.comboLayerList.currentText() != '': listKeep = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append(self.ui.tableFields.cellWidget(row, 2).checkState()) # get selected layer, to test self.ui.outShape.text() if self.ui.comboLayerList.currentText() != '': index = self.ui.comboLayerList.currentIndex() legendInterface = self.iface.legendInterface() listLayers = [layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer] selectedLayer = listLayers[index] outfile = QgsVectorFileWriter(self.ui.outShape.text(), "utf-8", selectedLayer.dataProvider().fields(), selectedLayer.dataProvider().geometryType(), selectedLayer.crs()) # if no layer is selected : if self.ui.comboLayerList.currentText() == '': message = 'No layer selected\nQGIS must have at least one vector layer loaded' # if a layer is selected but doesn't have any field elif self.ui.comboFieldList.currentText() == '': message = 'No Field selected\nThe selected layer must have at least one field' # if layer and dissolve field ok, but no fields are checked to be kept elif 2 not in listKeep: message = 'Please select at least one field to be kept' # if no output is selected: elif self.ui.outShape.text() == '': message = 'No output layer\nPlease click on Browse button to specify output layer' # if error in output shapefile path (permission problem for example) elif (outfile.hasError() != QgsVectorFileWriter.NoError): message = "Sorry, could not create output shapefile" # if output does not end in .shp (can happen if user wrote it directly in the QLineEdit box) elif self.ui.outShape.text()[-4:] not in ['.shp', '.SHP']: self.ui.outShape.setText(self.ui.outShape.text() + '.shp') # if something is wrong : show warning message if message != '': QtGui.QMessageBox.warning(self, 'Information missing or invalid', message, QtGui.QMessageBox.Ok) # if everything is ok : proceed else: self.accept() # if selected value in comboLayerList changes : # actualize the values in comboFieldList and in tableFields def onChangedValueLayer(self, index): # get list of all vector layers in QGIS legendInterface = self.iface.legendInterface() listLayers = [layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer] # get name of selected layer provider = listLayers[index].dataProvider() fields = provider.fields() listFieldNames = [field.name() for field in fields] # clear the combo box comboFieldList self.ui.comboFieldList.clear() # add all these field names to combo box comboFieldList self.ui.comboFieldList.addItems(listFieldNames) # add as many rows in the field table as fields in the shape, minus one self.ui.tableFields.setRowCount(len(fields)) # populate columns in field table for i in range (self.ui.tableFields.rowCount()): # first column : field names nameitem = QtGui.QTableWidgetItem(fields[i].name()) # the names are not editable nameitem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableFields.setItem(i, 0, nameitem) # second column : field types typeitem = QtGui.QTableWidgetItem(fields[i].typeName()) # the types are not editable typeitem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableFields.setItem(i, 1, typeitem) # third column : check box keepcheckbox = QtGui.QCheckBox() keepcheckbox.setCheckState(QtCore.Qt.Checked) keepitem = self.ui.tableFields.setCellWidget(i, 2, keepcheckbox) # fourth column : stat listStat = QtGui.QComboBox() # if field is numeric if fields[i].type() in [QtCore.QVariant.Int, QtCore.QVariant.Double]: listStat.addItems(statNum) # if field is not numeric else: listStat.addItems(statElse) statitem = self.ui.tableFields.setCellWidget(i, 3, listStat) # enable all field lists self.ui.tableFields.cellWidget(i, 3).setEnabled(True) # disable list for first field (since it is selected by default when a new layer is selected) self.ui.tableFields.cellWidget(0, 3).setEnabled(False) # if selected value in comboFieldList changes : # re-enable the stats list for ex-selected value, disable it for selected value def onChangedValueField(self, index): if self.ui.tableFields.cellWidget(index, 3): # enable all field lists for i in range (self.ui.tableFields.rowCount()): self.ui.tableFields.cellWidget(i, 3).setEnabled(True) # disable current field list self.ui.tableFields.cellWidget(index, 3).setEnabled(False) # creation of the output shapefile def outFile(self): # by Carson Farmer 2008 # display file dialog for output shapefile self.ui.outShape.clear() fileDialog = QtGui.QFileDialog() fileDialog.setConfirmOverwrite(False) outName = fileDialog.getSaveFileName(self, "Output Shapefile",".", "Shapefiles (*.shp)") outPath = QtCore.QFileInfo(outName).absoluteFilePath() if not outPath.upper().endswith(".SHP"): outPath = outPath + ".shp" if outName: self.ui.outShape.clear() self.ui.outShape.insert(outPath) # gets the median from a list of numbers def median(self, l): # sorts list, get list length l.sort() z = len(l) # if the list has an uneven number of elements if z%2: return l[z/2] # if the list has an even number of elements else: return (l[(z/2)-1] + l[z/2]) / 2.0 # gets standard deviation from a list of numbers def standard_dev(self, l): mean = sum(l) / len(l) dev = [(x - mean)*(x - mean) for x in l] return math.sqrt(sum(dev) / len(l)) # once the dissolve output layer is created, calculates its new attributes values def calculateFields(self, listKeep, listStats, output): # get selected layer index = self.ui.comboLayerList.currentIndex() legendInterface = self.iface.legendInterface() listLayers = [layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer] selectedLayer = listLayers[index] # iterates over layer features to get attributes as a list of lists # uses the processing method so as to get only selected features if this option is set in the processing options iter = processing.features(selectedLayer) attrs = [feature.attributes() for feature in iter] # get all values of the dissolve field (before processing : with duplicate values) indexDissolveField = self.ui.comboFieldList.currentIndex() valuesDissolveField = [feature[indexDissolveField] for feature in attrs] # get unique values for dissolve field, from output (seems more secure than to get it from valuesDissolveField ?) outputLayer = QgsVectorLayer(output, "name", "ogr") provider = outputLayer.dataProvider() fields = provider.fields() listFieldNames = [field.name() for field in fields] iter = outputLayer.getFeatures() uniqueValuesDissolveField = [feature.attributes()[indexDissolveField] for feature in iter] # initializes list of lists which will contain results (it will have one element per kept field) listRes = [] # trick for dissolve field, if kept if listKeep[indexDissolveField] == 2: listStats [indexDissolveField] = 'First' # for each kept field for i in range(len(listFieldNames)): if listKeep[i] == 2: # creates list which will contain attribute values for current field, one empty element per unique dissolve field value listAttrs = [[] for val in range(len(uniqueValuesDissolveField))] # fill this list with all the current field values corresponding to each dissolve field value valuesField = [feature[i] for feature in attrs] for (x,y) in zip(valuesDissolveField, valuesField): listAttrs[uniqueValuesDissolveField.index(x)].append(y) # removes any NULL values listAttrs = [[x for x in l if x] for l in listAttrs] # for each list in listAttrs, calculates one value according to the chosen stat # if list is empty (can happen if it contained originally only NULL values), return NULL as a result if listStats[i] == "Mean": listAttrs = [sum(y) / len(y) if y else NULL for y in listAttrs] elif listStats[i] == "Sum": listAttrs = [sum(y) if y else NULL for y in listAttrs] elif listStats[i] == "Min": listAttrs = [min(y) if y else NULL for y in listAttrs] elif listStats[i] == "Max": listAttrs = [max(y) if y else NULL for y in listAttrs] elif listStats[i] == "Count": listAttrs = [len(y) if y else NULL for y in listAttrs] elif listStats[i] == "First": listAttrs = [y[0] if y else NULL for y in listAttrs] elif listStats[i] == "Last": listAttrs = [y[-1] if y else NULL for y in listAttrs] elif listStats[i] == "Median": listAttrs = [self.median(y) if y else NULL for y in listAttrs] elif listStats[i] == "Standard deviation": listAttrs = [self.standard_dev(y) if y else NULL for y in listAttrs] elif listStats[i] == "Concatenation": listAttrs = [", ".join(y) if y else NULL for y in listAttrs] elif listStats[i] == "Uniquification": listAttrs = [", ".join(set(y)) if y else NULL for y in listAttrs] # append each field result to listRes listRes.append(listAttrs) return listRes # removes fields from the output which mustn't be kept, and set the other field values def setAttributes(self, listRes, listKeep, output): # get indexes of fields to be deleted listIndexesDel = [i for i in range(len(listKeep)) if listKeep[i] == 0] # get layer, provider and provider capabilities outputLayer = QgsVectorLayer(output, "name", "ogr") provider = outputLayer.dataProvider() caps = provider.capabilities() # delete fields to be deleted if caps & QgsVectorDataProvider.DeleteAttributes: res = provider.deleteAttributes(listIndexesDel) outputLayer.updateFields() # changes other fields attribute values fields = provider.fields() nb_fields = len(fields) outputLayer.startEditing() for fieldIndex in range(nb_fields): for fid in range(len(listRes[0])): outputLayer.changeAttributeValue(fid, fieldIndex, listRes[fieldIndex][fid]) outputLayer.commitChanges() # add output layer to the map def addFile(self, output): layerNameSHP = output.split('/')[-1] layerName = layerNameSHP.split('.')[0] layer = QgsVectorLayer(output, layerName, "ogr") QgsMapLayerRegistry.instance().addMapLayer(layer)
class DissolveWithStatsDialog(QtGui.QDialog, Ui_DissolveWithStats): def __init__(self, iface): QtGui.QDialog.__init__(self) self.iface = iface # Set up the user interface from Designer. self.ui = Ui_DissolveWithStats() self.ui.setupUi(self) # connect changed index signal in comboLayerList self.ui.comboLayerList.currentIndexChanged[int].connect( self.onChangedValueLayer) # connect changed index signal in comboFieldList self.ui.comboFieldList.currentIndexChanged[int].connect( self.onChangedValueField) # connect click on browse button, to display file dialog for output shapefile self.ui.outButton.clicked.connect(self.outFile) # connect OK button to validation function self.ui.buttonBox.accepted.connect(self.validation) # connect Cancel button to reject function self.ui.buttonBox.rejected.connect(self.reject) # to get all the vector layers names to populate combo box comboLayerList legendInterface = self.iface.legendInterface() listLayerName = [ i.name() for i in legendInterface.layers() if i.type() == QgsMapLayer.VectorLayer ] # add all these layer names to combo box comboLayerList self.ui.comboLayerList.addItems(listLayerName) # populate the field table header listHeaders = ["name", "type", "keep", "stat"] self.ui.tableFields.setHorizontalHeaderLabels(listHeaders) # set column widths for field table self.ui.tableFields.setColumnWidth(0, 120) self.ui.tableFields.setColumnWidth(1, 80) self.ui.tableFields.setColumnWidth(2, 80) self.ui.tableFields.setColumnWidth(3, 100) # Run the dialog event loop result = self.exec_() # if Cancel was pressed if result == QtGui.QFileDialog.Rejected: return # If OK was pressed if result == QtGui.QFileDialog.Accepted: # try: # get selected layer in combo box comboLayerList selectedLayerName = self.ui.comboLayerList.currentText() # get selected field in combo box comboFieldList selectedFieldName = self.ui.comboFieldList.currentText() # get fields to keep and stats to calculate listKeep = [] listStats = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append( self.ui.tableFields.cellWidget(row, 2).checkState()) listStats.append( self.ui.tableFields.cellWidget(row, 3).currentText()) # get output shape output = self.ui.outShape.text() # run qgis:dissolve algorithm from processing module processing.runalg("qgis:dissolve", selectedLayerName, "false", selectedFieldName, output) # calculate new field values listRes = self.calculateFields(listKeep, listStats, output) # integrates these new values in the output attribute table, and remove fields if necessary self.setAttributes(listRes, listKeep, output) # add layer to map if checkBoxAddFile is checked if self.ui.checkBoxAddFile.checkState() == 2: self.addFile(output) # except: # QtGui.QMessageBox.warning(self, 'Oops', 'Sorry, something went wrong', QtGui.QMessageBox.Ok) # check if all the dialog parameters are valid def validation(self): message = '' # get list of kept fields if self.ui.comboLayerList.currentText() != '': listKeep = [] for row in range(self.ui.tableFields.rowCount()): listKeep.append( self.ui.tableFields.cellWidget(row, 2).checkState()) # get selected layer, to test self.ui.outShape.text() if self.ui.comboLayerList.currentText() != '': index = self.ui.comboLayerList.currentIndex() legendInterface = self.iface.legendInterface() listLayers = [ layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer ] selectedLayer = listLayers[index] outfile = QgsVectorFileWriter( self.ui.outShape.text(), "utf-8", selectedLayer.dataProvider().fields(), selectedLayer.dataProvider().geometryType(), selectedLayer.crs()) # if no layer is selected : if self.ui.comboLayerList.currentText() == '': message = 'No layer selected\nQGIS must have at least one vector layer loaded' # if a layer is selected but doesn't have any field elif self.ui.comboFieldList.currentText() == '': message = 'No Field selected\nThe selected layer must have at least one field' # if layer and dissolve field ok, but no fields are checked to be kept elif 2 not in listKeep: message = 'Please select at least one field to be kept' # if no output is selected: elif self.ui.outShape.text() == '': message = 'No output layer\nPlease click on Browse button to specify output layer' # if error in output shapefile path (permission problem for example) elif (outfile.hasError() != QgsVectorFileWriter.NoError): message = "Sorry, could not create output shapefile" # if output does not end in .shp (can happen if user wrote it directly in the QLineEdit box) elif self.ui.outShape.text()[-4:] not in ['.shp', '.SHP']: self.ui.outShape.setText(self.ui.outShape.text() + '.shp') # if something is wrong : show warning message if message != '': QtGui.QMessageBox.warning(self, 'Information missing or invalid', message, QtGui.QMessageBox.Ok) # if everything is ok : proceed else: self.accept() # if selected value in comboLayerList changes : # actualize the values in comboFieldList and in tableFields def onChangedValueLayer(self, index): # get list of all vector layers in QGIS legendInterface = self.iface.legendInterface() listLayers = [ layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer ] # get name of selected layer provider = listLayers[index].dataProvider() fields = provider.fields() listFieldNames = [field.name() for field in fields] # clear the combo box comboFieldList self.ui.comboFieldList.clear() # add all these field names to combo box comboFieldList self.ui.comboFieldList.addItems(listFieldNames) # add as many rows in the field table as fields in the shape, minus one self.ui.tableFields.setRowCount(len(fields)) # populate columns in field table for i in range(self.ui.tableFields.rowCount()): # first column : field names nameitem = QtGui.QTableWidgetItem(fields[i].name()) # the names are not editable nameitem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableFields.setItem(i, 0, nameitem) # second column : field types typeitem = QtGui.QTableWidgetItem(fields[i].typeName()) # the types are not editable typeitem.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableFields.setItem(i, 1, typeitem) # third column : check box keepcheckbox = QtGui.QCheckBox() keepcheckbox.setCheckState(QtCore.Qt.Checked) keepitem = self.ui.tableFields.setCellWidget(i, 2, keepcheckbox) # fourth column : stat listStat = QtGui.QComboBox() # if field is numeric (works also for PostGIS data, fix by DelazJ, and for int64 and double, fix by A. Ferraton) if fields[i].type() in [ QtCore.QVariant.Int, QtCore.QVariant.Double, 2, 4, 6 ]: listStat.addItems(statNum) # if field is not numeric else: listStat.addItems(statElse) statitem = self.ui.tableFields.setCellWidget(i, 3, listStat) # enable all field lists self.ui.tableFields.cellWidget(i, 3).setEnabled(True) # disable list for first field (since it is selected by default when a new layer is selected) self.ui.tableFields.cellWidget(0, 3).setEnabled(False) # if selected value in comboFieldList changes : # re-enable the stats list for ex-selected value, disable it for selected value def onChangedValueField(self, index): if self.ui.tableFields.cellWidget(index, 3): # enable all field lists for i in range(self.ui.tableFields.rowCount()): self.ui.tableFields.cellWidget(i, 3).setEnabled(True) # disable current field list self.ui.tableFields.cellWidget(index, 3).setEnabled(False) # creation of the output shapefile def outFile(self): # by Carson Farmer 2008 # display file dialog for output shapefile self.ui.outShape.clear() fileDialog = QtGui.QFileDialog() fileDialog.setConfirmOverwrite(False) outName = fileDialog.getSaveFileName(self, "Output Shapefile", ".", "Shapefiles (*.shp)") outPath = QtCore.QFileInfo(outName).absoluteFilePath() if not outPath.upper().endswith(".SHP"): outPath = outPath + ".shp" if outName: self.ui.outShape.clear() self.ui.outShape.insert(outPath) # gets the median from a list of numbers def median(self, l): # sorts list, get list length l.sort() z = len(l) # if the list has an uneven number of elements if z % 2: return l[z / 2] # if the list has an even number of elements else: return (l[(z / 2) - 1] + l[z / 2]) / 2.0 # gets standard deviation from a list of numbers def standard_dev(self, l): mean = sum(l) / len(l) dev = [(x - mean) * (x - mean) for x in l] return math.sqrt(sum(dev) / len(l)) # once the dissolve output layer is created, calculates its new attributes values def calculateFields(self, listKeep, listStats, output): # get selected layer index = self.ui.comboLayerList.currentIndex() legendInterface = self.iface.legendInterface() listLayers = [ layer for layer in legendInterface.layers() if layer.type() == QgsMapLayer.VectorLayer ] selectedLayer = listLayers[index] # iterates over layer features to get attributes as a list of lists # uses the processing method so as to get only selected features if this option is set in the processing options iter = processing.features(selectedLayer) attrs = [feature.attributes() for feature in iter] # get all values of the dissolve field (before processing : with duplicate values) indexDissolveField = self.ui.comboFieldList.currentIndex() valuesDissolveField = [ feature[indexDissolveField] for feature in attrs ] # get unique values for dissolve field, from output (seems more secure than to get it from valuesDissolveField ?) outputLayer = QgsVectorLayer(output, "name", "ogr") provider = outputLayer.dataProvider() fields = provider.fields() listFieldNames = [field.name() for field in fields] iter = outputLayer.getFeatures() uniqueValuesDissolveField = [ feature.attributes()[indexDissolveField] for feature in iter ] # initializes list of lists which will contain results (it will have one element per kept field) listRes = [] # trick for dissolve field, if kept if listKeep[indexDissolveField] == 2: listStats[indexDissolveField] = 'First' # for each kept field for i in range(len(listFieldNames)): if listKeep[i] == 2: # creates list which will contain attribute values for current field, one empty element per unique dissolve field value listAttrs = [[] for val in range(len(uniqueValuesDissolveField))] # fill this list with all the current field values corresponding to each dissolve field value valuesField = [feature[i] for feature in attrs] for (x, y) in zip(valuesDissolveField, valuesField): listAttrs[uniqueValuesDissolveField.index(x)].append(y) # removes any NULL values listAttrs = [[x for x in l if x] for l in listAttrs] # for each list in listAttrs, calculates one value according to the chosen stat # if list is empty (can happen if it contained originally only NULL values), return NULL as a result if listStats[i] == "Mean": listAttrs = [ sum(y) / len(y) if y else NULL for y in listAttrs ] elif listStats[i] == "Sum": listAttrs = [sum(y) if y else NULL for y in listAttrs] elif listStats[i] == "Min": listAttrs = [min(y) if y else NULL for y in listAttrs] elif listStats[i] == "Max": listAttrs = [max(y) if y else NULL for y in listAttrs] elif listStats[i] == "Count": listAttrs = [len(y) if y else NULL for y in listAttrs] elif listStats[i] == "First": listAttrs = [y[0] if y else NULL for y in listAttrs] elif listStats[i] == "Last": listAttrs = [y[-1] if y else NULL for y in listAttrs] elif listStats[i] == "Median": listAttrs = [ self.median(y) if y else NULL for y in listAttrs ] elif listStats[i] == "Standard deviation": listAttrs = [ self.standard_dev(y) if y else NULL for y in listAttrs ] elif listStats[i] == "Concatenation": listAttrs = [ ", ".join(y) if y else NULL for y in listAttrs ] elif listStats[i] == "Uniquification": listAttrs = [ ", ".join(set(y)) if y else NULL for y in listAttrs ] # append each field result to listRes listRes.append(listAttrs) return listRes # removes fields from the output which mustn't be kept, and set the other field values def setAttributes(self, listRes, listKeep, output): # get indexes of fields to be deleted listIndexesDel = [i for i in range(len(listKeep)) if listKeep[i] == 0] # get layer, provider and provider capabilities outputLayer = QgsVectorLayer(output, "name", "ogr") provider = outputLayer.dataProvider() caps = provider.capabilities() # delete fields to be deleted if caps & QgsVectorDataProvider.DeleteAttributes: res = provider.deleteAttributes(listIndexesDel) outputLayer.updateFields() # changes other fields attribute values fields = provider.fields() nb_fields = len(fields) outputLayer.startEditing() for fieldIndex in range(nb_fields): for fid in range(len(listRes[0])): outputLayer.changeAttributeValue(fid, fieldIndex, listRes[fieldIndex][fid]) outputLayer.commitChanges() # add output layer to the map def addFile(self, output): layerNameSHP = output.split('/')[-1] layerName = layerNameSHP.split('.')[0] layer = QgsVectorLayer(output, layerName, "ogr") QgsMapLayerRegistry.instance().addMapLayer(layer)