Skip to content

Commit

Permalink
[processing] Consolidate 'delete holes' algorithms
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nyalldawson committed Nov 28, 2016
1 parent 57f482e commit fef15e0
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 22 deletions.
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/help/qgis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
41 changes: 20 additions & 21 deletions python/plugins/processing/algs/qgis/DeleteHoles.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,41 @@

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


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(),
Expand All @@ -56,28 +70,13 @@ def processAlgorithm(self, progress):
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
30 changes: 30 additions & 0 deletions python/plugins/processing/tests/testdata/custom/remove_holes.gfs
Original file line number Diff line number Diff line change
@@ -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>
40 changes: 40 additions & 0 deletions python/plugins/processing/tests/testdata/custom/remove_holes.gml
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit fef15e0

Please sign in to comment.