Skip to content

Commit 07772f1

Browse files
committedJan 22, 2013
fix output of the eliminate slivers polygon tool (fix 6925). Patch by
Bernhard Strobl
1 parent 181a739 commit 07772f1

File tree

2 files changed

+156
-168
lines changed

2 files changed

+156
-168
lines changed
 

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

Lines changed: 106 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ def __init__(self, iface):
4141
self.setupUi(self)
4242
QtCore.QObject.connect(self.toolOut, QtCore.SIGNAL("clicked()"), self.outFile)
4343
QtCore.QObject.connect(self.inShape, QtCore.SIGNAL("currentIndexChanged(QString)"), self.update)
44-
QtCore.QObject.connect(self.writeShapefileCheck, QtCore.SIGNAL("stateChanged(int)"), self.on_writeShapefileCheck_stateChanged)
4544
self.setWindowTitle(self.tr("Eliminate sliver polygons"))
4645
self.buttonOk = self.buttonBox_2.button(QtGui.QDialogButtonBox.Ok)
4746
# populate layer list
@@ -53,43 +52,34 @@ def __init__(self, iface):
5352
if len(layers) > 0:
5453
self.update(layers[0])
5554

56-
self.on_writeShapefileCheck_stateChanged(self.writeShapefileCheck.checkState())
57-
5855
def update(self, inputLayer):
5956
changedLayer = ftools_utils.getVectorLayerByName(inputLayer)
6057
selFeatures = changedLayer.selectedFeatureCount()
6158
self.selected.setText( self.tr("Selected features: %1").arg(selFeatures))
6259

63-
def on_writeShapefileCheck_stateChanged(self, newState):
64-
doEnable = (newState == 2)
65-
self.outShape.setEnabled(doEnable)
66-
self.toolOut.setEnabled(doEnable)
67-
self.addToCanvasCheck.setEnabled(doEnable)
68-
6960
def accept(self):
7061
self.buttonOk.setEnabled(False)
7162

7263
if self.inShape.currentText() == "":
7364
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("No input shapefile specified"))
7465
else:
66+
outFileName = self.outShape.text()
7567

76-
if self.writeShapefileCheck.isChecked():
77-
outFileName = self.outShape.text()
78-
79-
if outFileName == "":
80-
QtGui.MessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
81-
else:
82-
outFile = QtCore.QFile(outFileName)
68+
if outFileName == "":
69+
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
70+
self.buttonOk.setEnabled(True)
71+
return None
72+
else:
73+
outFile = QtCore.QFile(outFileName)
8374

84-
if outFile.exists():
85-
if not QgsVectorFileWriter.deleteShapeFile(outFileName):
86-
QtGui.QMessageBox.warning(self, self.tr("Delete error"),
87-
self.tr("Can't delete file %1").arg(outFileName))
88-
return
75+
if outFile.exists():
76+
if not QgsVectorFileWriter.deleteShapeFile(outFileName):
77+
QtGui.QMessageBox.warning(self, self.tr("Delete error"),
78+
self.tr("Can't delete file %1").arg(outFileName))
79+
self.buttonOk.setEnabled(True)
80+
return None
8981

90-
outFileName = unicode(outFileName)
91-
else:
92-
outFileName = None
82+
outFileName = unicode(outFileName)
9383

9484
inLayer = ftools_utils.getVectorLayerByName(unicode(self.inShape.currentText()))
9585

@@ -109,13 +99,23 @@ def outFile(self):
10999
self.outShape.clear()
110100
(outFileName, self.encoding) = ftools_utils.saveDialog(self)
111101
if outFileName is None or self.encoding is None:
112-
return
102+
return None
113103
self.outShape.setText(outFileName)
114104

115-
def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
105+
def saveChanges(self, outLayer):
106+
if outLayer.commitChanges():
107+
return True
108+
else:
109+
msg = ""
110+
for aStrm in outLayer.commitErrors():
111+
msg = msg + "\n" + aStrm
112+
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Commit error:\n %1").arg(msg))
113+
outLayer.rollBack()
114+
return False
115+
116+
def eliminate(self, inLayer, boundary, progressBar, outFileName):
116117
# keep references to the features to eliminate
117118
fidsToEliminate = inLayer.selectedFeaturesIds()
118-
fidsToProcess = inLayer.selectedFeaturesIds()
119119

