From 24e259bdb391e15cde341952e96eec503df2a271 Mon Sep 17 00:00:00 2001 From: Alexander Bruy Date: Mon, 26 Nov 2012 10:39:53 +0200 Subject: [PATCH] [FEATURE] Possibility to save error messages as shapefile from check geometry validity tool. Contributed by Salvatore Larosa --- i18n/qgis_it.ts | 5 + python/plugins/fTools/tools/doValidate.py | 154 ++++++++++++++++------ python/plugins/fTools/tools/doVisual.py | 13 +- python/plugins/fTools/tools/frmVisual.ui | 135 ++++++++++++------- 4 files changed, 222 insertions(+), 85 deletions(-) diff --git a/i18n/qgis_it.ts b/i18n/qgis_it.ts index 52fb096600c..5ce6ed2fe7e 100644 --- a/i18n/qgis_it.ts +++ b/i18n/qgis_it.ts @@ -830,6 +830,11 @@ sono stati ridotti a %2 dopo la semplificazione Press Ctrl+C to copy results to the clipboard Premi Ctrl+C per copiare i risultati negli appunti + + + Save errors location + Salva posizione errori + Sum line lengths Somma lunghezza linee diff --git a/python/plugins/fTools/tools/doValidate.py b/python/plugins/fTools/tools/doValidate.py index 2928ba1131c..cdcdf5c7edf 100644 --- a/python/plugins/fTools/tools/doValidate.py +++ b/python/plugins/fTools/tools/doValidate.py @@ -60,7 +60,7 @@ class MarkerErrorGeometry(): def reset(self): if not self.__marker is None: - self.__canvas.scene().removeItem(self.__marker) + self.__canvas.scene().removeItem(self.__marker) del self.__marker self.__marker = None @@ -83,9 +83,10 @@ class ValidateDialog( QDialog, Ui_Dialog ): self.tblUnique.setSelectionBehavior(QAbstractItemView.SelectRows) # populate list of available layers myList = ftools_utils.getLayerNames( [ QGis.Point, QGis.Line, QGis.Polygon ] ) - self.connect(self.tblUnique, SIGNAL("currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)" ), + self.connect(self.tblUnique, SIGNAL("currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)" ), self.zoomToError) self.inShape.addItems( myList ) + self.buttonBox_2.setOrientation(Qt.Horizontal) self.cancel_close = self.buttonBox_2.button(QDialogButtonBox.Close) self.buttonOk = self.buttonBox_2.button(QDialogButtonBox.Ok) self.progressBar.setValue(0) @@ -96,12 +97,16 @@ class ValidateDialog( QDialog, Ui_Dialog ): settings = QSettings() self.restoreGeometry( settings.value("/fTools/ValidateDialog/geometry").toByteArray() ) + QObject.connect( self.browseShpError, SIGNAL( "clicked()" ), self.outFile ) + QObject.connect( self.ckBoxShpError, SIGNAL( "stateChanged( int )" ), self.updateGui ) + self.updateGui() + def closeEvent(self, e): settings = QSettings() settings.setValue( "/fTools/ValidateDialog/geometry", QVariant(self.saveGeometry()) ) QDialog.closeEvent(self, e) del self.marker - + def keyPressEvent( self, e ): if ( e.modifiers() == Qt.ControlModifier or \ e.modifiers() == Qt.MetaModifier ) and \ @@ -122,10 +127,37 @@ class ValidateDialog( QDialog, Ui_Dialog ): QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input vector layer" ) ) elif self.cmbField.isVisible() and self.cmbField.currentText() == "": QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input field" ) ) + elif self.ckBoxShpError.isChecked() and self.lineEditShpError.text() == "": + QMessageBox.information( self, self.tr( "Error!" ), self.tr( "Please specify output shapefile" ) ) else: self.vlayer = ftools_utils.getVectorLayerByName( self.inShape.currentText() ) self.validate( self.useSelected.checkState() ) - + + def updateGui( self ): + if self.ckBoxShpError.isChecked(): + self.lineEditShpError.setEnabled( True ) + self.browseShpError.setEnabled( True ) + self.tblUnique.setEnabled( False ) + self.lstCount.setEnabled( False ) + self.label_2.setEnabled( False ) + self.label_4.setEnabled( False ) + self.label_5.setEnabled( False ) + else: + self.lineEditShpError.setEnabled( False ) + self.browseShpError.setEnabled( False ) + self.tblUnique.setEnabled( True ) + self.lstCount.setEnabled( True ) + self.label_2.setEnabled( True ) + self.label_4.setEnabled( True ) + self.label_5.setEnabled( True ) + + def outFile( self ): + self.lineEditShpError.clear() + (self.shapefileName, self.encoding) = ftools_utils.saveDialog( self ) + if self.shapefileName is None or self.encoding is None: + return + self.lineEditShpError.setText( QString( self.shapefileName ) ) + def zoomToError(self, curr, prev): if curr is None: return @@ -162,11 +194,16 @@ class ValidateDialog( QDialog, Ui_Dialog ): mc.refresh() def validate( self, mySelection ): - self.tblUnique.clearContents() - self.tblUnique.setRowCount( 0 ) - self.lstCount.clear() + if not self.ckBoxShpError.isChecked(): + self.tblUnique.clearContents() + self.tblUnique.setRowCount( 0 ) + self.lstCount.clear() + self.shapefileName = None + self.encoding = None + self.buttonOk.setEnabled( False ) - self.testThread = validateThread( self.iface.mainWindow(), self, self.vlayer, mySelection ) + + self.testThread = validateThread( self.iface.mainWindow(), self, self.vlayer, mySelection, self.shapefileName, self.encoding, self.ckBoxShpError.isChecked() ) QObject.connect( self.testThread, SIGNAL( "runFinished(PyQt_PyObject)" ), self.runFinishedFromThread ) QObject.connect( self.testThread, SIGNAL( "runStatus(PyQt_PyObject)" ), self.runStatusFromThread ) QObject.connect( self.testThread, SIGNAL( "runRange(PyQt_PyObject)" ), self.runRangeFromThread ) @@ -180,60 +217,73 @@ class ValidateDialog( QDialog, Ui_Dialog ): # Remove Marker self.marker.reset() QDialog.reject(self) - + def cancelThread( self ): self.testThread.stop() QApplication.restoreOverrideCursor() self.buttonOk.setEnabled( True ) - - def runFinishedFromThread( self, output ): + + def runFinishedFromThread( self, success ): self.testThread.stop() QApplication.restoreOverrideCursor() self.buttonOk.setEnabled( True ) - self.tblUnique.setColumnCount( 2 ) - count = 0 - for rec in output: - if len(rec[1]) < 1: - continue - where = None - for err in rec[1]: # for each error we find - self.tblUnique.insertRow(count) - fidItem = QTableWidgetItem( str(rec[0]) ) - self.tblUnique.setItem( count, 0, fidItem ) - message = err.what() - errItem = QTableWidgetItem( message ) - if err.hasWhere(): # if there is a location associated with the error - errItem.setData(Qt.UserRole, QVariant(err.where())) - self.tblUnique.setItem( count, 1, errItem ) - count += 1 - self.tblUnique.setHorizontalHeaderLabels( [ self.tr("Feature"), self.tr("Error(s)") ] ) - self.tblUnique.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents ) - self.tblUnique.horizontalHeader().show() - self.tblUnique.horizontalHeader().setResizeMode( 1, QHeaderView.Stretch ) - self.tblUnique.resizeRowsToContents() - self.lstCount.insert(str(count)) + if success == "writeShape": + extra = "" + addToTOC = QMessageBox.question( self, self.tr("Geometry"), + self.tr( "Created output shapefile:\n%1\n%2\n\nWould you like to add the new layer to the TOC?" ).arg( unicode( self.shapefileName ) ).arg( extra ), + QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton ) + if addToTOC == QMessageBox.Yes: + if not ftools_utils.addShapeToCanvas( unicode( self.shapefileName ) ): + QMessageBox.warning( self, self.tr( "Geometry"), + self.tr( "Error loading output shapefile:\n%1" ).arg( unicode( self.shapefileName ) ) ) + else: + self.tblUnique.setColumnCount( 2 ) + count = 0 + for rec in success: + if len(rec[1]) < 1: + continue + where = None + for err in rec[1]: # for each error we find + self.tblUnique.insertRow(count) + fidItem = QTableWidgetItem( str(rec[0]) ) + self.tblUnique.setItem( count, 0, fidItem ) + message = err.what() + errItem = QTableWidgetItem( message ) + if err.hasWhere(): # if there is a location associated with the error + errItem.setData(Qt.UserRole, QVariant(err.where())) + self.tblUnique.setItem( count, 1, errItem ) + count += 1 + self.tblUnique.setHorizontalHeaderLabels( [ self.tr("Feature"), self.tr("Error(s)") ] ) + self.tblUnique.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents ) + self.tblUnique.horizontalHeader().show() + self.tblUnique.horizontalHeader().setResizeMode( 1, QHeaderView.Stretch ) + self.tblUnique.resizeRowsToContents() + self.lstCount.insert(str(count)) self.cancel_close.setText( "Close" ) QObject.disconnect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread ) return True - + def runStatusFromThread( self, status ): self.progressBar.setValue( status ) - + def runRangeFromThread( self, range_vals ): self.progressBar.setRange( range_vals[ 0 ], range_vals[ 1 ] ) class validateThread( QThread ): - def __init__( self, parentThread, parentObject, vlayer, mySelection ): + def __init__( self, parentThread, parentObject, vlayer, mySelection, myName, myEncoding, myNewShape ): QThread.__init__( self, parentThread ) self.parent = parentObject self.running = False self.vlayer = vlayer self.mySelection = mySelection + self.myName = myName + self.myEncoding = myEncoding + self.writeShape = myNewShape def run( self ): self.running = True - output = self.check_geometry( self.vlayer ) - self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), output ) + success = self.check_geometry( self.vlayer ) + self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), success ) def stop(self): self.running = False @@ -265,4 +315,30 @@ class validateThread( QThread ): if not geom.isGeosEmpty(): lstErrors.append((feat.id(), list(geom.validateGeometry()))) self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nFeat ) - return lstErrors + + if self.writeShape: + fields = { 0 : QgsField( "FEAT_ID", QVariant.Int ), + 1 : QgsField( "ERROR", QVariant.String ) } + writer = QgsVectorFileWriter( self.myName, self.myEncoding, fields, + QGis.WKBPoint, vlayer.crs() ) + for rec in lstErrors: + if len(rec[1]) < 1: + continue + for err in rec[1]: + fidItem = str(rec[0]) + message = err.what() + if err.hasWhere(): + locErr = err.where() + xP = locErr.x() + yP = locErr.y() + myPoint = QgsPoint( xP, yP ) + geometry = QgsGeometry().fromPoint( myPoint ) + ft = QgsFeature() + ft.setGeometry( geometry ) + ft.setAttributeMap( { 0 : QVariant( fidItem ), + 1 : QVariant( message ) } ) + writer.addFeature( ft ) + del writer + return "writeShape" + else: + return lstErrors diff --git a/python/plugins/fTools/tools/doVisual.py b/python/plugins/fTools/tools/doVisual.py index a26fdfe2222..6e17e61617f 100644 --- a/python/plugins/fTools/tools/doVisual.py +++ b/python/plugins/fTools/tools/doVisual.py @@ -41,6 +41,15 @@ class VisualDialog( QDialog, Ui_Dialog ): self.iface = iface self.setupUi( self ) self.myFunction = function + + ## Set object visibility to False if tool is not Check geometry + self.ckBoxShpError.hide() + self.browseShpError.hide() + self.lineEditShpError.hide() + self.label_6.hide() + self.line.hide() + self.buttonBox_2.setOrientation(Qt.Horizontal) + if self.myFunction == 2 or self.myFunction == 3: QObject.connect( self.inShape, SIGNAL( "currentIndexChanged(QString)" ), self.update ) self.manageGui() @@ -289,7 +298,7 @@ class visualThread( QThread ): self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nElement ) else: # there is no selection, process the whole layer nFeat = vprovider.featureCount() - if nFeat > 0: + if nFeat > 0: self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 ) self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) ) vprovider.select( allAttrs ) @@ -355,7 +364,7 @@ class visualThread( QThread ): else: # there is no selection, process the whole layer nFeat = vprovider.featureCount() uniqueVal = ftools_utils.getUniqueValuesCount( vlayer, index, False ) - if nFeat > 0: + if nFeat > 0: self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 ) self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) ) vprovider.select( allAttrs ) diff --git a/python/plugins/fTools/tools/frmVisual.ui b/python/plugins/fTools/tools/frmVisual.ui index fdbb8845efb..3d2b21c172f 100644 --- a/python/plugins/fTools/tools/frmVisual.ui +++ b/python/plugins/fTools/tools/frmVisual.ui @@ -9,8 +9,8 @@ 0 0 - 404 - 481 + 370 + 484 @@ -20,7 +20,7 @@ true - + @@ -34,18 +34,7 @@ - - - - - - Use only selected features - - - - - - + @@ -59,7 +48,7 @@ - + @@ -107,7 +96,7 @@ - + @@ -125,40 +114,98 @@ - - - - 24 - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - QDialogButtonBox::Close|QDialogButtonBox::Ok - - - - - - - 24 - - - + + + Save errors location + + + + Press Ctrl+C to copy results to the clipboard + + + + + + true + + + + + + + Browse + + + + + + + + + Output point shapefile + + + + + + + + + Use only selected features + + + + + + + + + + + + + 24 + + + + + + + 24 + + + Qt::AlignCenter + + + + + + + + + Qt::Vertical + + + QDialogButtonBox::Close|QDialogButtonBox::Ok + + + + + + + + + Qt::Horizontal + + +