fix output of the eliminate slivers polygon tool (fix 6925). Patch by

Bernhard Strobl
This commit is contained in:
Alexander Bruy 2013-01-22 12:22:17 +04:00
parent 181a7393c1
commit 07772f13f6
2 changed files with 150 additions and 162 deletions

View File

@ -41,7 +41,6 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
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
@ -53,43 +52,34 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
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:
outFileName = self.outShape.text()
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)
if outFileName == "":
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
self.buttonOk.setEnabled(True)
return None
else:
outFileName = None
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))
self.buttonOk.setEnabled(True)
return None
outFileName = unicode(outFileName)
inLayer = ftools_utils.getVectorLayerByName(unicode(self.inShape.currentText()))
@ -109,13 +99,23 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
self.outShape.clear()
(outFileName, self.encoding) = ftools_utils.saveDialog(self)
if outFileName is None or self.encoding is None:
return
return None
self.outShape.setText(outFileName)
def eliminate(self, inLayer, boundary, progressBar, outFileName = None):
def saveChanges(self, outLayer):
if outLayer.commitChanges():
return True
else:
msg = ""
for aStrm in outLayer.commitErrors():
msg = msg + "\n" + aStrm
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Commit error:\n %1").arg(msg))
outLayer.rollBack()
return False
def eliminate(self, inLayer, boundary, progressBar, outFileName):
# 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()
@ -128,12 +128,20 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
outLayer = QgsVectorLayer(outFileName, QtCore.QFileInfo(outFileName).completeBaseName(), "ogr")
else:
outLayer = inLayer
outLayer.removeSelection(False)
QtGui.QMessageBox.information(self, self.tr("Eliminate"), self.tr("Please specify output shapefile"))
return None
# delete features to be eliminated in outLayer
outLayer.setSelectedFeatures(fidsToEliminate)
outLayer.startEditing()
doCommit = True
if outLayer.deleteSelectedFeatures():
if self.saveChanges(outLayer):
outLayer.startEditing()
else:
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Could not delete features"))
return None
# ANALYZE
start = 20.00
progressBar.setValue(start)
@ -144,103 +152,92 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
# 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 = []
while (lastLen != inLayer.selectedFeatureCount()): #check if we made any progress
lastLen = inLayer.selectedFeatureCount()
fidsToDeselect = []
#iterate over the polygons to eliminate
for fid in fidsToProcess:
for fid2Eliminate in inLayer.selectedFeaturesIds():
feat = QgsFeature()
if outLayer.featureAtId(fid, feat, True, False):
geom = feat.geometry()
bbox = geom.boundingBox()
if inLayer.featureAtId(fid2Eliminate, feat, True, False):
geom2Eliminate = feat.geometry()
bbox = geom2Eliminate.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 whether the geometry to eliminate and the other geometry intersect
selFeat = QgsFeature()
selFeat = QgsFeature()
if outLayer.featureAtId(selFid, selFeat, True, False):
selGeom = selFeat.geometry()
if outLayer.featureAtId(selFid, selFeat, True, False):
selGeom = selFeat.geometry()
if geom.intersects(selGeom): # we have a candidate
iGeom = geom.intersection(selGeom)
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 = selFid
mergeWithGeom = QgsGeometry(selGeom) # deep copy of the geometry
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 != None: # a successful candidate
try:
geomList = geomsToMerge[mergeWithFid]
except KeyError:
geomList = [mergeWithGeom]
newGeom = mergeWithGeom.combine(geom2Eliminate)
geomList.append(QgsGeometry(geom)) # deep copy of the geom
geomsToMerge[mergeWithFid] = geomList
fidsToDelete.append(fid)
if outLayer.changeGeometry(mergeWithFid, newGeom):
# write change back to disc
if self.saveChanges(outLayer):
outLayer.startEditing()
else:
return None
# mark feature as eliminated in inLayer
fidsToDeselect.append(fid2Eliminate)
else:
QtGui.QMessageBox.warning(self, self.tr("Eliminate"),
self.tr("Could not replace geometry of feature with id %1").arg( mergeWithFid ))
return None
start = start + add
progressBar.setValue(start)
else:
fidsNotEliminated.append(fid)
# end for fid2Eliminate
# PROCESS
for aFid in geomsToMerge.iterkeys():
geomList = geomsToMerge[aFid]
# deselect features that are already eliminated in inLayer
for aFid in fidsToDeselect:
inLayer.deselect(aFid, False)
#end while
if len(geomList) > 1:
for i in range(len(geomList)):
aGeom = geomList[i]
if inLayer.selectedFeatureCount() > 0:
# copy all features that could not be eliminated to outLayer
if outLayer.addFeatures(inLayer.selectedFeatures()):
# inform user
fidList = QtCore.QString()
if i == 0:
newGeom = aGeom
else:
newGeom = newGeom.combine(aGeom)
for fid in inLayer.selectedFeaturesIds():
if not fidList.isEmpty():
fidList.append(", ")
# 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 %1").arg( aFid ))
doCommit = False
break
fidList.append(str(fid))
# 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 %1").arg( aFid ))
doCommit = False
break
# prepare array for the next loop
fidsToProcess = fidsNotEliminated
QtGui.QMessageBox.information(self, self.tr("Eliminate"),
self.tr("Could not eliminate features with these ids:\n%1").arg(fidList))
else:
QtGui.QMessageBox.warning(self, self.tr("Eliminate"), self.tr("Could not add features"))
# SAVE CHANGES
if doCommit:
if not outLayer.commitChanges():
QtGui.QMessageBox.warning(self, self.tr("Commit error"), self.tr("Commit error"))
else:
outLayer.rollBack()
# stop editing outLayer and commit any pending changes
if not self.saveChanges(outLayer):
return None
if outFileName:
if self.addToCanvasCheck.isChecked():
@ -249,17 +246,4 @@ class Dialog(QtGui.QDialog, Ui_Dialog):
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()

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>377</width>
<height>243</height>
<width>436</width>
<height>218</height>
</rect>
</property>
<property name="windowTitle">
@ -15,52 +15,21 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0">
<widget class="QRadioButton" name="area">
<property name="text">
<string>area</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="selected">
<property name="text">
<string>Selected features:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input vector layer</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QRadioButton" name="boundary">
<property name="text">
<string>common boundary</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Merge selection with the neighbouring polygon with the largest</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QComboBox" name="inShape"/>
</item>
<item row="6" column="0" colspan="3">
<item row="5" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="writeShapefileCheck">
<property name="text">
<string>Save to new file</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="outShape">
<property name="readOnly">
@ -77,14 +46,17 @@
</item>
</layout>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="addToCanvasCheck">
<property name="text">
<string>Add result to canvas</string>
<item row="7" column="1">
<widget class="QDialogButtonBox" name="buttonBox_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="7" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
@ -94,13 +66,45 @@
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QDialogButtonBox" name="buttonBox_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Input vector layer</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="inShape">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="addToCanvasCheck">
<property name="text">
<string>Add result to canvas</string>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="selected">
<property name="text">
<string>Selected features:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QRadioButton" name="area">
<property name="text">
<string>area</string>
</property>
</widget>
</item>