120120
if outFileName: # user wants a new shape file to be created as result
121121
provider = inLayer.dataProvider()
@@ -128,12 +128,20 @@ def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
128128
outLayer = QgsVectorLayer(outFileName, QtCore.QFileInfo(outFileName).completeBaseName(), "ogr")
129129

130130
else:
131-
outLayer = inLayer
132-
outLayer.removeSelection(False)
131+
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
132+
return None
133133

134+
# delete features to be eliminated in outLayer
135+
outLayer.setSelectedFeatures(fidsToEliminate)
134136
outLayer.startEditing()
135-
doCommit = True
136137

138+
if outLayer.deleteSelectedFeatures():
139+
if self.saveChanges(outLayer):
140+
outLayer.startEditing()
141+
else:
142+
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Could not delete features"))
143+
return None
144+
137145
# ANALYZE
138146
start = 20.00
139147
progressBar.setValue(start)
@@ -144,103 +152,92 @@ def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
144152

145153
# we go through the list and see if we find any polygons we can merge the selected with
146154
# if we have no success with some we merge and then restart the whole story
147-
while (lastLen != len(fidsToProcess)): #check if we made any progress
148-
lastLen = len(fidsToProcess)
149-
fidsNotEliminated = []
150-
fidsToDelete = []
155+
while (lastLen != inLayer.selectedFeatureCount()): #check if we made any progress
156+
lastLen = inLayer.selectedFeatureCount()
157+
fidsToDeselect = []
151158

152159
#iterate over the polygons to eliminate
153-
for fid in fidsToProcess:
160+
for fid2Eliminate in inLayer.selectedFeaturesIds():
154161
feat = QgsFeature()
155162

156-
if outLayer.featureAtId(fid, feat, True, False):
157-
geom = feat.geometry()
158-
bbox = geom.boundingBox()
163+
if inLayer.featureAtId(fid2Eliminate, feat, True, False):
164+
geom2Eliminate = feat.geometry()
165+
bbox = geom2Eliminate.boundingBox()
159166
outLayer.select(bbox, False) # make a new selection
160167
mergeWithFid = None
161168
mergeWithGeom = None
162169
max = 0
163170

164171
for selFid in outLayer.selectedFeaturesIds():
165-
if fid != selFid:
166-
#check if this feature is to be eliminated, too
167-
try:
168-
found = fidsToEliminate.index(selFid)
169-
except ValueError: #selFid is not in fidsToEliminate
170-
# check whether the geometry to eliminate and the other geometry intersect
171-
selFeat = QgsFeature()
172-
173-
if outLayer.featureAtId(selFid, selFeat, True, False):
174-
selGeom = selFeat.geometry()
175-
176-
if geom.intersects(selGeom): # we have a candidate
177-
iGeom = geom.intersection(selGeom)
178-
179-
if boundary:
180-
selValue = iGeom.length()
181-
else:
182-
# we need a common boundary
183-
if 0 < iGeom.length():
184-
selValue = selGeom.area()
185-
else:
186-
selValue = 0
187-
188-
if selValue > max:
189-
max = selValue
190-
mergeWithFid = selFid
191-
mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry
192-
172+
selFeat = QgsFeature()
173+
174+
if outLayer.featureAtId(selFid, selFeat, True, False):
175+
selGeom = selFeat.geometry()
176+
177+
if geom2Eliminate.intersects(selGeom): # we have a candidate
178+
iGeom = geom2Eliminate.intersection(selGeom)
179+
180+
if boundary:
181+
selValue = iGeom.length()
182+
else:
183+
# we need a common boundary
184+
if 0 < iGeom.length():
185+
selValue = selGeom.area()
186+
else:
187+
selValue = 0
188+
189+
if selValue > max:
190+
max = selValue
191+
mergeWithFid = selFid
192+
mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry
193+
193194
if mergeWithFid != None: # a successful candidate
194-
try:
195-
geomList = geomsToMerge[mergeWithFid]
196-
except KeyError:
197-
geomList = [mergeWithGeom]
195+
newGeom = mergeWithGeom.combine(geom2Eliminate)
198196

