[processing] Consolidate 'delete holes' algorithms

Copy min area parameter from 'Fill holes' algorithm to 'delete
holes' algorithm.

Also:

- make algorithm maintain z/m values
- make algorithm work with curved geometries
- add unit tests
This commit is contained in:
Nyall Dawson 2016-11-28 13:12:50 +10:00
parent 57f482e2d9
commit fef15e0165
9 changed files with 257 additions and 22 deletions

View File

@ -130,6 +130,8 @@ qgis:deleteduplicategeometries: >
qgis:deleteholes: >
This algorithm takes a polygon layer and removes holes in polygons. It creates a new vector layer in which polygons with holes have been replaced by polygons with only their external ring. Attributes are not modified.
An optional minimum area parameter allows removing only holes which are smaller than a specified area threshold. Leaving this parameter as 0.0 results in all holes being removed.
qgis:densifygeometries:
This algorithm takes a polygon or line layer and generaates a new one in which the geometries have a larger number of vertices than the original one.

View File

@ -26,7 +26,8 @@ __revision__ = '$Format:%H$'
from qgis.core import QgsFeature, QgsGeometry
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import (ParameterVector,
ParameterNumber)
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector
@ -34,19 +35,32 @@ from processing.tools import dataobjects, vector
class DeleteHoles(GeoAlgorithm):
INPUT = 'INPUT'
MIN_AREA = 'MIN_AREA'
OUTPUT = 'OUTPUT'
def defineCharacteristics(self):
self.name, self.i18n_name = self.trAlgorithm('Delete holes')
self.group, self.i18n_group = self.trAlgorithm('Vector geometry tools')
self.tags = self.tr('remove,delete,drop,holes,rings')
self.addParameter(ParameterVector(self.INPUT,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(ParameterNumber(self.MIN_AREA,
self.tr('Remove holes with area less than'), 0, 10000000.0, default=0.0, optional=True))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Cleaned'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
def processAlgorithm(self, progress):
layer = dataobjects.getObjectFromUri(
self.getParameterValue(self.INPUT))
min_area = self.getParameterValue(self.MIN_AREA)
if min_area is not None:
try:
min_area = float(min_area)
except:
pass
if min_area == 0.0:
min_area = -1.0
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
layer.fields(),
@ -56,28 +70,13 @@ class DeleteHoles(GeoAlgorithm):
features = vector.features(layer)
total = 100.0 / len(features)
feat = QgsFeature()
for current, f in enumerate(features):
geometry = f.geometry()
if not geometry.isEmpty():
if geometry.isMultipart():
multi_polygon = geometry.asMultiPolygon()
for polygon in multi_polygon:
for ring in polygon[1:]:
polygon.remove(ring)
geometry = QgsGeometry.fromMultiPolygon(multi_polygon)
if f.hasGeometry():
if min_area is not None:
f.setGeometry(f.geometry().removeInteriorRings(min_area))
else:
polygon = geometry.asPolygon()
for ring in polygon[1:]:
polygon.remove(ring)
geometry = QgsGeometry.fromPolygon(polygon)
else:
geometry = QgsGeometry(None)
feat.setGeometry(geometry)
feat.setAttributes(f.attributes())
writer.addFeature(feat)
f.setGeometry(f.geometry().removeInteriorRings())
writer.addFeature(f)
progress.setPercentage(int(current * total))
del writer

View File

@ -0,0 +1,30 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>remove_holes</Name>
<ElementPath>remove_holes</ElementPath>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>4</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>4</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>2</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:remove_holes fid="polys.4">
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:remove_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:remove_holes fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:remove_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:remove_holes fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,1 10,1 10,-3 6,-3 6,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>7,0 7,-2 9,-2 9,0 7,0</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
</ogr:remove_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:remove_holes fid="remove_holes.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,6 2,3 10,3 10,6 2,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>2.5,5.6 2.5,3.5 5.6,3.5 5.6,5.6 2.5,5.6</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>7.0,5.5 7,5 8,5 8.0,5.5 7.0,5.5</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>6.0,4.5 6.0,3.5 9.0,3.5 9.0,4.5 6.0,4.5</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:remove_holes>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,30 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>removed_holes</Name>
<ElementPath>removed_holes</ElementPath>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>4</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>4</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>2</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:removed_holes fid="polys.4">
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:removed_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
<ogr:name>elim</ogr:name>
</ogr:removed_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,1 10,1 10,-3 6,-3 6,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:intval>0</ogr:intval>
<ogr:name>ASDF</ogr:name>
</ogr:removed_holes>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes fid="remove_holes.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,6 2,3 10,3 10,6 2,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:removed_holes>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,30 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>removed_holes_min_area</Name>
<ElementPath>removed_holes_min_area</ElementPath>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>4</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>6.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>4</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>2</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:removed_holes_min_area fid="polys.4">
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:removed_holes_min_area>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes_min_area fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
<ogr:name>elim</ogr:name>
</ogr:removed_holes_min_area>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes_min_area fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,1 10,1 10,-3 6,-3 6,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:intval>0</ogr:intval>
<ogr:name>ASDF</ogr:name>
</ogr:removed_holes_min_area>
</gml:featureMember>
<gml:featureMember>
<ogr:removed_holes_min_area fid="remove_holes.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,6 2,3 10,3 10,6 2,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>2.5,5.6 2.5,3.5 5.6,3.5 5.6,5.6 2.5,5.6</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
</ogr:removed_holes_min_area>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -1728,4 +1728,28 @@ tests:
type: vector
compare:
geometry:
precision: 7
precision: 7
- algorithm: qgis:deleteholes
name: Delete holes (no min)
params:
INPUT:
name: custom/remove_holes.gml
type: vector
MIN_AREA: 0.0
results:
OUTPUT:
name: expected/removed_holes.gml
type: vector
- algorithm: qgis:deleteholes
name: Delete holes (with min)
params:
INPUT:
name: custom/remove_holes.gml
type: vector
MIN_AREA: 5.0
results:
OUTPUT:
name: expected/removed_holes_min_area.gml
type: vector