QGIS/python/plugins/fTools/tools/doEliminate.py

266 lines
11 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
#-----------------------------------------------------------
#
# Eliminate for fTools
# Copyright (C) 2011 Bernhard Ströbl
# EMAIL: bernhard.stroebl@jena.de
#
# Eliminate sliver polygons
#
#-----------------------------------------------------------
#
# 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 import QtCore, QtGui
from qgis.core import *
import ftools_utils
from ui_frmEliminate import Ui_Dialog
class Dialog(QtGui.QDialog, Ui_Dialog):
def __init__(self, iface):
QtGui.QDialog.__init__(self)
self.iface = iface
# Set up the user interface from Designer.
self.setupUi(self)
QtCore.QObject.connect(self.toolOut, QtCore.SIGNAL("clicked()"), self.outFile)
QtCore.QObject.connect(self.inShape, QtCore.SIGNAL("currentIndexChanged(QString)"), self.update)
QtCore.QObject.connect(self.writeShapefileCheck, QtCore.SIGNAL("stateChanged(int)"), self.on_writeShapefileCheck_stateChanged)
self.setWindowTitle(self.tr("Eliminate sliver polygons"))
self.buttonOk = self.buttonBox_2.button(QtGui.QDialogButtonBox.Ok)
# populate layer list
self.progressBar.setValue(0)
self.area.setChecked(True)
layers = ftools_utils.getLayerNames([QGis.Polygon])
self.inShape.addItems(layers)
if len(layers) > 0:
self.update(layers[0])
self.on_writeShapefileCheck_stateChanged(self.writeShapefileCheck.checkState())
def update(self, inputLayer):
changedLayer = ftools_utils.getVectorLayerByName(inputLayer)
selFeatures = changedLayer.selectedFeatureCount()
self.selected.setText( self.tr("Selected features: %1").arg(selFeatures))
def on_writeShapefileCheck_stateChanged(self, newState):
doEnable = (newState == 2)
self.outShape.setEnabled(doEnable)
self.toolOut.setEnabled(doEnable)
self.addToCanvasCheck.setEnabled(doEnable)
def accept(self):
self.buttonOk.setEnabled(False)
if self.inShape.currentText() == "":
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("No input shapefile specified"))
else:
if self.writeShapefileCheck.isChecked():
outFileName = self.outShape.text()
if outFileName == "":
QtGui.MessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
else:
outFile = QtCore.QFile(outFileName)
if outFile.exists():
if not QgsVectorFileWriter.deleteShapeFile(outFileName):
QtGui.QMessageBox.warning(self, self.tr("Delete error"),
self.tr("Can't delete file %1").arg(outFileName))
return
outFileName = unicode(outFileName)
else:
outFileName = None
inLayer = ftools_utils.getVectorLayerByName(unicode(self.inShape.currentText()))
if inLayer.selectedFeatureCount() == 0:
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("No selection in input layer"))
else:
self.progressBar.setValue(5)
boundary = self.boundary.isChecked()
self.eliminate(inLayer, boundary, self.progressBar, outFileName)
self.progressBar.setValue(100)
self.outShape.clear()
self.progressBar.setValue(0)
self.buttonOk.setEnabled(True)
def outFile(self):
self.outShape.clear()
(outFileName, self.encoding) = ftools_utils.saveDialog(self)
if outFileName is None or self.encoding is None:
return
self.outShape.setText(outFileName)
def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
# keep references to the features to eliminate
fidsToEliminate = inLayer.selectedFeaturesIds()
fidsToProcess = inLayer.selectedFeaturesIds()
if outFileName: # user wants a new shape file to be created as result
provider = inLayer.dataProvider()
error = QgsVectorFileWriter.writeAsVectorFormat(inLayer, outFileName, provider.encoding(), inLayer.crs(), "ESRI Shapefile")
if error != QgsVectorFileWriter.NoError:
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Error creating output file"))
return None
outLayer = QgsVectorLayer(outFileName, QtCore.QFileInfo(outFileName).completeBaseName(), "ogr")
else:
outLayer = inLayer
outLayer.removeSelection(False)
outLayer.startEditing()
doCommit = True
# ANALYZE
start = 20.00
progressBar.setValue(start)
add = 80.00 / len(fidsToEliminate)
lastLen = 0
geomsToMerge = dict()
# we go through the list and see if we find any polygons we can merge the selected with
# if we have no success with some we merge and then restart the whole story
while (lastLen != len(fidsToProcess)): #check if we made any progress
lastLen = len(fidsToProcess)
fidsNotEliminated = []
fidsToDelete = []
#iterate over the polygons to eliminate
for fid in fidsToProcess:
feat = QgsFeature()
if outLayer.featureAtId(fid, feat, True, False):
geom = feat.geometry()
bbox = geom.boundingBox()
outLayer.select(bbox, False) # make a new selection
mergeWithFid = None
mergeWithGeom = None
max = 0
for selFid in outLayer.selectedFeaturesIds():
if fid != selFid:
#check if this feature is to be eliminated, too
try:
found = fidsToEliminate.index(selFid)
except ValueError: #selFid is not in fidsToEliminate
# check wether the geometry to eliminate and the other geometry intersect
selFeat = QgsFeature()
if outLayer.featureAtId(selFid, selFeat, True, False):
selGeom = selFeat.geometry()
if geom.intersects(selGeom): # we have a candidate
iGeom = geom.intersection(selGeom)
if boundary:
selValue = iGeom.length()
else:
# we need a common boundary
if 0 < iGeom.length():
selValue = selGeom.area()
else:
selValue = 0
if selValue > max:
max = selValue
mergeWithFid = selFid
mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry
if mergeWithFid: # a successful candidate
try:
geomList = geomsToMerge[mergeWithFid]
except KeyError:
geomList = [mergeWithGeom]
geomList.append(QgsGeometry(geom)) # deep copy of the geom
geomsToMerge[mergeWithFid] = geomList
fidsToDelete.append(fid)
start = start + add
progressBar.setValue(start)
else:
fidsNotEliminated.append(fid)
# PROCESS
for aFid in geomsToMerge.iterkeys():
geomList = geomsToMerge[aFid]
if len(geomList) > 1:
for i in range(len(geomList)):
aGeom = geomList[i]
if i == 0:
newGeom = aGeom
else:
newGeom = newGeom.combine(aGeom)
# replace geometry in outLayer
if not outLayer.changeGeometry(aFid, newGeom):
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
self.tr("Could not replace geometry of feature with id ") + str(aFid))
doCommit = False
break
# delete eliminated features
for aFid in fidsToDelete:
if not outLayer.deleteFeature(aFid):
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
self.tr("Could not delete feature with id ") + str(aFid))
doCommit = False
break
# prepare array for the next loop
fidsToProcess = fidsNotEliminated
# SAVE CHANGES
if doCommit:
if not outLayer.commitChanges():
QtGui.QMessageBox.warning(self, self.tr("Commit error"), self.tr("Commit Error"))
else:
outLayer.rollBack()
if outFileName:
if self.addToCanvasCheck.isChecked():
ftools_utils.addShapeToCanvas(outFileName)
else:
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
self.tr("Created output shapefile:\n%1").arg(outFileName))
# inform user
if len(fidsNotEliminated) > 0:
fidList = QtCore.QString()
for fid in fidsNotEliminated:
if not fidList.isEmpty():
fidList.append(", ")
fidList.append(str(fid))
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
self.tr("Could not eliminate features with these ids:\n%1").arg(fidList))
self.iface.mapCanvas().refresh()