Skip to content

Commit 5368468

Browse files
committed
run Points in Polygon in separate thread and don't block QGIS main
window (addresses #5162)
1 parent 34b95d3 commit 5368468

File tree

4 files changed

+236
-138
lines changed

4 files changed

+236
-138
lines changed

python/plugins/fTools/fTools.py

+1
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ def dosumLines(self):
450450

451451
def dopointsPoly(self):
452452
d = doPointsInPolygon.Dialog(self.iface)
453+
d.show()
453454
d.exec_()
454455

455456
def dorandSel(self):

python/plugins/fTools/tools/doPointsInPolygon.py

+171-69
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@ class Dialog(QDialog, Ui_Dialog):
3939
def __init__(self, iface):
4040
QDialog.__init__(self, iface.mainWindow())
4141
self.iface = iface
42-
# Set up the user interface from Designer.
4342
self.setupUi(self)
44-
QObject.connect(self.toolOut, SIGNAL("clicked()"), self.outFile)
43+
4544
self.setWindowTitle(self.tr("Count Points in Polygon"))
46-
self.buttonOk = self.buttonBox_2.button( QDialogButtonBox.Ok )
45+
self.btnOk = self.buttonBox.button( QDialogButtonBox.Ok )
46+
self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
47+
48+
QObject.connect(self.toolOut, SIGNAL("clicked()"), self.outFile)
4749
self.progressBar.setValue(0)
4850
self.populateLayers()
4951

@@ -56,93 +58,193 @@ def populateLayers( self ):
5658
layers = ftools_utils.getLayerNames([QGis.Point])
5759
self.inPoint.addItems(layers)
5860

61+
def outFile(self):
62+
self.outShape.clear()
63+
(self.shapefileName, self.encoding) = ftools_utils.saveDialog(self)
64+
if self.shapefileName is None or self.encoding is None:
65+
return
66+
self.outShape.setText(QString(self.shapefileName))
67+
5968
def accept(self):
60-
self.buttonOk.setEnabled( False )
6169
if self.inPolygon.currentText() == "":
62-
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify input polygon vector layer"))
63-
elif self.outShape.text() == "":
64-
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify output shapefile"))
70+
QMessageBox.information(self, self.tr("Count Points In Polygon"),
71+
self.tr("Please specify input polygon vector layer"))
6572
elif self.inPoint.currentText() == "":
66-
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify input point vector layer"))
73+
QMessageBox.information(self, self.tr("Count Points In Polygon"),
74+
self.tr("Please specify input point vector layer"))
6775
elif self.lnField.text() == "":
68-
QMessageBox.information(self, self.tr("Count Points In Polygon"), self.tr("Please specify output count field"))
76+
QMessageBox.information(self, self.tr("Count Points In Polygon"),
77+
self.tr("Please specify output count field"))
78+
elif self.outShape.text() == "":
79+
QMessageBox.information(self, self.tr("Count Points In Polygon"),
80+
self.tr("Please specify output shapefile"))
6981
else:
70-
inPoly = self.inPolygon.currentText()
71-
inPts = self.inPoint.currentText()
72-
inField = self.lnField.text()
73-
outPath = self.outShape.text()
74-
if outPath.contains("\\"):
75-
outName = outPath.right((outPath.length() - outPath.lastIndexOf("\\")) - 1)
76-
else:
77-
outName = outPath.right((outPath.length() - outPath.lastIndexOf("/")) - 1)
78-
if outName.endsWith(".shp"):
79-
outName = outName.left(outName.length() - 4)
80-
self.compute(inPoly, inPts, inField, outPath, self.progressBar)
81-
self.outShape.clear()
82-
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)
83-
if addToTOC == QMessageBox.Yes:
84-
self.vlayer = QgsVectorLayer(outPath, unicode(outName), "ogr")
85-
QgsMapLayerRegistry.instance().addMapLayer(self.vlayer)
86-
self.populateLayers()
82+
inPoly = ftools_utils.getVectorLayerByName(self.inPolygon.currentText())
83+
inPnts = ftools_utils.getVectorLayerByName(self.inPoint.currentText())
84+
85+
polyProvider = inPoly.dataProvider()
86+
pointProvider = inPnts.dataProvider()
87+
if polyProvider.crs() <> pointProvider.crs():
88+
QMessageBox.warning(self, self.tr("CRS warning!"),
89+
self.tr("Warning: Input layers have non-matching CRS.\nThis may cause unexpected results."))
90+
91+
self.btnOk.setEnabled(False)
92+
93+
self.workThread = PointsInPolygonThread(inPoly, inPnts, self.lnField.text(), self.outShape.text(), self.encoding)
94+
95+
QObject.connect(self.workThread, SIGNAL("rangeChanged(int)"), self.setProgressRange)
96+
QObject.connect(self.workThread, SIGNAL("updateProgress()"), self.updateProgress)
97+
QObject.connect(self.workThread, SIGNAL("processingFinished()"), self.processFinished)
98+
QObject.connect(self.workThread, SIGNAL("processingInterrupted()"), self.processInterrupted)
99+
100+
self.btnClose.setText(self.tr("Cancel"))
101+
QObject.disconnect(self.buttonBox, SIGNAL("rejected()"), self.reject)
102+
QObject.connect(self.btnClose, SIGNAL("clicked()"), self.stopProcessing)
103+
104+
self.workThread.start()
105+
106+
def setProgressRange(self, maxValue):
107+
self.progressBar.setRange(0, maxValue)
87108
self.progressBar.setValue(0)
88-
self.buttonOk.setEnabled( True )
89109

