Skip to content

Commit b6ca151

Browse files
committedSep 26, 2013
Merge pull request #911 from bstroebl/testEliminate
[processing] select features to eliminate in algorithm
2 parents 7a720c2 + e5a7210 commit b6ca151

File tree

1 file changed

+197
-128
lines changed

1 file changed

+197
-128
lines changed
 

‎python/plugins/processing/algs/ftools/Eliminate.py

Lines changed: 197 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818
"""
1919
from processing.parameters.ParameterSelection import ParameterSelection
2020
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
21-
from PyQt4 import QtCore
2221

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

@@ -31,161 +30,231 @@
3130
from processing.core.GeoAlgorithm import GeoAlgorithm
3231
from processing.tools import dataobjects
3332
from processing.parameters.ParameterVector import ParameterVector
33+
from processing.parameters.ParameterBoolean import ParameterBoolean
34+
from processing.parameters.ParameterTableField import ParameterTableField
35+
from processing.parameters.ParameterString import ParameterString
3436
from processing.outputs.OutputVector import OutputVector
35-
37+
from processing.core.ProcessingLog import ProcessingLog
3638

3739
class Eliminate(GeoAlgorithm):
3840

3941
INPUT = "INPUT"
4042
OUTPUT = "OUTPUT"
4143
MODE = "MODE"
42-
44+
KEEPSELECTION = "KEEPSELECTION"
45+
ATTRIBUTE = "ATTRIBUTE"
46+
COMPARISONVALUE = "COMPARISONVALUE"
47+
COMPARISON = "COMPARISON"
48+
4349
MODES = ["Area", "Common boundary"]
4450
MODE_AREA = 0
4551
MODE_BOUNDARY = 1
46-
4752

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

5565
def processAlgorithm(self, progress):
56-
inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))
57-
boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY
66+
inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))
67+
boundary = self.getParameterValue(self.MODE) == self.MODE_BOUNDARY
68+
keepSelection = self.getParameterValue(self.KEEPSELECTION)
69+
70+
if not keepSelection:
71+
# make a selection with the values provided
72+
attribute = self.getParameterValue(self.ATTRIBUTE)
73+
comparison = self.comparisons [self.getParameterValue(self.COMPARISON)]
74+
comparisonvalue = self.getParameterValue(self.COMPARISONVALUE)
75+
76+
selectindex = inLayer.dataProvider().fieldNameIndex(attribute)
77+
selectType = inLayer.dataProvider().fields()[selectindex].type()
78+
selectionError = False
79+
80+
if selectType == 2:
81+
try:
82+
y = int(comparisonvalue)
83+
except ValueError:
84+
selectionError = True
85+
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to integer"
86+
elif selectType == 6:
87+
try:
88+
y = float(comparisonvalue)
89+
except ValueError:
90+
selectionError = True
91+
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to float"
92+
elif selectType == 10: # 10: string, boolean
93+
try:
94+
y = unicode(comparisonvalue)
95+
except ValueError:
96+
selectionError = True
97+
msg = "Cannot convert \"" + unicode(comparisonvalue) + "\" to unicode"
98+
elif selectType == 14: # date
99+
dateAndFormat = comparisonvalue.split(" ")
100+
101+
if len(dateAndFormat) == 1:
102+
y = QLocale.system().toDate(dateAndFormat[0]) # QtCore.QDate object
103+
104+
if y.isNull():
105+
msg = "Cannot convert \"" + unicode(dateAndFormat) + "\" to date with system date format " + QLocale.system().dateFormat()
106+
elif len(dateAndFormat) == 2:
107+
y = QDate.fromString(dateAndFormat[0], dateAndFormat[1])
108+
109+
if y.isNull():
110+
msg = "Cannot convert \"" + unicode(dateAndFormat[0]) + "\" to date with format string \"" + unicode(dateAndFormat[1] + "\". ")
111+
else:
112+
y = QDate()
113+
msg = ""
114+
115+
if y.isNull(): # conversion was unsuccessfull
116+
selectionError = True
117+
msg += "Enter the date and the date format, e.g. \"07.26.2011\" \"MM.dd.yyyy\"."
118+
119+
if ((comparison == 'begins with') or (comparison == 'contains')) and selectType != 10:
120+
selectionError = True
121+
msg = "\"" + comparison + "\" can only be used with string fields"
122+
123+
selected = []
124+
125+
if selectionError:
126+
raise GeoAlgorithmExecutionException("Error in selection input: " + msg)
127+
else:
128+
for feature in inLayer.getFeatures():
129+
aValue = feature.attributes()[selectindex]
130+
131+
if aValue == None:
132+
continue
133+
134+
if selectType == 2:
135+
x = int(aValue)
136+
elif selectType == 6:
137+
x = float(aValue)
138+
elif selectType == 10: # 10: string, boolean
139+
x = unicode(aValue)
140+
elif selectType == 14: # date
141+
x = aValue # should be date
142+
143+
match = False
144+
145+
if (comparison == '=='):
146+
match = (x == y)
147+
elif (comparison == '!='):
148+
match = (x != y)
149+
elif (comparison == '>'):
150+
match = (x > y)
151+
elif (comparison == '>='):
152+
match = (x >= y)
153+
elif (comparison == '<'):
154+
match = (x < y)
155+
elif (comparison == '<='):
156+
match = (x <= y)
157+
elif (comparison == 'begins with'):
158+
match = x.startswith(y)
159+
elif (comparison == 'contains'):
160+
match = (x.find(y) >= 0)
161+
162+
if (match):
163+
selected.append(feature.id())
164+
165+
inLayer.setSelectedFeatures(selected)
58166

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

171+
# keep references to the features to eliminate
172+
featToEliminate = []
173+
for aFeat in inLayer.selectedFeatures():
174+
featToEliminate.append( aFeat )
62175

63-
provider = inLayer.dataProvider()
64-
output = self.getOutputFromName(self.OUTPUT)
65-
writer = output.getVectorWriter( provider.fields(),
66-
provider.geometryType(),
67-
inLayer.crs() )
68-
69-
#write all features to output layer
70-
iterator = inLayer.getFeatures()
71-
for feature in iterator:
72-
writer.addFeature(feature)
73-
74-
#Open the layer to start cleaning it
75-
outFileName = output.value
76-
outLayer = QgsVectorLayer(outFileName, QtCore.QFileInfo(outFileName).completeBaseName(), "ogr")
77-
78-
79-
# delete features to be eliminated in outLayer
80-
outLayer.setSelectedFeatures(fidsToEliminate)
81-
outLayer.startEditing()
82-
83-
if outLayer.deleteSelectedFeatures():
84-
if self.saveChanges(outLayer):
85-
outLayer.startEditing()
86-
else:
87-
raise GeoAlgorithmExecutionException("Could not delete features")
176+
#delete all features to eliminate in inLayer (we won't save this)
177+
inLayer.startEditing()
178+
inLayer.deleteSelectedFeatures()
88179

89180
# ANALYZE
90-
start = 20.00
91-
progress.setPercentage(start)
92-
add = 80.00 / len(fidsToEliminate)
181+
if len( featToEliminate ) > 0: # prevent zero division
182+
start = 20.00
183+
add = 80.00 / len( featToEliminate )
184+
else:
185+
start = 100
93186

94-
lastLen = 0
95-
geomsToMerge = dict()
187+
progress.setPercentage( start )
188+
madeProgress = True
96189

97190
# we go through the list and see if we find any polygons we can merge the selected with
98191
# if we have no success with some we merge and then restart the whole story
99-
while (lastLen != inLayer.selectedFeatureCount()): #check if we made any progress
100-
lastLen = inLayer.selectedFeatureCount()
101-
fidsToDeselect = []
192+
while ( madeProgress ): #check if we made any progress
193+
madeProgress = False
194+
featNotEliminated = []
102195

103196
#iterate over the polygons to eliminate
104-
for fid2Eliminate in inLayer.selectedFeaturesIds():
105-
feat = QgsFeature()
106-
107-
if inLayer.getFeatures( QgsFeatureRequest().setFilterFid( fid2Eliminate ).setSubsetOfAttributes([]) ).nextFeature( feat ):
108-
geom2Eliminate = feat.geometry()
109-
bbox = geom2Eliminate.boundingBox()
110-
fit = outLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox ) )
111-
mergeWithFid = None
112-
mergeWithGeom = None
113-
max = 0
114-
115-
selFeat = QgsFeature()
116-
while fit.nextFeature(selFeat):
117-
selGeom = selFeat.geometry()
118-
119-
if geom2Eliminate.intersects(selGeom): # we have a candidate
120-
iGeom = geom2Eliminate.intersection(selGeom)
121-
122-
if boundary:
123-
selValue = iGeom.length()
124-
else:
125-
# we need a common boundary
126-
if 0 < iGeom.length():
127-
selValue = selGeom.area()
128-
else:
129-
selValue = 0
130-
131-
if selValue > max:
132-
max = selValue
133-
mergeWithFid = selFeat.id()
134-
mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry
135-
136-
if mergeWithFid != None: # a successful candidate
137-
newGeom = mergeWithGeom.combine(geom2Eliminate)
138-
139-
if outLayer.changeGeometry(mergeWithFid, newGeom):
140-
# write change back to disc
141-
if self.saveChanges(outLayer):
142-
outLayer.startEditing()
197+
for i in range( len( featToEliminate )):
198+
feat = featToEliminate.pop()
199+
geom2Eliminate = feat.geometry()
200+
bbox = geom2Eliminate.boundingBox()
201+
fit = inLayer.getFeatures( QgsFeatureRequest().setFilterRect( bbox ) )
202+
mergeWithFid = None
203+
mergeWithGeom = None
204+
max = 0
205+
selFeat = QgsFeature()
206+
207+
while fit.nextFeature( selFeat ):
208+
selGeom = selFeat.geometry()
209+
210+
if geom2Eliminate.intersects( selGeom ): # we have a candidate
211+
iGeom = geom2Eliminate.intersection( selGeom )
212+
213+
if boundary:
214+
selValue = iGeom.length()
215+
else: # largest area
216+
# we need a common boundary in order to merge
217+
if 0 < iGeom.length():
218+
selValue = selGeom.area()
143219
else:
144-
return
145-
146-
# mark feature as eliminated in inLayer
147-
fidsToDeselect.append(fid2Eliminate)
148-
else:
149-
raise GeoAlgorithmExecutionException("Could not replace geometry of feature with id %s" % (mergeWithFid))
150-
151-
start = start + add
152-
progress.setPercentage(start)
153-
# end for fid2Eliminate
154-
155-
# deselect features that are already eliminated in inLayer
156-
inLayer.deselect(fidsToDeselect)
157-
220+
selValue = 0
221+
222+
if selValue > max:
223+
max = selValue
224+
mergeWithFid = selFeat.id()
225+
mergeWithGeom = QgsGeometry( selGeom ) # deep copy of the geometry
226+
# end while fit
227+
228+
if mergeWithFid != None: # a successful candidate
229+
newGeom = mergeWithGeom.combine( geom2Eliminate )
230+
231+
if inLayer.changeGeometry( mergeWithFid, newGeom ):
232+
madeProgress = True
233+
else:
234+
raise GeoAlgorithmExecutionException("Could not replace geometry of feature with id %s" % (mergeWithFid))
235+
236+
start = start + add
237+
progress.setPercentage( start )
238+
else:
239+
featNotEliminated.append( feat )
240+
# end for featToEliminate
241+
featToEliminate = featNotEliminated
158242
#end while
159243

160-
if inLayer.selectedFeatureCount() > 0:
161-
# copy all features that could not be eliminated to outLayer
162-
if outLayer.addFeatures(inLayer.selectedFeatures()):
163-
# inform user
164-
fidList = ""
244+
# create output
245+
provider = inLayer.dataProvider()
246+
output = self.getOutputFromName( self.OUTPUT )
247+
writer = output.getVectorWriter( provider.fields(),
248+
provider.geometryType(),
249+
inLayer.crs() )
165250

166-
for fid in inLayer.selectedFeaturesIds():
167-
if not fidList == "":
168-
fidList += ", "
251+
#write all features that are left over to output layer
252+
iterator = inLayer.getFeatures()
253+
for feature in iterator:
254+
writer.addFeature( feature )
169255

170-
fidList += str(fid)
256+
# leave inLayer untouched
257+
inLayer.rollBack()
171258

172-
raise GeoAlgorithmExecutionException("Could not eliminate features with these ids:\n%s" % (fidList))
173-
else:
174-
raise GeoAlgorithmExecutionException("Could not add features")
175-
176-
# stop editing outLayer and commit any pending changes
177-
self.saveChanges(outLayer)
178-
179-
def saveChanges(self, outLayer):
180-
if not outLayer.commitChanges():
181-
msg = ""
182-
for aStrm in outLayer.commitErrors():
183-
msg = msg + "\n" + aStrm
184-
outLayer.rollBack()
185-
raise GeoAlgorithmExecutionException("Commit error:\n%s" % (msg))
186-
187-
def checkParameterValuesBeforeExecuting(self):
188-
inLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT))
189-
if inLayer.selectedFeatureCount() == 0:
190-
return "No selection in input layer"
191-
259+
for feature in featNotEliminated:
260+
writer.addFeature( feature )

0 commit comments

Comments
 (0)
Please sign in to comment.