199-
geomList.append(QgsGeometry(geom)) # deep copy of the geom
200-
geomsToMerge[mergeWithFid] = geomList
201-
fidsToDelete.append(fid)
197+
if outLayer.changeGeometry(mergeWithFid, newGeom):
198+
# write change back to disc
199+
if self.saveChanges(outLayer):
200+
outLayer.startEditing()
201+
else:
202+
return None
202203

204+
# mark feature as eliminated in inLayer
205+
fidsToDeselect.append(fid2Eliminate)
206+
else:
207+
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
208+
self.tr("Could not replace geometry of feature with id %1").arg( mergeWithFid ))
209+
return None
210+
203211
start = start + add
204212
progressBar.setValue(start)
205-
else:
206-
fidsNotEliminated.append(fid)
213+
# end for fid2Eliminate
207214

208-
# PROCESS
209-
for aFid in geomsToMerge.iterkeys():
210-
geomList = geomsToMerge[aFid]
215+
# deselect features that are already eliminated in inLayer
216+
for aFid in fidsToDeselect:
217+
inLayer.deselect(aFid, False)
218+
219+
#end while
211220

212-
if len(geomList) > 1:
213-
for i in range(len(geomList)):
214-
aGeom = geomList[i]
221+
if inLayer.selectedFeatureCount() > 0:
222+
# copy all features that could not be eliminated to outLayer
223+
if outLayer.addFeatures(inLayer.selectedFeatures()):
224+
# inform user
225+
fidList = QtCore.QString()
215226

216-
if i == 0:
217-
newGeom = aGeom
218-
else:
219-
newGeom = newGeom.combine(aGeom)
220-
221-
# replace geometry in outLayer
222-
if not outLayer.changeGeometry(aFid, newGeom):
223-
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
224-
self.tr("Could not replace geometry of feature with id %1").arg( aFid ))
225-
doCommit = False
226-
break
227-
228-
# delete eliminated features
229-
for aFid in fidsToDelete:
230-
if not outLayer.deleteFeature(aFid):
231-
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
232-
self.tr("Could not delete feature with id %1").arg( aFid ))
233-
doCommit = False
234-
break
235-
# prepare array for the next loop
236-
fidsToProcess = fidsNotEliminated
237-
238-
# SAVE CHANGES
239-
if doCommit:
240-
if not outLayer.commitChanges():
241-
QtGui.QMessageBox.warning(self, self.tr("Commit error"), self.tr("Commit error"))
242-
else:
243-
outLayer.rollBack()
227+
for fid in inLayer.selectedFeaturesIds():
228+
if not fidList.isEmpty():
229+
fidList.append(", ")
230+
231+
fidList.append(str(fid))
232+
233+
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
234+
self.tr("Could not eliminate features with these ids:\n%1").arg(fidList))
235+
else:
236+
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Could not add features"))
237+
238+
# stop editing outLayer and commit any pending changes
239+
if not self.saveChanges(outLayer):
240+
return None
244241

245242
if outFileName:
246243
if self.addToCanvasCheck.isChecked():
@@ -249,17 +246,4 @@ def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
249246
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
250247
self.tr("Created output shapefile:\n%1").arg(outFileName))
251248

252-
# inform user
253-
if len(fidsNotEliminated) > 0:
254-
fidList = QtCore.QString()
255-
256-
for fid in fidsNotEliminated:
257-
if not fidList.isEmpty():
258-
fidList.append(", ")
259-
260-
fidList.append(str(fid))
261-
262-
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
263-
self.tr("Could not eliminate features with these ids:\n%1").arg(fidList))
264-
265249
self.iface.mapCanvas().refresh()

