# -*- coding: utf-8 -*- """ *************************************************************************** Eliminate.py --------------------- Date : August 2012 Copyright : (C) 2013 by Bernhard Str�bl Email : bernhard.stroebl@jena.de *************************************************************************** * * * 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 processing.parameters.ParameterSelection import ParameterSelection from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException from PyQt4 import QtCore __author__ = 'Bernhard Strobl' __date__ = 'August 2013' __copyright__ = '(C) 2013, Bernhard Strobl' # This will get replaced with a git SHA1 when you do a git archive __revision__ = '$Format:%H$' from PyQt4.QtCore import * from qgis.core import * from processing.core.GeoAlgorithm import GeoAlgorithm from processing.tools import dataobjects from processing.parameters.ParameterVector import ParameterVector from processing.outputs.OutputVector import OutputVector class Eliminate(GeoAlgorithm): INPUT = "INPUT" OUTPUT = "OUTPUT" MODE = "MODE" MODES = ["Area", "Common boundary"] MODE_AREA = 0 MODE_BOUNDARY = 1 def defineCharacteristics(self): self.name = "Eliminate sliver polygons" self.group = "Vector geometry tools" self.addParameter(ParameterVector(self.INPUT, "Input layer", [ParameterVector.VECTOR_TYPE_POLYGON])) self.addParameter(ParameterSelection(self.MODE, "Segments", self.MODES)) self.addOutput(OutputVector(self.OUTPUT, "Cleaned layer")) def processAlgorithm(self, progress): inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY # keep references to the features to eliminate fidsToEliminate = inLayer.selectedFeaturesIds() provider = inLayer.dataProvider() output = self.getOutputFromName(self.OUTPUT) writer = output.getVectorWriter( provider.fields(), provider.geometryType(), inLayer.crs() ) #write all features to output layer iterator = inLayer.getFeatures() for feature in iterator: writer.addFeature(feature) #Open the layer to start cleaning it outFileName = output.value outLayer = QgsVectorLayer(outFileName, QtCore.QFileInfo(outFileName).completeBaseName(), "ogr") # delete features to be eliminated in outLayer outLayer.setSelectedFeatures(fidsToEliminate) outLayer.startEditing() if outLayer.deleteSelectedFeatures(): if self.saveChanges(outLayer): outLayer.startEditing() else: raise GeoAlgorithmExecutionException("Could not delete features") # ANALYZE start = 20.00 progress.setPercentage(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 != inLayer.selectedFeatureCount()): #check if we made any progress lastLen = inLayer.selectedFeatureCount() fidsToDeselect = [] #iterate over the polygons to eliminate for fid2Eliminate in inLayer.selectedFeaturesIds(): feat = QgsFeature() if inLayer.getFeatures( QgsFeatureRequest().setFilterFid( fid2Eliminate ).setSubsetOfAttributes([]) ).nextFeature( feat ): geom2Eliminate = feat.geometry() bbox = geom2Eliminate.boundingBox() fit = outLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox ) ) mergeWithFid = None mergeWithGeom = None max = 0 selFeat = QgsFeature() while fit.nextFeature(selFeat): selGeom = selFeat.geometry() if geom2Eliminate.intersects(selGeom): # we have a candidate iGeom = geom2Eliminate.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 = selFeat.id() mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry if mergeWithFid != None: # a successful candidate newGeom = mergeWithGeom.combine(geom2Eliminate) if outLayer.changeGeometry(mergeWithFid, newGeom): # write change back to disc if self.saveChanges(outLayer): outLayer.startEditing() else: return # mark feature as eliminated in inLayer fidsToDeselect.append(fid2Eliminate) else: raise GeoAlgorithmExecutionException("Could not replace geometry of feature with id %s" % (mergeWithFid)) start = start + add progress.setPercentage(start) # end for fid2Eliminate # deselect features that are already eliminated in inLayer inLayer.deselect(fidsToDeselect) #end while if inLayer.selectedFeatureCount() > 0: # copy all features that could not be eliminated to outLayer if outLayer.addFeatures(inLayer.selectedFeatures()): # inform user fidList = "" for fid in inLayer.selectedFeaturesIds(): if not fidList == "": fidList += ", " fidList += str(fid) raise GeoAlgorithmExecutionException("Could not eliminate features with these ids:\n%s" % (fidList)) else: raise GeoAlgorithmExecutionException("Could not add features") # stop editing outLayer and commit any pending changes self.saveChanges(outLayer) def saveChanges(self, outLayer): if not outLayer.commitChanges(): msg = "" for aStrm in outLayer.commitErrors(): msg = msg + "\n" + aStrm outLayer.rollBack() raise GeoAlgorithmExecutionException("Commit error:\n%s" % (msg))