[FEATURE] Build spatial index tool for fTools (see #2779). Currently

without icon
This commit is contained in:
Alexander Bruy 2011-11-12 12:42:30 +02:00
parent 55337b3bbc
commit 48112cc1bc
3 changed files with 360 additions and 3 deletions

View File

@ -43,7 +43,7 @@ import doGeometry, doGeoprocessing, doVisual
import doIntersectLines, doSelectByLocation, doVectorSplit, doMeanCoords
import doPointDistance, doPointsInPolygon, doRandom, doRandPoints, doRegPoints
import doSpatialJoin, doSubsetSelect, doSumLines, doVectorGrid, doMergeShapes
import doValidate, doSimplify, doDefineProj
import doValidate, doSimplify, doDefineProj, doSpatialIndex
class fToolsPlugin:
def __init__(self,iface):
@ -113,6 +113,7 @@ class fToolsPlugin:
self.spatJoin.setIcon(QIcon(self.getThemeIcon("join_location.png")))
self.splitVect.setIcon(QIcon(self.getThemeIcon("split_layer.png")))
self.mergeShapes.setIcon(QIcon(self.getThemeIcon("merge_shapes.png")))
self.spatialIndex.setIcon(QIcon(self.getThemeIcon("spatial_index.png")))
def initGui(self):
if int(self.QgisVersion) < 1:
@ -179,7 +180,8 @@ class fToolsPlugin:
self.spatJoin = QAction(QCoreApplication.translate("fTools", "Join attributes by location"), self.iface.mainWindow())
self.splitVect = QAction(QCoreApplication.translate("fTools", "Split vector layer"), self.iface.mainWindow())
self.mergeShapes = QAction(QCoreApplication.translate("fTools", "Merge shapefiles to one"), self.iface.mainWindow())
self.dataManageMenu.addActions([self.define, self.spatJoin, self.splitVect, self.mergeShapes])
self.spatialIndex = QAction(QCoreApplication.translate("fTools", "Create spatial index"), self.iface.mainWindow())
self.dataManageMenu.addActions([self.define, self.spatJoin, self.splitVect, self.mergeShapes, self.spatialIndex])
self.updateThemeIcons("theme")
self.menu.addMenu(self.analysisMenu)
@ -235,6 +237,7 @@ class fToolsPlugin:
QObject.connect(self.spatJoin, SIGNAL("triggered()"), self.dospatJoin)
QObject.connect(self.splitVect, SIGNAL("triggered()"), self.dosplitVect)
QObject.connect(self.mergeShapes, SIGNAL("triggered()"), self.doMergeShapes)
QObject.connect(self.spatialIndex, SIGNAL("triggered()"), self.doSpatIndex)
def unload(self):
pass
@ -319,7 +322,7 @@ class fToolsPlugin:
def dodelaunay(self):
d = doGeometry.GeometryDialog(self.iface, 8)
d.exec_()
def dovoronoi(self):
d = doGeometry.GeometryDialog(self.iface, 10)
d.exec_()
@ -392,3 +395,7 @@ class fToolsPlugin:
d = doMergeShapes.Dialog(self.iface)
d.exec_()
def doSpatIndex(self):
d = doSpatialIndex.Dialog(self.iface)
d.show()
d.exec_()

View File

@ -0,0 +1,223 @@
# -*- coding: utf-8 -*-
"""
***************************************************************************
doSpatialIndex.py - build spatial index for vector layers or files
--------------------------------------
Date : 11-Nov-2011
Copyright : (C) 2011 by Alexander Bruy
Email : alexander dot bruy at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************
"""
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import *
import ftools_utils
from ui_frmSpatialIndex import Ui_Dialog
class Dialog( QDialog, Ui_Dialog ):
def __init__( self, iface ):
QDialog.__init__( self )
self.setupUi( self )
self.iface = iface
self.workThread = None
self.btnOk = self.buttonBox.button( QDialogButtonBox.Ok )
self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
QObject.connect( self.chkExternalFiles, SIGNAL( "stateChanged( int )" ), self.toggleExternalFiles )
QObject.connect( self.btnSelectFiles, SIGNAL( "clicked()" ), self.selectFiles )
QObject.connect( self.lstLayers, SIGNAL( "itemSelectionChanged()" ), self.updateLayerList )
QObject.connect( self.btnSelectAll, SIGNAL( "clicked()" ), self.selectAll )
QObject.connect( self.btnSelectNone, SIGNAL( "clicked()" ), self.selectNone )
QObject.connect( self.btnClearList, SIGNAL( "clicked()" ), self.clearList )
self.manageGui()
def manageGui( self ):
self.btnSelectFiles.setEnabled( False )
self.btnClearList.setEnabled( False )
self.fillLayersList()
def fillLayersList( self ):
self.lstLayers.clear()
layers = ftools_utils.getLayerNames( [ QGis.Line, QGis.Point, QGis.Polygon ] )
for lay in layers:
source = ftools_utils.getVectorLayerByName( lay ).source()
item = QListWidgetItem( lay, self.lstLayers )
item.setData( Qt.UserRole, source )
item.setData( Qt.ToolTipRole, source )
def toggleExternalFiles( self ):
if self.chkExternalFiles.isChecked():
self.btnSelectFiles.setEnabled( True )
self.btnClearList.setEnabled( True )
self.btnSelectAll.setEnabled( False )
self.btnSelectNone.setEnabled( False )
self.lstLayers.clear()
self.lstLayers.setSelectionMode( QAbstractItemView.NoSelection )
self.layers = []
else:
self.btnSelectFiles.setEnabled( False )
self.btnClearList.setEnabled( False )
self.btnSelectAll.setEnabled( True )
self.btnSelectNone.setEnabled( True )
self.fillLayersList()
self.lstLayers.setSelectionMode( QAbstractItemView.ExtendedSelection )
self.updateLayerList()
def updateLayerList( self ):
self.layers = []
selection = self.lstLayers.selectedItems()
for item in selection:
self.layers.append( item.text() )
def selectFiles( self ):
filters = QgsProviderRegistry.instance().fileVectorFilters()
( files, self.encoding ) = ftools_utils.openDialog( self, filtering = filters, dialogMode = "MultipleFiles" )
if files is None:
return
self.layers.extend( [ unicode( f ) for f in files ] )
self.lstLayers.addItems( files )
def selectAll( self ):
self.lstLayers.selectAll()
def selectNone( self ):
self.lstLayers.clearSelection()
def clearList( self ):
self.layers = []
self.lstLayers.clear()
def accept( self ):
QApplication.setOverrideCursor( Qt.WaitCursor )
self.btnOk.setEnabled( False )
self.workThread = SpatialIdxThread( self.layers, self.chkExternalFiles.isChecked() )
self.progressBar.setRange( 0, len( self.layers ) )
QObject.connect( self.workThread, SIGNAL( "layerProcessed()" ), self.layerProcessed )
QObject.connect( self.workThread, SIGNAL( "processFinished( PyQt_PyObject )" ), self.processFinished )
QObject.connect( self.workThread, SIGNAL( "processInterrupted()" ), 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 layerProcessed( self ):
self.progressBar.setValue( self.progressBar.value() + 1 )
def processInterrupted( self ):
self.restoreGui()
def processFinished( self, errors ):
self.stopProcessing()
self.restoreGui()
if not errors.isEmpty():
msg = QString( "Processing of the following layers/files ended with error:<br><br>" ).append( errors.join( "<br>" ) )
QErrorMessage( self ).showMessage( msg )
QMessageBox.information( self, self.tr( "Finished" ), self.tr( "Processing completed." ) )
def stopProcessing( self ):
if self.workThread != None:
self.workThread.stop()
self.workThread = None
def restoreGui( self ):
self.progressBar.setValue( 0 )
QApplication.restoreOverrideCursor()
QObject.connect( self.buttonBox, SIGNAL( "rejected()" ), self.reject )
self.btnClose.setText( self.tr( "Close" ) )
self.btnOk.setEnabled( True )
if self.chkExternalFiles.isChecked():
self.clearList()
class SpatialIdxThread( QThread ):
def __init__( self, layers, isFiles ):
QThread.__init__( self, QThread.currentThread() )
self.layers = layers
self.isFiles = isFiles
self.mutex = QMutex()
self.stopMe = 0
self.errors = QStringList()
def run( self ):
self.mutex.lock()
self.stopMe = 0
self.mutex.unlock()
interrupted = False
if self.isFiles:
for layer in self.layers:
vl = QgsVectorLayer( layer, "tmp", "ogr" )
provider = vl.dataProvider()
if provider.capabilities() & QgsVectorDataProvider.CreateSpatialIndex:
if not provider.createSpatialIndex():
self.errors.append( layer )
else:
self.errors.append( layer )
self.emit( SIGNAL( "layerProcessed()" ) )
self.mutex.lock()
s = self.stopMe
self.mutex.unlock()
if s == 1:
interrupted = True
break
else:
for layer in self.layers:
vl = ftools_utils.getVectorLayerByName( layer )
provider = vl.dataProvider()
if provider.capabilities() & QgsVectorDataProvider.CreateSpatialIndex:
if not provider.createSpatialIndex():
self.errors.append( layer )
else:
self.errors.append( layer )
self.emit( SIGNAL( "layerProcessed()" ) )
self.mutex.lock()
s = self.stopMe
self.mutex.unlock()
if s == 1:
interrupted = True
break
if not interrupted:
self.emit( SIGNAL( "processFinished( PyQt_PyObject )" ), self.errors )
else:
self.emit( SIGNAL( "processInterrupted()" ) )
def stop( self ):
self.mutex.lock()
self.stopMe = 1
self.mutex.unlock()
QThread.wait( self )

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>351</width>
<height>272</height>
</rect>
</property>
<property name="windowTitle">
<string>Build spatial index</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="chkExternalFiles">
<property name="text">
<string>Select files from disk</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectFiles">
<property name="text">
<string>Select files...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="lstLayers">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="btnSelectAll">
<property name="text">
<string>Select all</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSelectNone">
<property name="text">
<string>Select none</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClearList">
<property name="text">
<string>Clear list</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>