Skip to content

Commit

Permalink
Merge pull request #911 from bstroebl/testEliminate
Browse files Browse the repository at this point in the history
[processing] select features to eliminate in algorithm
  • Loading branch information
volaya committed Sep 26, 2013
2 parents 7a720c2 + e5a7210 commit b6ca151
Showing 1 changed file with 197 additions and 128 deletions.
325 changes: 197 additions & 128 deletions python/plugins/processing/algs/ftools/Eliminate.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
""" """
from processing.parameters.ParameterSelection import ParameterSelection from processing.parameters.ParameterSelection import ParameterSelection
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from PyQt4 import QtCore


__author__ = 'Bernhard Strobl' __author__ = 'Bernhard Ströbl'
__date__ = 'August 2013' __date__ = 'September 2013'
__copyright__ = '(C) 2013, Bernhard Strobl' __copyright__ = '(C) 2013, Bernhard Ströbl'
# This will get replaced with a git SHA1 when you do a git archive # This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$' __revision__ = '$Format:%H$'


Expand All @@ -31,161 +30,231 @@
from processing.core.GeoAlgorithm import GeoAlgorithm from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.tools import dataobjects from processing.tools import dataobjects
from processing.parameters.ParameterVector import ParameterVector from processing.parameters.ParameterVector import ParameterVector
from processing.parameters.ParameterBoolean import ParameterBoolean
from processing.parameters.ParameterTableField import ParameterTableField
from processing.parameters.ParameterString import ParameterString
from processing.outputs.OutputVector import OutputVector from processing.outputs.OutputVector import OutputVector

from processing.core.ProcessingLog import ProcessingLog


class Eliminate(GeoAlgorithm): class Eliminate(GeoAlgorithm):


INPUT = "INPUT" INPUT = "INPUT"
OUTPUT = "OUTPUT" OUTPUT = "OUTPUT"
MODE = "MODE" MODE = "MODE"

KEEPSELECTION = "KEEPSELECTION"
ATTRIBUTE = "ATTRIBUTE"
COMPARISONVALUE = "COMPARISONVALUE"
COMPARISON = "COMPARISON"

MODES = ["Area", "Common boundary"] MODES = ["Area", "Common boundary"]
MODE_AREA = 0 MODE_AREA = 0
MODE_BOUNDARY = 1 MODE_BOUNDARY = 1



def defineCharacteristics(self): def defineCharacteristics(self):
self.name = "Eliminate sliver polygons" self.name = "Eliminate sliver polygons"
self.group = "Vector geometry tools" self.group = "Vector geometry tools"
self.addParameter(ParameterVector(self.INPUT, "Input layer", [ParameterVector.VECTOR_TYPE_POLYGON])) self.addParameter(ParameterVector(self.INPUT, "Input layer", [ParameterVector.VECTOR_TYPE_POLYGON]))
self.addParameter(ParameterSelection(self.MODE, "Merge selection with the neighbouring polygon with the largest", self.MODES)) self.addParameter(ParameterBoolean(self.KEEPSELECTION, "Use current selection in input layer (works only if called from toolbox)", False))
self.addParameter(ParameterTableField(self.ATTRIBUTE, "Selection attribute", self.INPUT))
self.comparisons = ['==', '!=', '>', '>=', '<', '<=', 'begins with', 'contains']
self.addParameter(ParameterSelection(self.COMPARISON, "Comparison", self.comparisons, default = 0))
self.addParameter(ParameterString(self.COMPARISONVALUE, "Value", default = "0"))
self.addParameter(ParameterSelection(self.MODE, "Merge selection with the neighbouring polygon with the largest", self.MODES))
self.addOutput(OutputVector(self.OUTPUT, "Cleaned layer")) self.addOutput(OutputVector(self.OUTPUT, "Cleaned layer"))


def processAlgorithm(self, progress): def processAlgorithm(self, progress):
inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT)) inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))
boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY
keepSelection = self.getParameterValue(self.KEEPSELECTION)

