run Points in Polygon in separate thread and don't block QGIS main

window (addresses #5162)
This commit is contained in:
Alexander Bruy 2012-05-31 14:21:20 +03:00
parent 34b95d30af
commit 53684682f2
4 changed files with 239 additions and 141 deletions

View File

@ -450,6 +450,7 @@ class fToolsPlugin:
def dopointsPoly(self):
d = doPointsInPolygon.Dialog(self.iface)
d.show()
d.exec_()
def dorandSel(self):

View File

@ -39,11 +39,13 @@ class Dialog(QDialog, Ui_Dialog):
def __init__(self, iface):
QDialog.__init__(self, iface.mainWindow())
self.iface = iface
# Set up the user interface from Designer.
self.setupUi(self)
QObject.connect(self.toolOut, SIGNAL("clicked()"), self.outFile)
self.setWindowTitle(self.tr("Count Points in Polygon"))
self.buttonOk = self.buttonBox_2.button( QDialogButtonBox.Ok )
self.btnOk = self.buttonBox.button( QDialogButtonBox.Ok )
self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
QObject.connect(self.toolOut, SIGNAL("clicked()"), self.outFile)
self.progressBar.setValue(0)
self.populateLayers()
@ -56,93 +58,193 @@ class Dialog(QDialog, Ui_Dialog):
layers = ftools_utils.getLayerNames([QGis.Point])
self.inPoint.addItems(layers)
def accept(self):
self.buttonOk.setEnabled( False )
if self.inPolygon.currentText() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify input polygon vector layer"))
elif self.outShape.text() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify output shapefile"))
elif self.inPoint.currentText() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify input point vector layer"))
elif self.lnField.text() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify output count field"))
else:
inPoly = self.inPolygon.currentText()
inPts = self.inPoint.currentText()
inField = self.lnField.text()
outPath = self.outShape.text()
if outPath.contains("\\"):
outName = outPath.right((outPath.length() - outPath.lastIndexOf("\\")) - 1)
else:
outName = outPath.right((outPath.length() - outPath.lastIndexOf("/")) - 1)
if outName.endsWith(".shp"):
outName = outName.left(outName.length() - 4)
self.compute(inPoly, inPts, inField, outPath, self.progressBar)
self.outShape.clear()
addToTOC = QMessageBox.question(self, self.tr("Count Points in Polygon"), self.tr("Created output shapefile:\n%1\n\nWould you like to add the new layer to the TOC?").arg(unicode(outPath)), QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton)
if addToTOC == QMessageBox.Yes:
self.vlayer = QgsVectorLayer(outPath, unicode(outName), "ogr")
QgsMapLayerRegistry.instance().addMapLayer(self.vlayer)
self.populateLayers()
self.progressBar.setValue(0)
self.buttonOk.setEnabled( True )
def outFile(self):
self.outShape.clear()
( self.shapefileName, self.encoding ) = ftools_utils.saveDialog( self )
(self.shapefileName, self.encoding) = ftools_utils.saveDialog(self)
if self.shapefileName is None or self.encoding is None:
return
self.outShape.setText( QString( self.shapefileName ) )
self.outShape.setText(QString(self.shapefileName))
def accept(self):
if self.inPolygon.currentText() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"),
self.tr("Please specify input polygon vector layer"))
elif self.inPoint.currentText() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"),
self.tr("Please specify input point vector layer"))
elif self.lnField.text() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"),
self.tr("Please specify output count field"))
elif self.outShape.text() == "":
QMessageBox.information(self, self.tr("Count Points In Polygon"),
self.tr("Please specify output shapefile"))
else:
inPoly = ftools_utils.getVectorLayerByName(self.inPolygon.currentText())
inPnts = ftools_utils.getVectorLayerByName(self.inPoint.currentText())
polyProvider = inPoly.dataProvider()
pointProvider = inPnts.dataProvider()
if polyProvider.crs() <> pointProvider.crs():
QMessageBox.warning(self, self.tr("CRS warning!"),
self.tr("Warning: Input layers have non-matching CRS.\nThis may cause unexpected results."))
self.btnOk.setEnabled(False)
self.workThread = PointsInPolygonThread(inPoly, inPnts, self.lnField.text(), self.outShape.text(), self.encoding)
QObject.connect(self.workThread, SIGNAL("rangeChanged(int)"), self.setProgressRange)
QObject.connect(self.workThread, SIGNAL("updateProgress()"), self.updateProgress)
QObject.connect(self.workThread, SIGNAL("processingFinished()"), self.processFinished)
QObject.connect(self.workThread, SIGNAL("processingInterrupted()"), self.processInterrupted)
self.btnClose.setText(self.tr("Cancel"))
QObject.disconnect(self.buttonBox, SIGNAL("rejected()"), self.reject)
QObject.connect(self.btnClose, SIGNAL("clicked()"), self.stopProcessing)
self.workThread.start()
def setProgressRange(self, maxValue):
self.progressBar.setRange(0, maxValue)
self.progressBar.setValue(0)
def updateProgress(self):
self.progressBar.setValue(self.progressBar.value() + 1)
def processFinished(self):
self.stopProcessing()
addToTOC = QMessageBox.question(self, self.tr("Count Points in Polygon"),
self.tr("Created output shapefile:\n%1\n\nWould you like to add the new layer to the TOC?").arg(self.outShape.text()),
QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton)
if addToTOC == QMessageBox.Yes:
fileInfo = QFileInfo( self.outShape.text() )
layerName = fileInfo.completeBaseName()
layer = QgsVectorLayer(self.outShape.text(), layerName, "ogr")
QgsMapLayerRegistry.instance().addMapLayer(layer)
self.populateLayers()
self.restoreGui()
def processInterrupted(self):
self.restoreGui()
def stopProcessing(self):
if self.workThread != None:
self.workThread.stop()
self.workThread = None
def restoreGui(self):
self.progressBar.setRange(0, 1)
self.progressBar.setValue(0)
self.outShape.clear()
QObject.disconnect(self.btnClose, SIGNAL("clicked()"), self.stopProcessing)
QObject.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
self.btnClose.setText(self.tr("Close"))
self.btnOk.setEnabled(True)
class PointsInPolygonThread(QThread):
def __init__( self, inPoly, inPoints, fieldName, outPath, encoding ):
QThread.__init__( self, QThread.currentThread() )
self.mutex = QMutex()
self.stopMe = 0
self.interrupted = False
self.layerPoly = inPoly
self.layerPoints = inPoints
self.fieldName = fieldName
self.outPath = outPath
self.encoding = encoding
def run(self):
self.mutex.lock()
self.stopMe = 0
self.mutex.unlock()
interrupted = False
polyProvider = self.layerPoly.dataProvider()
pointProvider = self.layerPoints.dataProvider()
def compute(self, inPoly, inPts, inField, outPath, progressBar):
polyLayer = ftools_utils.getVectorLayerByName(inPoly)
pointLayer = ftools_utils.getVectorLayerByName(inPts)
polyProvider = polyLayer.dataProvider()
pointProvider = pointLayer.dataProvider()
if polyProvider.crs() <> pointProvider.crs():
QMessageBox.warning(self, self.tr("CRS warning!"), self.tr("Warning: Input layers have non-matching CRS.\nThis may cause unexpected results."))
allAttrs = polyProvider.attributeIndexes()
polyProvider.select(allAttrs)
allAttrs = pointProvider.attributeIndexes()
pointProvider.select(allAttrs)
fieldList = ftools_utils.getFieldList(polyLayer)
index = polyProvider.fieldNameIndex(unicode(inField))
fieldList = ftools_utils.getFieldList(self.layerPoly)
index = polyProvider.fieldNameIndex(unicode(self.fieldName))
if index == -1:
index = polyProvider.fieldCount()
field = QgsField(unicode(inField), QVariant.Double, "real", 24, 15, self.tr("point count field"))
field = QgsField(unicode(self.fieldName), QVariant.Double, "real", 24, 15, self.tr("point count field"))
fieldList[index] = field
sRs = polyProvider.crs()
check = QFile(self.shapefileName)
if check.exists():
if not QgsVectorFileWriter.deleteShapeFile(self.shapefileName):
if QFile(self.outPath).exists():
if not QgsVectorFileWriter.deleteShapeFile(self.outPath):
return
writer = QgsVectorFileWriter(self.shapefileName, self.encoding, fieldList, polyProvider.geometryType(), sRs)
inFeat = QgsFeature()
inFeatB = QgsFeature()
writer = QgsVectorFileWriter(self.outPath, self.encoding, fieldList,
polyProvider.geometryType(), sRs)
spatialIndex = ftools_utils.createIndex( pointProvider )
pointProvider.rewind()
pointProvider.select()
self.emit(SIGNAL("rangeChanged(int)"), polyProvider.featureCount() )
polyFeat = QgsFeature()
pntFeat = QgsFeature()
outFeat = QgsFeature()
inGeom = QgsGeometry()
start = 15.00
add = 85.00 / polyProvider.featureCount()
spatialIndex = ftools_utils.createIndex( pointProvider )
while polyProvider.nextFeature(inFeat):
inGeom = inFeat.geometry()
atMap = inFeat.attributeMap()
while polyProvider.nextFeature(polyFeat):
inGeom = polyFeat.geometry()
atMap = polyFeat.attributeMap()
outFeat.setAttributeMap(atMap)
outFeat.setGeometry(inGeom)
pointList = []
count = 0
pointList = []
hasIntersection = True
pointList = spatialIndex.intersects(inGeom.boundingBox())
if len(pointList) > 0: check = 0
else: check = 1
if check == 0:
for i in pointList:
pointProvider.featureAtId( int( i ), inFeatB , True, allAttrs )
tmpGeom = QgsGeometry( inFeatB.geometry() )
if inGeom.intersects( tmpGeom ):
count = count + 1
outFeat.setAttributeMap(atMap)
if len(pointList) > 0:
hasIntersection = True
else:
hasIntersection = False
if hasIntersection:
for p in pointList:
pointProvider.featureAtId(p, pntFeat , True)
tmpGeom = QgsGeometry(pntFeat.geometry())
if inGeom.intersects(tmpGeom):
count += 1
self.mutex.lock()
s = self.stopMe
self.mutex.unlock()
if s == 1:
interrupted = True
break
outFeat.addAttribute(index, QVariant(count))
writer.addFeature(outFeat)
start = start + 1
progressBar.setValue(start)
self.emit( SIGNAL( "updateProgress()" ) )
self.mutex.lock()
s = self.stopMe
self.mutex.unlock()
if s == 1:
interrupted = True
break
del writer
if not interrupted:
self.emit( SIGNAL( "processingFinished()" ) )
else:
self.emit( SIGNAL( "processingInterrupted()" ) )
def stop(self):
self.mutex.lock()
self.stopMe = 1
self.mutex.unlock()
QThread.wait( self )

