mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \ --exclude="ui_*.py,debian/*,python/ext-libs/*" \ .
514 lines
18 KiB
Python
514 lines
18 KiB
Python
# -*- coding: utf-8 -*-
|
|
#-----------------------------------------------------------
|
|
#
|
|
# fTools
|
|
# Copyright (C) 2008-2011 Carson Farmer
|
|
# EMAIL: carson.farmer (at) gmail.com
|
|
# WEB : http://www.ftools.ca/fTools.html
|
|
#
|
|
# A collection of data management and analysis tools for vector data
|
|
#
|
|
#-----------------------------------------------------------
|
|
#
|
|
# licensed under the terms of GNU GPL 2
|
|
#
|
|
# 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.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
#
|
|
#---------------------------------------------------------------------
|
|
|
|
from PyQt4.QtCore import QObject, SIGNAL, QThread, QMutex, QFile
|
|
from PyQt4.QtGui import QDialog, QDialogButtonBox, QMessageBox
|
|
|
|
from qgis.core import QGis, QgsVectorFileWriter, QgsPoint, QgsGeometry, QgsFeature
|
|
|
|
import ftools_utils
|
|
|
|
from ui_frmSimplify import Ui_Dialog
|
|
|
|
class Dialog( QDialog, Ui_Dialog ):
|
|
def __init__( self, iface, function ):
|
|
QDialog.__init__( self, iface.mainWindow() )
|
|
self.setupUi( self )
|
|
self.iface = iface
|
|
self.myFunction = function
|
|
|
|
self.workThread = None
|
|
|
|
if self.myFunction == 2:
|
|
self.setWindowTitle( self.tr( "Densify geometries" ) )
|
|
self.lblTolerance.setText( self.tr( "Vertices to add" ) )
|
|
self.spnTolerance.setDecimals( 0 )
|
|
self.spnTolerance.setMinimum( 1 )
|
|
self.spnTolerance.setSingleStep( 1 )
|
|
self.spnTolerance.setValue( 1.0 )
|
|
|
|
self.btnOk = self.buttonBox.button( QDialogButtonBox.Ok )
|
|
self.btnClose = self.buttonBox.button( QDialogButtonBox.Close )
|
|
|
|
QObject.connect( self.chkWriteShapefile, SIGNAL( "stateChanged( int )" ), self.updateGui )
|
|
QObject.connect( self.btnSelectOutputFile, SIGNAL( "clicked()" ), self.selectOutputFile )
|
|
|
|
self.populateLayers()
|
|
|
|
def populateLayers( self ):
|
|
layers = ftools_utils.getLayerNames( [ QGis.Polygon, QGis.Line ] )
|
|
self.cmbInputLayer.clear()
|
|
self.cmbInputLayer.addItems( layers )
|
|
|
|
def updateGui( self ):
|
|
if self.chkWriteShapefile.isChecked():
|
|
self.edOutputFile.setEnabled( True )
|
|
self.btnSelectOutputFile.setEnabled( True )
|
|
self.chkAddToCanvas.setEnabled( True )
|
|
else:
|
|
self.edOutputFile.setEnabled( False )
|
|
self.btnSelectOutputFile.setEnabled( False )
|
|
self.chkAddToCanvas.setEnabled( False )
|
|
|
|
self.encoding = None
|
|
|
|
def selectOutputFile( self ):
|
|
self.edOutputFile.clear()
|
|
( self.shapeFileName, self.encoding ) = ftools_utils.saveDialog( self )
|
|
if self.shapeFileName is None or self.encoding is None:
|
|
return
|
|
self.edOutputFile.setText( self.shapeFileName )
|
|
|
|
def accept( self ):
|
|
if not self.cmbInputLayer.currentText():
|
|
QMessageBox.warning( self, self.tr( "Warning" ), self.tr( "Please specify an input layer" ) )
|
|
return
|
|
vLayer = ftools_utils.getVectorLayerByName( self.cmbInputLayer.currentText() )
|
|
|
|
self.btnOk.setEnabled( False )
|
|
|
|
if self.chkWriteShapefile.isChecked():
|
|
outFileName = self.edOutputFile.text()
|
|
outFile = QFile( outFileName )
|
|
if outFile.exists():
|
|
if not QgsVectorFileWriter.deleteShapeFile( outFileName ):
|
|
QMessageBox.warning( self, self.tr( "Delete error" ),
|
|
self.tr( "Can't delete file %s" ) % ( outFileName ) )
|
|
return
|
|
|
|
self.workThread = GeomThread( self.myFunction, vLayer, self.chkUseSelection.isChecked(),
|
|
self.spnTolerance.value(), True, outFileName, self.encoding )
|
|
else:
|
|
res = QMessageBox.warning( self, self.tr( "Warning"),
|
|
self.tr(
|
|
"Currently QGIS doesn't allow simultaneous access from "
|
|
"different threads to the same datasource. Make sure your layer's "
|
|
"attribute tables are closed. Continue?"),
|
|
QMessageBox.Yes | QMessageBox.No )
|
|
if res == QMessageBox.No:
|
|
return
|
|
|
|
self.workThread = GeomThread( self.myFunction, vLayer, self.chkUseSelection.isChecked(),
|
|
self.spnTolerance.value(), False, None, None )
|
|
|
|
QObject.connect( self.workThread, SIGNAL( "rangeCalculated( PyQt_PyObject )" ), self.setProgressRange )
|
|
QObject.connect( self.workThread, SIGNAL( "featureProcessed()" ), self.featureProcessed )
|
|
QObject.connect( self.workThread, SIGNAL( "processingFinished( PyQt_PyObject )" ), 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, maximum ):
|
|
self.progressBar.setRange( 0, maximum )
|
|
|
|
def featureProcessed( self ):
|
|
self.progressBar.setValue( self.progressBar.value() + 1 )
|
|
|
|
def processFinished( self, pointsCount ):
|
|
self.stopProcessing()
|
|
|
|
if self.myFunction == 1:
|
|
QMessageBox.information( self, self.tr( "Simplify results" ),
|
|
self.tr( "There were %d vertices in original dataset which\nwere reduced to %d vertices after simplification" ) % ( pointsCount[ 0 ], pointsCount[ 1 ] ) )
|
|
|
|
self.restoreGui()
|
|
|
|
if self.chkAddToCanvas.isEnabled() and self.chkAddToCanvas.isChecked():
|
|
if not ftools_utils.addShapeToCanvas( unicode( self.shapeFileName ) ):
|
|
QMessageBox.warning( self, self.tr( "Error" ),
|
|
self.tr( "Error loading output shapefile:\n%s" ) % ( unicode( self.shapeFileName ) ) )
|
|
self.populateLayers()
|
|
|
|
QMessageBox.information( self, self.tr( "Finished" ), self.tr( "Processing completed." ) )
|
|
self.iface.mapCanvas().refresh()
|
|
|
|
def processInterrupted( self ):
|
|
self.restoreGui()
|
|
|
|
def stopProcessing( self ):
|
|
if self.workThread is not None:
|
|
self.workThread.stop()
|
|
self.workThread = None
|
|
|
|
def restoreGui( self ):
|
|
self.progressBar.setValue( 0 )
|
|
QObject.connect( self.buttonBox, SIGNAL( "rejected()" ), self.reject )
|
|
self.btnClose.setText( self.tr( "Close" ) )
|
|
self.btnOk.setEnabled( True )
|
|
|
|
def geomVertexCount( geometry ):
|
|
geomType = geometry.type()
|
|
if geomType == QGis.Line:
|
|
if geometry.isMultipart():
|
|
pointsList = geometry.asMultiPolyline()
|
|
points = sum( pointsList, [] )
|
|
else:
|
|
points = geometry.asPolyline()
|
|
return len( points )
|
|
elif geomType == QGis.Polygon:
|
|
if geometry.isMultipart():
|
|
polylinesList = geometry.asMultiPolygon()
|
|
polylines = sum( polylinesList, [] )
|
|
else:
|
|
polylines = geometry.asPolygon()
|
|
points = []
|
|
for l in polylines:
|
|
points.extend( l )
|
|
return len( points )
|
|
else:
|
|
return None
|
|
|
|
def densify( polyline, pointsNumber ):
|
|
output = []
|
|
if pointsNumber != 1:
|
|
multiplier = 1.0 / float( pointsNumber + 1 )
|
|
else:
|
|
multiplier = 1
|
|
for i in xrange( len( polyline ) - 1 ):
|
|
p1 = polyline[ i ]
|
|
p2 = polyline[ i + 1 ]
|
|
output.append( p1 )
|
|
for j in xrange( pointsNumber ):
|
|
delta = multiplier * ( j + 1 )
|
|
x = p1.x() + delta * ( p2.x() - p1.x() )
|
|
y = p1.y() + delta * ( p2.y() - p1.y() )
|
|
output.append( QgsPoint( x, y ) )
|
|
if j + 1 == pointsNumber:
|
|
break
|
|
output.append( polyline[ len( polyline ) - 1 ] )
|
|
return output
|
|
|
|
def densifyGeometry( geometry, pointsNumber, isPolygon ):
|
|
output = []
|
|
if isPolygon:
|
|
if geometry.isMultipart():
|
|
polygons = geometry.asMultiPolygon()
|
|
for poly in polygons:
|
|
p = []
|
|
for ring in poly:
|
|
p.append( densify( ring, pointsNumber ) )
|
|
output.append( p )
|
|
return QgsGeometry.fromMultiPolygon( output )
|
|
else:
|
|
rings = geometry.asPolygon()
|
|
for ring in rings:
|
|
output.append( densify( ring, pointsNumber ) )
|
|
return QgsGeometry.fromPolygon( output )
|
|
else:
|
|
if geometry.isMultipart():
|
|
lines = geometry.asMultiPolyline()
|
|
for points in lines:
|
|
output.append( densify( points, pointsNumber ) )
|
|
return QgsGeometry.fromMultiPolyline( output )
|
|
else:
|
|
points = geometry.asPolyline()
|
|
output = densify( points, pointsNumber )
|
|
return QgsGeometry.fromPolyline( output )
|
|
|
|
class GeomThread( QThread ):
|
|
def __init__( self, function, inputLayer, useSelection, tolerance, writeShape, shapePath, shapeEncoding ):
|
|
QThread.__init__( self, QThread.currentThread() )
|
|
self.inputLayer = inputLayer
|
|
self.useSelection = useSelection
|
|
self.tolerance = tolerance
|
|
self.writeShape = writeShape
|
|
self.outputFileName = shapePath
|
|
self.outputEncoding = shapeEncoding
|
|
self.myFunction = function
|
|
|
|
self.mutex = QMutex()
|
|
self.stopMe = 0
|
|
|
|
def run( self ):
|
|
if self.myFunction == 1:
|
|
self.runSimplify()
|
|
else:
|
|
self.runDensify()
|
|
|
|
def runSimplify( self ):
|
|
self.mutex.lock()
|
|
self.stopMe = 0
|
|
self.mutex.unlock()
|
|
|
|
interrupted = False
|
|
|
|
shapeFileWriter = None
|
|
|
|
pointsBefore = 0
|
|
pointsAfter = 0
|
|
|
|
if self.writeShape:
|
|
vProvider = self.inputLayer.dataProvider()
|
|
shapeFields = vProvider.fields()
|
|
crs = vProvider.crs()
|
|
wkbType = self.inputLayer.wkbType()
|
|
if not crs.isValid():
|
|
crs = None
|
|
shapeFileWriter = QgsVectorFileWriter( self.outputFileName, self.outputEncoding, shapeFields, wkbType, crs )
|
|
featureId = 0
|
|
if self.useSelection:
|
|
selection = self.inputLayer.selectedFeatures()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), len( selection ) )
|
|
for f in selection:
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
attrMap = f.attributes()
|
|
|
|
pointsBefore += geomVertexCount( featGeometry )
|
|
newGeometry = featGeometry.simplify( self.tolerance )
|
|
pointsAfter += geomVertexCount( newGeometry )
|
|
|
|
feature = QgsFeature()
|
|
feature.setGeometry( newGeometry )
|
|
feature.setAttributes( attrMap )
|
|
shapeFileWriter.addFeature( feature )
|
|
featureId += 1
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else:
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), vProvider.featureCount() )
|
|
f = QgsFeature()
|
|
fit = vProvider.getFeatures()
|
|
while fit.nextFeature( f ):
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
attrMap = f.attributes()
|
|
|
|
pointsBefore += geomVertexCount( featGeometry )
|
|
newGeometry = featGeometry.simplify( self.tolerance )
|
|
pointsAfter += geomVertexCount( newGeometry )
|
|
|
|
feature = QgsFeature()
|
|
feature.setGeometry( newGeometry )
|
|
feature.setAttributes( attrMap )
|
|
shapeFileWriter.addFeature( feature )
|
|
featureId += 1
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else: # modify existing shapefile
|
|
if not self.inputLayer.isEditable():
|
|
self.inputLayer.startEditing()
|
|
self.inputLayer.beginEditCommand( "Simplify line(s)" )
|
|
if self.useSelection:
|
|
selection = self.inputLayer.selectedFeatures()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), len( selection ) )
|
|
for f in selection:
|
|
featureId = f.id()
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
|
|
pointsBefore += geomVertexCount( featGeometry )
|
|
newGeometry = featGeometry.simplify( self.tolerance )
|
|
pointsAfter += geomVertexCount( newGeometry )
|
|
|
|
self.inputLayer.changeGeometry( featureId, newGeometry )
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else:
|
|
vProvider = self.inputLayer.dataProvider()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), vProvider.featureCount() )
|
|
f = QgsFeature()
|
|
fit = vProvider.getFeatures()
|
|
while fit.nextFeature( f ):
|
|
featureId = f.id()
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
|
|
pointsBefore += geomVertexCount( featGeometry )
|
|
newGeometry = featGeometry.simplify( self.tolerance )
|
|
pointsAfter += geomVertexCount( newGeometry )
|
|
|
|
self.inputLayer.changeGeometry( featureId, newGeometry )
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
|
|
# cleanup
|
|
if self.inputLayer.isEditable():
|
|
self.inputLayer.endEditCommand()
|
|
|
|
if shapeFileWriter is not None:
|
|
del shapeFileWriter
|
|
|
|
if not interrupted:
|
|
self.emit( SIGNAL( "processingFinished( PyQt_PyObject )" ), ( pointsBefore, pointsAfter ) )
|
|
else:
|
|
self.emit( SIGNAL( "processingInterrupted()" ) )
|
|
|
|
def runDensify( self ):
|
|
self.mutex.lock()
|
|
self.stopMe = 0
|
|
self.mutex.unlock()
|
|
|
|
interrupted = False
|
|
|
|
shapeFileWriter = None
|
|
|
|
isPolygon = self.inputLayer.geometryType() == QGis.Polygon
|
|
|
|
if self.writeShape:
|
|
# prepare writer
|
|
vProvider = self.inputLayer.dataProvider()
|
|
shapeFields = vProvider.fields()
|
|
crs = vProvider.crs()
|
|
wkbType = self.inputLayer.wkbType()
|
|
if not crs.isValid():
|
|
crs = None
|
|
shapeFileWriter = QgsVectorFileWriter( self.outputFileName, self.outputEncoding, shapeFields, wkbType, crs )
|
|
featureId = 0
|
|
|
|
if self.useSelection:
|
|
selection = self.inputLayer.selectedFeatures()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), len( selection ) )
|
|
for f in selection:
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
attrMap = f.attributes()
|
|
newGeometry = densifyGeometry( featGeometry, int( self.tolerance ), isPolygon )
|
|
|
|
feature = QgsFeature()
|
|
feature.setGeometry( newGeometry )
|
|
feature.setAttributes( attrMap )
|
|
shapeFileWriter.addFeature( feature )
|
|
featureId += 1
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else:
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), vProvider.featureCount() )
|
|
f = QgsFeature()
|
|
fit = vProvider.getFeatures()
|
|
while fit.nextFeature( f ):
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
attrMap = f.attributes()
|
|
newGeometry = densifyGeometry( featGeometry, int( self.tolerance ), isPolygon )
|
|
|
|
feature = QgsFeature()
|
|
feature.setGeometry( newGeometry )
|
|
feature.setAttributes( attrMap )
|
|
shapeFileWriter.addFeature( feature )
|
|
featureId += 1
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else: # modify existing shapefile
|
|
if not self.inputLayer.isEditable():
|
|
self.inputLayer.startEditing()
|
|
|
|
self.inputLayer.beginEditCommand( "Densify line(s)" )
|
|
|
|
if self.useSelection:
|
|
selection = self.inputLayer.selectedFeatures()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), len( selection ) )
|
|
for f in selection:
|
|
featureId = f.id()
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
newGeometry = densifyGeometry( featGeometry, int( self.tolerance ), isPolygon )
|
|
|
|
self.inputLayer.changeGeometry( featureId, newGeometry )
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
else:
|
|
vProvider = self.inputLayer.dataProvider()
|
|
self.emit( SIGNAL( "rangeCalculated( PyQt_PyObject )" ), vProvider.featureCount() )
|
|
f = QgsFeature()
|
|
fit = vProvider.getFeatures()
|
|
while fit.nextFeature( f ):
|
|
featureId = f.id()
|
|
featGeometry = QgsGeometry( f.geometry() )
|
|
newGeometry = densifyGeometry( featGeometry, int( self.tolerance ), isPolygon )
|
|
|
|
self.inputLayer.changeGeometry( featureId, newGeometry )
|
|
self.emit( SIGNAL( "featureProcessed()" ) )
|
|
|
|
self.mutex.lock()
|
|
s = self.stopMe
|
|
self.mutex.unlock()
|
|
if s == 1:
|
|
interrupted = True
|
|
break
|
|
|
|
# cleanup
|
|
if self.inputLayer.isEditable():
|
|
self.inputLayer.endEditCommand()
|
|
|
|
if shapeFileWriter is not None:
|
|
del shapeFileWriter
|
|
|
|
if not interrupted:
|
|
self.emit( SIGNAL( "processingFinished( PyQt_PyObject )" ), None )
|
|
else:
|
|
self.emit( SIGNAL( "processingInterrupted()" ) )
|
|
|
|
def stop( self ):
|
|
self.mutex.lock()
|
|
self.stopMe = 1
|
|
self.mutex.unlock()
|
|
|
|
QThread.wait( self )
|