if not keepSelection:
# make a selection with the values provided
attribute = self.getParameterValue(self.ATTRIBUTE)
comparison = self.comparisons [self.getParameterValue(self.COMPARISON)]
comparisonvalue = self.getParameterValue(self.COMPARISONVALUE)

selectindex = inLayer.dataProvider().fieldNameIndex(attribute)
selectType = inLayer.dataProvider().fields()[selectindex].type()
selectionError = False

if selectType == 2:
try:
y = int(comparisonvalue)
except ValueError:
selectionError = True
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to integer"
elif selectType == 6:
try:
y = float(comparisonvalue)
except ValueError:
selectionError = True
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to float"
elif selectType == 10: # 10: string, boolean
try:
y = unicode(comparisonvalue)
except ValueError:
selectionError = True
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to unicode"
elif selectType == 14: # date
dateAndFormat = comparisonvalue.split(" ")

if len(dateAndFormat) == 1:
y = QLocale.system().toDate(dateAndFormat[0]) # QtCore.QDate object

if y.isNull():
msg = "Cannot convert \"" + unicode(dateAndFormat) + "\" to date with system date format " + QLocale.system().dateFormat()
elif len(dateAndFormat) == 2:
y = QDate.fromString(dateAndFormat[0], dateAndFormat[1])

if y.isNull():
msg = "Cannot convert \"" + unicode(dateAndFormat[0]) + "\" to date with format string \"" + unicode(dateAndFormat[1] + "\". ")
else:
y = QDate()
msg = ""

if y.isNull(): # conversion was unsuccessfull
selectionError = True
msg += "Enter the date and the date format, e.g. \"07.26.2011\" \"MM.dd.yyyy\"."

if ((comparison == 'begins with') or (comparison == 'contains')) and selectType != 10:
selectionError = True
msg = "\"" + comparison + "\" can only be used with string fields"

selected = []

if selectionError:
raise GeoAlgorithmExecutionException("Error in selection input: " + msg)
else:
for feature in inLayer.getFeatures():
aValue = feature.attributes()[selectindex]

if aValue == None:
continue

if selectType == 2:
x = int(aValue)
elif selectType == 6:
x = float(aValue)
elif selectType == 10: # 10: string, boolean
x = unicode(aValue)
elif selectType == 14: # date
x = aValue # should be date

match = False

if (comparison == '=='):
match = (x == y)
elif (comparison == '!='):
match = (x != y)
elif (comparison == '>'):
match = (x > y)
elif (comparison == '>='):
match = (x >= y)
elif (comparison == '<'):
match = (x < y)
elif (comparison == '<='):
match = (x <= y)
elif (comparison == 'begins with'):
match = x.startswith(y)
elif (comparison == 'contains'):
match = (x.find(y) >= 0)

if (match):
selected.append(feature.id())

inLayer.setSelectedFeatures(selected)


# keep references to the features to eliminate if inLayer.selectedFeatureCount() == 0:
fidsToEliminate = inLayer.selectedFeaturesIds() ProcessingLog.addToLog(ProcessingLog.LOG_WARNING, self.commandLineName() + "(No selection in input layer \"" + self.getParameterValue(self.INPUT) + "\")")
#self.iface.messageBar().pushMessage("Eliminate", "No selection provided")


# keep references to the features to eliminate
featToEliminate = []
for aFeat in inLayer.selectedFeatures():
featToEliminate.append( aFeat )