View File

@ -41,7 +41,7 @@ class Dialog(QDialog, Ui_Dialog):
self.iface = iface
# Set up the user interface from Designer.
self.setupUi(self)
self.buttonOk = self.buttonBox_2.button( QDialogButtonBox.Ok )
self.buttonOk = self.buttonBox.button( QDialogButtonBox.Ok )
# populate layer list
self.progressBar.setValue(0)
mapCanvas = self.iface.mapCanvas()

View File

@ -1,107 +1,102 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog" >
<property name="windowModality" >
<enum>Qt::NonModal</enum>
</property>
<property name="geometry" >
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>383</width>
<height>291</height>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Count Points In Polygons</string>
</property>
<property name="sizeGripEnabled" >
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" colspan="2" >
<layout class="QVBoxLayout" >
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_3" >
<property name="text" >
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input polygon vector layer</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inPolygon" />
<widget class="QComboBox" name="inPolygon"/>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2" >
<layout class="QVBoxLayout" >
<item row="1" column="0" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_4" >
<property name="text" >
<widget class="QLabel" name="label_4">
<property name="text">
<string>Input point vector layer</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inPoint" />
<widget class="QComboBox" name="inPoint"/>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2" >
<layout class="QHBoxLayout" >
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout">
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<widget class="QLabel" name="label_5">
<property name="text">
<string>Output count field name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lnField" >
<property name="text" >
<widget class="QLineEdit" name="lnField">
<property name="text">
<string>PNTCNT</string>
</property>
<property name="maxLength" >
<property name="maxLength">
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2" >
<spacer name="verticalSpacer" >
<property name="orientation" >
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>16</height>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0" colspan="2" >
<layout class="QVBoxLayout" name="verticalLayout" >
<item row="4" column="0" colspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<widget class="QLabel" name="label_2">
<property name="text">
<string>Output Shapefile</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="outShape" >
<property name="readOnly" >
<widget class="QLineEdit" name="outShape">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolOut" >
<property name="text" >
<widget class="QToolButton" name="toolOut">
<property name="text">
<string>Browse</string>
</property>
</widget>
@ -110,25 +105,25 @@
</item>
</layout>
</item>
<item row="5" column="0" >
<widget class="QProgressBar" name="progressBar" >
<property name="value" >
<item row="5" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textVisible" >
<property name="textVisible">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="1" >
<widget class="QDialogButtonBox" name="buttonBox_2" >
<property name="orientation" >
<item row="5" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
@ -138,34 +133,34 @@
<resources/>
<connections>
<connection>
<sender>buttonBox_2</sender>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>133</x>
<y>512</y>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<x>215</x>
<y>290</y>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox_2</sender>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>close()</slot>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>59</x>
<y>512</y>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<x>132</x>
<y>239</y>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>