|
| 1 | +# -*- coding: utf-8 -*- |
| 2 | +from PyQt4.QtCore import * |
| 3 | +from PyQt4.QtGui import * |
| 4 | +from qgis.core import * |
| 5 | +from ui_frmVisual import Ui_Dialog |
| 6 | +import ftools_utils |
| 7 | +import math |
| 8 | + |
| 9 | +class ValidateDialog( QDialog, Ui_Dialog ): |
| 10 | + def __init__(self, iface): |
| 11 | + QDialog.__init__(self) |
| 12 | + self.iface = iface |
| 13 | + self.setupUi(self) |
| 14 | + self.setModal(False) # we want to be able to interact with the featuresmc.extent().width() |
| 15 | + # adjust user interface |
| 16 | + self.setWindowTitle( self.tr( "Check geometry validity" ) ) |
| 17 | + self.cmbField.setVisible( False ) |
| 18 | + self.label.setVisible( False ) |
| 19 | + self.useSelected.setVisible( True ) |
| 20 | + self.label_2.setText( self.tr( "Geometry errors" ) ) |
| 21 | + self.label_4.setText( self.tr( "Total encountered errors" ) ) |
| 22 | + self.partProgressBar.setVisible( False ) |
| 23 | + self.tblUnique.setSelectionMode(QAbstractItemView.SingleSelection) |
| 24 | + self.tblUnique.setSelectionBehavior(QAbstractItemView.SelectRows) |
| 25 | + # populate list of available layers |
| 26 | + myList = ftools_utils.getLayerNames( [ QGis.Point, QGis.Line, QGis.Polygon ] ) |
| 27 | + self.connect(self.tblUnique, SIGNAL("currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)" ), |
| 28 | + self.zoomToError) |
| 29 | + self.inShape.addItems( myList ) |
| 30 | + self.cancel_close = self.buttonBox_2.button(QDialogButtonBox.Close) |
| 31 | + self.buttonOk = self.buttonBox_2.button(QDialogButtonBox.Ok) |
| 32 | + self.progressBar.setValue(0) |
| 33 | + self.storedScale = self.iface.mapCanvas().scale() |
| 34 | + |
| 35 | + def keyPressEvent( self, e ): |
| 36 | + if ( e.modifiers() == Qt.ControlModifier or \ |
| 37 | + e.modifiers() == Qt.MetaModifier ) and \ |
| 38 | + e.key() == Qt.Key_C: |
| 39 | + #selection = self.tblUnique.selectedItems() |
| 40 | + items = QString() |
| 41 | + for row in range( self.tblUnique.rowCount() ): |
| 42 | + items.append( self.tblUnique.item( row, 0 ).text() |
| 43 | + + "," + self.tblUnique.item( row, 1 ).text() + "\n" ) |
| 44 | + if not items.isEmpty(): |
| 45 | + clip_board = QApplication.clipboard() |
| 46 | + clip_board.setText( items ) |
| 47 | + else: |
| 48 | + QDialog.keyPressEvent( self, e ) |
| 49 | + |
| 50 | + def accept( self ): |
| 51 | + if self.inShape.currentText() == "": |
| 52 | + QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input vector layer" ) ) |
| 53 | + elif self.cmbField.isVisible() and self.cmbField.currentText() == "": |
| 54 | + QMessageBox.information( self, self.tr("Error!"), self.tr( "Please specify input field" ) ) |
| 55 | + else: |
| 56 | + self.validate( self.inShape.currentText(), self.useSelected.checkState() ) |
| 57 | + |
| 58 | + def zoomToError(self, curr, prev): |
| 59 | + if curr is None: |
| 60 | + return |
| 61 | + row = curr.row() # if we clicked in the first column, we want the second |
| 62 | + item = self.tblUnique.item(row, 1) |
| 63 | + if not item.data(Qt.UserRole) is None: |
| 64 | + mc = self.iface.mapCanvas() |
| 65 | + x = item.data(Qt.UserRole).toPyObject().x() |
| 66 | + y = item.data(Qt.UserRole).toPyObject().y() |
| 67 | + mc.zoomToPreviousExtent() |
| 68 | + scale = mc.scale() |
| 69 | + rect = QgsRectangle(float(x)-(4.0/scale),float(y)-(4.0/scale), |
| 70 | + float(x)+(4.0/scale),float(y)+(4.0/scale)) |
| 71 | + # Set the extent to our new rectangle |
| 72 | + mc.setExtent(rect) |
| 73 | + # Refresh the map |
| 74 | + mc.refresh() |
| 75 | + |
| 76 | + def validate( self, myLayer, mySelection ): |
| 77 | + vlayer = ftools_utils.getVectorLayerByName( myLayer ) |
| 78 | + self.tblUnique.clearContents() |
| 79 | + self.tblUnique.setRowCount( 0 ) |
| 80 | + self.lstCount.clear() |
| 81 | + self.buttonOk.setEnabled( False ) |
| 82 | + self.testThread = validateThread( self.iface.mainWindow(), self, vlayer, mySelection ) |
| 83 | + QObject.connect( self.testThread, SIGNAL( "runFinished(PyQt_PyObject)" ), self.runFinishedFromThread ) |
| 84 | + QObject.connect( self.testThread, SIGNAL( "runStatus(PyQt_PyObject)" ), self.runStatusFromThread ) |
| 85 | + QObject.connect( self.testThread, SIGNAL( "runRange(PyQt_PyObject)" ), self.runRangeFromThread ) |
| 86 | + self.cancel_close.setText( self.tr("Cancel") ) |
| 87 | + QObject.connect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread ) |
| 88 | + QApplication.setOverrideCursor( Qt.WaitCursor ) |
| 89 | + self.testThread.start() |
| 90 | + return True |
| 91 | + |
| 92 | + def cancelThread( self ): |
| 93 | + self.testThread.stop() |
| 94 | + QApplication.restoreOverrideCursor() |
| 95 | + self.buttonOk.setEnabled( True ) |
| 96 | + |
| 97 | + def runFinishedFromThread( self, output ): |
| 98 | + self.testThread.stop() |
| 99 | + QApplication.restoreOverrideCursor() |
| 100 | + self.buttonOk.setEnabled( True ) |
| 101 | + self.tblUnique.setColumnCount( 2 ) |
| 102 | + count = 0 |
| 103 | + for rec in output: |
| 104 | + if len(rec[1]) < 1: |
| 105 | + continue |
| 106 | + where = None |
| 107 | + for err in rec[1]: # for each error we find |
| 108 | + self.tblUnique.insertRow(count) |
| 109 | + fidItem = QTableWidgetItem( str(rec[0]) ) |
| 110 | + self.tblUnique.setItem( count, 0, fidItem ) |
| 111 | + if err.hasWhere(): # if there is a location associated with the error |
| 112 | + where = err.where() |
| 113 | + message = err.what() |
| 114 | + errItem = QTableWidgetItem( message ) |
| 115 | + errItem.setData(Qt.UserRole, QVariant(where)) |
| 116 | + self.tblUnique.setItem( count, 1, errItem ) |
| 117 | + count += 1 |
| 118 | + self.tblUnique.setHorizontalHeaderLabels( [ self.tr("Feature"), self.tr("Error(s)") ] ) |
| 119 | + self.tblUnique.horizontalHeader().setResizeMode( 0, QHeaderView.ResizeToContents ) |
| 120 | + self.tblUnique.horizontalHeader().show() |
| 121 | + self.tblUnique.horizontalHeader().setResizeMode( 1, QHeaderView.Stretch ) |
| 122 | + self.tblUnique.resizeRowsToContents() |
| 123 | + self.lstCount.insert(str(count)) |
| 124 | + self.cancel_close.setText( "Close" ) |
| 125 | + QObject.disconnect( self.cancel_close, SIGNAL( "clicked()" ), self.cancelThread ) |
| 126 | + return True |
| 127 | + |
| 128 | + def runStatusFromThread( self, status ): |
| 129 | + self.progressBar.setValue( status ) |
| 130 | + |
| 131 | + def runRangeFromThread( self, range_vals ): |
| 132 | + self.progressBar.setRange( range_vals[ 0 ], range_vals[ 1 ] ) |
| 133 | + |
| 134 | +class validateThread( QThread ): |
| 135 | + def __init__( self, parentThread, parentObject, vlayer, mySelection ): |
| 136 | + QThread.__init__( self, parentThread ) |
| 137 | + self.parent = parentObject |
| 138 | + self.running = False |
| 139 | + self.vlayer = vlayer |
| 140 | + self.mySelection = mySelection |
| 141 | + |
| 142 | + def run( self ): |
| 143 | + self.running = True |
| 144 | + output = self.check_geometry( self.vlayer ) |
| 145 | + self.emit( SIGNAL( "runFinished(PyQt_PyObject)" ), output ) |
| 146 | + self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 ) |
| 147 | + |
| 148 | + def stop(self): |
| 149 | + self.running = False |
| 150 | + |
| 151 | + def check_geometry( self, vlayer ): |
| 152 | + lstErrors = [] |
| 153 | + if self.mySelection: |
| 154 | + layer = vlayer.selectedFeatures() |
| 155 | + nFeat = len(layer) |
| 156 | + else: |
| 157 | + layer = vlayer |
| 158 | + layer.select([]) # select all features, and ignore attributes |
| 159 | + nFeat = layer.featureCount() |
| 160 | + nElement = 0 |
| 161 | + if nFeat > 0: |
| 162 | + self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), 0 ) |
| 163 | + self.emit( SIGNAL( "runRange(PyQt_PyObject)" ), ( 0, nFeat ) ) |
| 164 | + for feat in layer: |
| 165 | + if not self.running: |
| 166 | + return list() |
| 167 | + geom = QgsGeometry(feat.geometry()) # ger reference to geometry |
| 168 | + self.emit(SIGNAL("runStatus(PyQt_PyObject)"), nElement) |
| 169 | + nElement += 1 |
| 170 | + lstErrors.append((feat.id(), list(geom.validateGeometry()))) |
| 171 | + self.emit( SIGNAL( "runStatus(PyQt_PyObject)" ), nFeat ) |
| 172 | + return lstErrors |
0 commit comments