provider = inLayer.dataProvider() #delete all features to eliminate in inLayer (we won't save this)
output = self.getOutputFromName(self.OUTPUT) inLayer.startEditing()
writer = output.getVectorWriter( provider.fields(), inLayer.deleteSelectedFeatures()
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 # ANALYZE
start = 20.00 if len( featToEliminate ) > 0: # prevent zero division
progress.setPercentage(start) start = 20.00
add = 80.00 / len(fidsToEliminate) add = 80.00 / len( featToEliminate )
else:
start = 100


lastLen = 0 progress.setPercentage( start )
geomsToMerge = dict() madeProgress = True


# we go through the list and see if we find any polygons we can merge the selected with # 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 # 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 while ( madeProgress ): #check if we made any progress
lastLen = inLayer.selectedFeatureCount() madeProgress = False
fidsToDeselect = [] featNotEliminated = []


#iterate over the polygons to eliminate #iterate over the polygons to eliminate
for fid2Eliminate in inLayer.selectedFeaturesIds(): for i in range( len( featToEliminate )):
feat = QgsFeature() feat = featToEliminate.pop()

geom2Eliminate = feat.geometry()
if inLayer.getFeatures( QgsFeatureRequest().setFilterFid( fid2Eliminate ).setSubsetOfAttributes([]) ).nextFeature( feat ): bbox = geom2Eliminate.boundingBox()
geom2Eliminate = feat.geometry() fit = inLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox ) )
bbox = geom2Eliminate.boundingBox() mergeWithFid = None
fit = outLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox ) ) mergeWithGeom = None
mergeWithFid = None max = 0
mergeWithGeom = None selFeat = QgsFeature()
max = 0

while fit.nextFeature( selFeat ):
selFeat = QgsFeature() selGeom = selFeat.geometry()
while fit.nextFeature(selFeat):
selGeom = selFeat.geometry() if geom2Eliminate.intersects( selGeom ): # we have a candidate

iGeom = geom2Eliminate.intersection( selGeom )
if geom2Eliminate.intersects(selGeom): # we have a candidate
iGeom = geom2Eliminate.intersection(selGeom) if boundary:

selValue = iGeom.length()
if boundary: else: # largest area
selValue = iGeom.length() # we need a common boundary in order to merge
else: if 0 < iGeom.length():
# we need a common boundary selValue = selGeom.area()
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: else:
return selValue = 0


# mark feature as eliminated in inLayer if selValue > max:
fidsToDeselect.append(fid2Eliminate) max = selValue
else: mergeWithFid = selFeat.id()
raise GeoAlgorithmExecutionException("Could not replace geometry of feature with id %s" % (mergeWithFid)) mergeWithGeom = QgsGeometry( selGeom ) # deep copy of the geometry

# end while fit
start = start + add
progress.setPercentage(start) if mergeWithFid != None: # a successful candidate
# end for fid2Eliminate newGeom = mergeWithGeom.combine( geom2Eliminate )


# deselect features that are already eliminated in inLayer if inLayer.changeGeometry( mergeWithFid, newGeom ):
inLayer.deselect(fidsToDeselect) madeProgress = True

else:
raise GeoAlgorithmExecutionException("Could not replace geometry of feature with id %s" % (mergeWithFid))

start = start + add
progress.setPercentage( start )
else:
featNotEliminated.append( feat )
# end for featToEliminate
featToEliminate = featNotEliminated
#end while #end while


if inLayer.selectedFeatureCount() > 0: # create output
# copy all features that could not be eliminated to outLayer provider = inLayer.dataProvider()
if outLayer.addFeatures(inLayer.selectedFeatures()): output = self.getOutputFromName( self.OUTPUT )
# inform user writer = output.getVectorWriter( provider.fields(),
fidList = "" provider.geometryType(),
inLayer.crs() )


for fid in inLayer.selectedFeaturesIds(): #write all features that are left over to output layer
if not fidList == "": iterator = inLayer.getFeatures()
fidList += ", " for feature in iterator:
writer.addFeature( feature )


fidList += str(fid) # leave inLayer untouched
inLayer.rollBack()


raise GeoAlgorithmExecutionException("Could not eliminate features with these ids:\n%s" % (fidList)) for feature in featNotEliminated:
else: writer.addFeature( feature )
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))

def checkParameterValuesBeforeExecuting(self):
inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))
if inLayer.selectedFeatureCount() == 0:
return "No selection in input layer"

0 comments on commit b6ca151

Please sign in to comment.