90-
def outFile(self):
110+
def updateProgress(self):
111+
self.progressBar.setValue(self.progressBar.value() + 1)
112+
113+
def processFinished(self):
114+
self.stopProcessing()
115+
116+
addToTOC = QMessageBox.question(self, self.tr("Count Points in Polygon"),
117+
self.tr("Created output shapefile:\n%1\n\nWould you like to add the new layer to the TOC?").arg(self.outShape.text()),
118+
QMessageBox.Yes, QMessageBox.No, QMessageBox.NoButton)
119+
if addToTOC == QMessageBox.Yes:
120+
fileInfo = QFileInfo( self.outShape.text() )
121+
layerName = fileInfo.completeBaseName()
122+
layer = QgsVectorLayer(self.outShape.text(), layerName, "ogr")
123+
QgsMapLayerRegistry.instance().addMapLayer(layer)
124+
self.populateLayers()
125+
126+
self.restoreGui()
127+
128+
def processInterrupted(self):
129+
self.restoreGui()
130+
131+
def stopProcessing(self):
132+
if self.workThread != None:
133+
self.workThread.stop()
134+
self.workThread = None
135+
136+
def restoreGui(self):
137+
self.progressBar.setRange(0, 1)
138+
self.progressBar.setValue(0)
91139
self.outShape.clear()
92-
( self.shapefileName, self.encoding ) = ftools_utils.saveDialog( self )
93-
if self.shapefileName is None or self.encoding is None:
94-
return
95-
self.outShape.setText( QString( self.shapefileName ) )
96-
97-
def compute(self, inPoly, inPts, inField, outPath, progressBar):
98-
polyLayer = ftools_utils.getVectorLayerByName(inPoly)
99-
pointLayer = ftools_utils.getVectorLayerByName(inPts)
100-
polyProvider = polyLayer.dataProvider()
101-
pointProvider = pointLayer.dataProvider()
102-
if polyProvider.crs() <> pointProvider.crs():
103-
QMessageBox.warning(self, self.tr("CRS warning!"), self.tr("Warning: Input layers have non-matching CRS.\nThis may cause unexpected results."))
140+
141+
QObject.disconnect(self.btnClose, SIGNAL("clicked()"), self.stopProcessing)
142+
QObject.connect(self.buttonBox, SIGNAL("rejected()"), self.reject)
143+
self.btnClose.setText(self.tr("Close"))
144+
self.btnOk.setEnabled(True)
145+
146+
class PointsInPolygonThread(QThread):
147+
def __init__( self, inPoly, inPoints, fieldName, outPath, encoding ):
148+
QThread.__init__( self, QThread.currentThread() )
149+
self.mutex = QMutex()
150+
self.stopMe = 0
151+
self.interrupted = False
152+
153+
self.layerPoly = inPoly
154+
self.layerPoints = inPoints
155+
self.fieldName = fieldName
156+
self.outPath = outPath
157+
self.encoding = encoding
158+
159+
def run(self):
160+
self.mutex.lock()
161+
self.stopMe = 0
162+
self.mutex.unlock()
163+
164+
interrupted = False
165+
166+
polyProvider = self.layerPoly.dataProvider()
167+
pointProvider = self.layerPoints.dataProvider()
168+
104169
allAttrs = polyProvider.attributeIndexes()
105170
polyProvider.select(allAttrs)
106-
allAttrs = pointProvider.attributeIndexes()
107-
pointProvider.select(allAttrs)
108-
fieldList = ftools_utils.getFieldList(polyLayer)
109-
index = polyProvider.fieldNameIndex(unicode(inField))
171+
172+
fieldList = ftools_utils.getFieldList(self.layerPoly)
173+
index = polyProvider.fieldNameIndex(unicode(self.fieldName))
110174
if index == -1:
111175
index = polyProvider.fieldCount()
112-
field = QgsField(unicode(inField), QVariant.Double, "real", 24, 15, self.tr("point count field"))
176+
field = QgsField(unicode(self.fieldName), QVariant.Double, "real", 24, 15, self.tr("point count field"))
113177
fieldList[index] = field
178+
114179
sRs = polyProvider.crs()
115-
check = QFile(self.shapefileName)
116-
if check.exists():
117-
if not QgsVectorFileWriter.deleteShapeFile(self.shapefileName):
180+
if QFile(self.outPath).exists():
181+
if not QgsVectorFileWriter.deleteShapeFile(self.outPath):
118182
return
119-
writer = QgsVectorFileWriter(self.shapefileName, self.encoding, fieldList, polyProvider.geometryType(), sRs)
120-
inFeat = QgsFeature()
121-
inFeatB = QgsFeature()
183+
184+
writer = QgsVectorFileWriter(self.outPath, self.encoding, fieldList,
185+
polyProvider.geometryType(), sRs)
186+
187+
spatialIndex = ftools_utils.createIndex( pointProvider )
188+
pointProvider.rewind()
189+
pointProvider.select()
190+
191+
self.emit(SIGNAL("rangeChanged(int)"), polyProvider.featureCount() )
192+
193+
polyFeat = QgsFeature()
194+
pntFeat = QgsFeature()
122195
outFeat = QgsFeature()
123196
inGeom = QgsGeometry()
124-
start = 15.00
125-
add = 85.00 / polyProvider.featureCount()
126-
spatialIndex = ftools_utils.createIndex( pointProvider )
127-
while polyProvider.nextFeature(inFeat):
128-
inGeom = inFeat.geometry()
129-
atMap = inFeat.attributeMap()
197+
while polyProvider.nextFeature(polyFeat):
198+
inGeom = polyFeat.geometry()
199+
atMap = polyFeat.attributeMap()
130200
outFeat.setAttributeMap(atMap)
131201
outFeat.setGeometry(inGeom)
132-
pointList = []
202+
133203
count = 0
204+
pointList = []
205+
hasIntersection = True
134206
pointList = spatialIndex.intersects(inGeom.boundingBox())
135-
if len(pointList) > 0: check = 0
136-
else: check = 1
137-
if check == 0:
138-
for i in pointList:
139-
pointProvider.featureAtId( int( i ), inFeatB , True, allAttrs )
140-
tmpGeom = QgsGeometry( inFeatB.geometry() )
141-
if inGeom.intersects( tmpGeom ):
142-
count = count + 1
143-
outFeat.setAttributeMap(atMap)
207+
if len(pointList) > 0:
208+
hasIntersection = True
209+
else:
210+
hasIntersection = False
211+
212+
if hasIntersection:
213+
for p in pointList:
214+
pointProvider.featureAtId(p, pntFeat , True)
215+
tmpGeom = QgsGeometry(pntFeat.geometry())
216+
if inGeom.intersects(tmpGeom):
217+
count += 1
218+
219+
self.mutex.lock()
220+
s = self.stopMe
221+
self.mutex.unlock()
222+
if s == 1:
223+
interrupted = True
224+
break
225+
144226
outFeat.addAttribute(index, QVariant(count))
145227
writer.addFeature(outFeat)
146-
start = start + 1
147-
progressBar.setValue(start)
228+
229+
self.emit( SIGNAL( "updateProgress()" ) )
230+
231+
self.mutex.lock()
232+
s = self.stopMe
233+
self.mutex.unlock()
234+
if s == 1:
235+
interrupted = True
236+
break
237+
148238
del writer
239+
240+
if not interrupted:
241+
self.emit( SIGNAL( "processingFinished()" ) )
242+
else:
243+
self.emit( SIGNAL( "processingInterrupted()" ) )
244+
245+
def stop(self):
246+
self.mutex.lock()
247+
self.stopMe = 1
248+
self.mutex.unlock()
249+
250+
QThread.wait( self )

python/plugins/fTools/tools/doSelectByLocation.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self, iface):
4141
self.iface = iface
4242
# Set up the user interface from Designer.
4343
self.setupUi(self)
44-
self.buttonOk = self.buttonBox_2.button( QDialogButtonBox.Ok )
44+
self.buttonOk = self.buttonBox.button( QDialogButtonBox.Ok )
4545
# populate layer list
4646
self.progressBar.setValue(0)
4747
mapCanvas = self.iface.mapCanvas()

0 commit comments

Comments
 (0)