‎python/plugins/fTools/tools/frmEliminate.ui

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,61 +6,30 @@
66
<rect>
77
<x>0</x>
88
<y>0</y>
9-
<width>377</width>
10-
<height>243</height>
9+
<width>436</width>
10+
<height>218</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
1414
<string>Eliminate sliver polygons</string>
1515
</property>
1616
<layout class="QGridLayout" name="gridLayout">
1717
<item row="4" column="0">
18-
<widget class="QRadioButton" name="area">
19-
<property name="text">
20-
<string>area</string>
21-
</property>
22-
</widget>
23-
</item>
24-
<item row="2" column="0" colspan="2">
25-
<widget class="QLabel" name="selected">
26-
<property name="text">
27-
<string>Selected features:</string>
28-
</property>
29-
</widget>
30-
</item>
31-
<item row="0" column="0">
32-
<widget class="QLabel" name="label_3">
33-
<property name="text">
34-
<string>Input vector layer</string>
35-
</property>
36-
</widget>
37-
</item>
38-
<item row="5" column="0" colspan="2">
3918
<widget class="QRadioButton" name="boundary">
4019
<property name="text">
4120
<string>common boundary</string>
4221
</property>
4322
</widget>
4423
</item>
45-
<item row="3" column="0" colspan="3">
24+
<item row="2" column="0" colspan="2">
4625
<widget class="QLabel" name="label">
4726
<property name="text">
4827
<string>Merge selection with the neighbouring polygon with the largest</string>
4928
</property>
5029
</widget>
5130
</item>
52-
<item row="1" column="0">
53-
<widget class="QComboBox" name="inShape"/>
54-
</item>
55-
<item row="6" column="0" colspan="3">
31+
<item row="5" column="0" colspan="2">
5632
<layout class="QHBoxLayout" name="horizontalLayout">
57-
<item>
58-
<widget class="QCheckBox" name="writeShapefileCheck">
59-
<property name="text">
60-
<string>Save to new file</string>
61-
</property>
62-
</widget>
63-
</item>
6433
<item>
6534
<widget class="QLineEdit" name="outShape">
6635
<property name="readOnly">
@@ -77,14 +46,17 @@
7746
</item>
7847
</layout>
7948
</item>
80-
<item row="7" column="0">
81-
<widget class="QCheckBox" name="addToCanvasCheck">
82-
<property name="text">
83-
<string>Add result to canvas</string>
49+
<item row="7" column="1">
50+
<widget class="QDialogButtonBox" name="buttonBox_2">
51+
<property name="orientation">
52+
<enum>Qt::Horizontal</enum>
53+
</property>
54+
<property name="standardButtons">
55+
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
8456
</property>
8557
</widget>
8658
</item>
87-
<item row="8" column="0">
59+
<item row="7" column="0">
8860
<widget class="QProgressBar" name="progressBar">
8961
<property name="value">
9062
<number>0</number>
@@ -94,13 +66,45 @@
9466
</property>
9567
</widget>
9668
</item>
97-
<item row="8" column="2">
98-
<widget class="QDialogButtonBox" name="buttonBox_2">
99-
<property name="orientation">
100-
<enum>Qt::Horizontal</enum>
69+
<item row="0" column="0" colspan="2">
70+
<layout class="QHBoxLayout" name="horizontalLayout_2">
71+
<item>
72+
<widget class="QLabel" name="label_3">
73+
<property name="text">
74+
<string>Input vector layer</string>
75+
</property>
76+
</widget>
77+
</item>
78+
<item>
79+
<widget class="QComboBox" name="inShape">
80+
<property name="sizePolicy">
81+
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
82+
<horstretch>0</horstretch>
83+
<verstretch>0</verstretch>
84+
</sizepolicy>
85+
</property>
86+
</widget>
87+
</item>
88+
</layout>
89+
</item>
90+
<item row="6" column="0">
91+
<widget class="QCheckBox" name="addToCanvasCheck">
92+
<property name="text">
93+
<string>Add result to canvas</string>
10194
</property>
102-
<property name="standardButtons">
103-
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
95+
</widget>
96+
</item>
97+
<item row="1" column="0">
98+
<widget class="QLabel" name="selected">
99+
<property name="text">
100+
<string>Selected features:</string>
101+
</property>
102+
</widget>
103+
</item>
104+
<item row="3" column="0">
105+
<widget class="QRadioButton" name="area">
106+
<property name="text">
107+
<string>area</string>
104108
</property>
105109
</widget>
106110
</item>

0 commit comments

Comments
 (0)
Please sign in to comment.