QGIS/python/plugins/fTools/tools/doMergeShapes.py
2013-06-03 16:16:04 +04:00

348 lines
12 KiB
Python

# -*- coding: utf-8 -*-
"""
***************************************************************************
doMergeShapes.py - merge multiple shapefile into one
--------------------------------------
Date : 30-Mar-2010
Copyright : (C) 2010 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_frmMergeShapes import Ui_Dialog
class Dialog( QDialog, Ui_Dialog ):
def __init__( self, iface ):
QDialog.__init__( self, iface.mainWindow() )
self.setupUi( self )
self.iface = iface
self.mergeThread = None
self.inputFiles = None
self.outFileName = None
self.inEncoding = None
self.btnOk = self.buttonBox.button( QDialogButtonBox.Ok )
self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
QObject.connect( self.btnSelectDir, SIGNAL( "clicked()" ), self.inputDir )
QObject.connect( self.btnSelectFile, SIGNAL( "clicked()" ), self.outFile )
QObject.connect( self.chkListMode, SIGNAL( "stateChanged( int )" ), self.changeMode )
QObject.connect( self.leOutShape, SIGNAL( "editingFinished()" ), self.updateOutFile )
def inputDir( self ):
settings = QSettings()
lastDir = settings.value( "/fTools/lastShapeDir", "." )
inDir = QFileDialog.getExistingDirectory( self,
self.tr( "Select directory with shapefiles to merge" ),
lastDir )
if inDir.isEmpty():
return
workDir = QDir( inDir )
workDir.setFilter( QDir.Files | QDir.NoSymLinks | QDir.NoDotAndDotDot )
nameFilter = [ "*.shp", "*.SHP" ]
workDir.setNameFilters( nameFilter )
self.inputFiles = workDir.entryList()
if self.inputFiles.count() == 0:
QMessageBox.warning( self, self.tr( "No shapefiles found" ),
self.tr( "There are no shapefiles in this directory. Please select another one." ) )
self.inputFiles = None
return
settings.setValue( "/fTools/lastShapeDir", inDir )
self.progressFiles.setRange( 0, self.inputFiles.count() )
self.leInputDir.setText( inDir )
def outFile( self ):
( self.outFileName, self.encoding ) = ftools_utils.saveDialog( self )
if self.outFileName is None or self.encoding is None:
return
self.leOutShape.setText( self.outFileName )
def inputFile( self ):
( files, self.inEncoding ) = ftools_utils.openDialog( self, dialogMode="ManyFiles" )
if files is None or self.inEncoding is None:
self.inputFiles = None
return
self.inputFiles = []
for f in files:
fileName = QFileInfo( f ).fileName()
self.inputFiles.append( fileName )
self.progressFiles.setRange( 0, self.inputFiles.count() )
self.leInputDir.setText( files.join( ";" ) )
def changeMode( self ):
if self.chkListMode.isChecked():
self.label.setText( self.tr( "Input files" ) )
QObject.disconnect( self.btnSelectDir, SIGNAL( "clicked()" ), self.inputDir )
QObject.connect( self.btnSelectDir, SIGNAL( "clicked()" ), self.inputFile )
self.lblGeometry.setEnabled( False )
self.cmbGeometry.setEnabled( False )
else:
self.label.setText( self.tr( "Input directory" ) )
QObject.disconnect( self.btnSelectDir, SIGNAL( "clicked()" ), self.inputFile )
QObject.connect( self.btnSelectDir, SIGNAL( "clicked()" ), self.inputDir )
self.lblGeometry.setEnabled( True )
self.cmbGeometry.setEnabled( True )
def updateOutFile( self ):
self.outFileName = self.leOutShape.text()
settings = QSettings()
self.outEncoding = settings.value( "/UI/encoding" )
def reject( self ):
QDialog.reject( self )
def accept( self ):
if self.inputFiles is None:
workDir = QDir( self.leInputDir.text() )
workDir.setFilter( QDir.Files | QDir.NoSymLinks | QDir.NoDotAndDotDot )
nameFilter = [ "*.shp" << "*.SHP" ]
workDir.setNameFilters( nameFilter )
self.inputFiles = workDir.entryList()
if self.inputFiles.count() == 0:
QMessageBox.warning( self, self.tr( "No shapefiles found" ),
self.tr( "There are no shapefiles in this directory. Please select another one." ) )
self.inputFiles = None
return
if self.outFileName is None:
QMessageBox.warning( self, self.tr( "No output file" ),
self.tr( "Please specify output file." ) )
return
if self.chkListMode.isChecked():
files = self.leInputDir.text().split( ";" )
baseDir = QFileInfo( files[ 0 ] ).absolutePath()
else:
baseDir = self.leInputDir.text()
# look for shapes with specified geometry type
self.inputFiles = ftools_utils.getShapesByGeometryType( baseDir, self.inputFiles, self.cmbGeometry.currentIndex() )
if self.inputFiles is None:
QMessageBox.warning( self, self.tr( "No shapefiles found" ),
self.tr( "There are no shapefiles with the given geometry type. Please select an available geometry type." ) )
return
self.progressFiles.setRange( 0, self.inputFiles.count() )
outFile = QFile( self.outFileName )
if outFile.exists():
if not QgsVectorFileWriter.deleteShapeFile( self.outFileName ):
QMessageBox.warning( self, self.tr( "Delete error" ), self.tr( "Can't delete file %s" ) % ( self.outFileName ) )
return
if self.inEncoding == None:
self.inEncoding = "System"
self.btnOk.setEnabled( False )
self.mergeThread = ShapeMergeThread( baseDir, self.inputFiles, self.inEncoding, self.outFileName, self.encoding )
QObject.connect( self.mergeThread, SIGNAL( "rangeChanged( PyQt_PyObject )" ), self.setFeatureProgressRange )
QObject.connect( self.mergeThread, SIGNAL( "checkStarted()" ), self.setFeatureProgressFormat )
QObject.connect( self.mergeThread, SIGNAL( "checkFinished()" ), self.resetFeatureProgressFormat )
QObject.connect( self.mergeThread, SIGNAL( "fileNameChanged( PyQt_PyObject )" ), self.setShapeProgressFormat )
QObject.connect( self.mergeThread, SIGNAL( "featureProcessed()" ), self.featureProcessed )
QObject.connect( self.mergeThread, SIGNAL( "shapeProcessed()" ), self.shapeProcessed )
QObject.connect( self.mergeThread, SIGNAL( "processingFinished()" ), self.processingFinished )
QObject.connect( self.mergeThread, SIGNAL( "processingInterrupted()" ), self.processingInterrupted )
self.btnClose.setText( self.tr( "Cancel" ) )
QObject.disconnect( self.buttonBox, SIGNAL( "rejected()" ), self.reject )
QObject.connect( self.btnClose, SIGNAL( "clicked()" ), self.stopProcessing )
self.mergeThread.start()
def setFeatureProgressRange( self, maximum ):
self.progressFeatures.setRange( 0, maximum )
self.progressFeatures.setValue( 0 )
def setFeatureProgressFormat( self ):
self.progressFeatures.setFormat( "Checking files: %p% ")
def resetFeatureProgressFormat( self ):
self.progressFeatures.setFormat( "%p% ")
def featureProcessed( self ):
self.progressFeatures.setValue( self.progressFeatures.value() + 1 )
def setShapeProgressFormat( self, fileName ):
self.progressFiles.setFormat( "%p% " + fileName )
def shapeProcessed( self ):
self.progressFiles.setValue( self.progressFiles.value() + 1 )
def processingFinished( self ):
self.stopProcessing()
if self.chkAddToCanvas.isChecked():
if not ftools_utils.addShapeToCanvas( unicode( self.outFileName ) ):
QMessageBox.warning( self, self.tr( "Merging" ),
self.tr( "Error loading output shapefile:\n%s" ) % ( unicode( self.outFileName ) ) )
self.restoreGui()
def processingInterrupted( self ):
self.restoreGui()
def stopProcessing( self ):
if self.mergeThread != None:
self.mergeThread.stop()
self.mergeThread = None
def restoreGui( self ):
self.progressFiles.setFormat( "%p%" )
self.progressFeatures.setRange( 0, 100 )
self.progressFeatures.setValue( 0 )
self.progressFiles.setValue( 0 )
QObject.connect( self.buttonBox, SIGNAL( "rejected()" ), self.reject )
self.btnClose.setText( self.tr( "Close" ) )
self.btnOk.setEnabled( True )
class ShapeMergeThread( QThread ):
def __init__( self, dir, shapes, inputEncoding, outputFileName, outputEncoding ):
QThread.__init__( self, QThread.currentThread() )
self.baseDir = dir
self.shapes = shapes
self.inputEncoding = inputEncoding
self.outputFileName = outputFileName
self.outputEncoding = outputEncoding
self.mutex = QMutex()
self.stopMe = 0
def run( self ):
self.mutex.lock()
self.stopMe = 0
self.mutex.unlock()
interrupted = False
# create attribute list with uniquie fields
# from all selected layers
mergedFields = []
self.emit( SIGNAL( "rangeChanged( PyQt_PyObject )" ), len( self.shapes ) )
self.emit( SIGNAL( "checkStarted()" ) )
shapeIndex = 0
fieldMap = {}
for fileName in self.shapes:
layerPath = QFileInfo( self.baseDir + "/" + fileName ).absoluteFilePath()
newLayer = QgsVectorLayer( layerPath, QFileInfo( layerPath ).baseName(), "ogr" )
if not newLayer.isValid():
continue
vprovider = newLayer.dataProvider()
fieldIndex = 0
for layerField in vprovider.fields():
fieldFound = False
for mergedField in mergedFields:
if mergedField.name() == layerField.name() and mergedField.type() == layerField.type():
fieldFound = True
break
if not fieldFound:
if not fieldMap.has_key(shapeIndex):
fieldMap[shapeIndex]={}
fieldMap[shapeIndex][fieldIndex] = len(mergedFields)
mergedFields.append( layerField )
fieldIndex += 1
shapeIndex += 1
self.emit( SIGNAL( "featureProcessed()" ) )
self.emit( SIGNAL( "checkFinished()" ) )
# get information about shapefiles
layerPath = QFileInfo( self.baseDir + "/" + self.shapes[ 0 ] ).absoluteFilePath()
newLayer = QgsVectorLayer( layerPath, QFileInfo( layerPath ).baseName(), "ogr" )
self.crs = newLayer.crs()
self.geom = newLayer.wkbType()
vprovider = newLayer.dataProvider()
fields = QgsFields()
for f in mergedFields:
fields.append(f)
writer = QgsVectorFileWriter( self.outputFileName, self.outputEncoding,
fields, self.geom, self.crs )
shapeIndex = 0
for fileName in self.shapes:
layerPath = QFileInfo( self.baseDir + "/" + fileName ).absoluteFilePath()
newLayer = QgsVectorLayer( layerPath, QFileInfo( layerPath ).baseName(), "ogr" )
if not newLayer.isValid():
continue
vprovider = newLayer.dataProvider()
vprovider.setEncoding( self.inputEncoding )
layerFields = vprovider.fields()
nFeat = vprovider.featureCount()
self.emit( SIGNAL( "rangeChanged( PyQt_PyObject )" ), nFeat )
self.emit( SIGNAL( "fileNameChanged( PyQt_PyObject )" ), fileName )
inFeat = QgsFeature()
outFeat = QgsFeature()
inGeom = QgsGeometry()
fit = vprovider.getFeatures()
while fit.nextFeature( inFeat ):
mergedAttrs = [""] * len(mergedFields)
# fill available attributes with values
fieldIndex = 0
for v in inFeat.attributes():
if fieldMap.has_key(shapeIndex) and fieldMap[shapeIndex].has_key(fieldIndex):
mergedAttrs[ fieldMap[shapeIndex][fieldIndex] ] = v
fieldIndex += 1
inGeom = QgsGeometry( inFeat.geometry() )
outFeat.setGeometry( inGeom )
outFeat.setAttributes( mergedAttrs )
writer.addFeature( outFeat )
self.emit( SIGNAL( "featureProcessed()" ) )
self.emit( SIGNAL( "shapeProcessed()" ) )
self.mutex.lock()
s = self.stopMe
self.mutex.unlock()
if s == 1:
interrupted = True
break
shapeIndex += 1
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 )