I have many polygon layers, each layer containing many polygons with inner rings. I know that you can delete a ring manually with QGIS (Edit -> Delete ring). I need the python equivalent to automatically process all my polygon layers.
4 Answers
This blog posts tells how to do it with SQL in PostGIS http://geospatial.commons.gc.cuny.edu/2013/11/04/filling-in-holes-with-postgis/
Spatialite has also ExteriorRing function http://www.gaia-gis.it/gaia-sins/spatialite-sql-latest.html so you do not need to have PostGIS installed. Is it necessary for you to do the job with python inside QGIS? The SQL looks like this:
UPDATE my_spatial_table t
SET geom = a.geom
FROM (
SELECT gid, ST_Collect(ST_MakePolygon(geom)) AS geom
FROM (
SELECT gid, ST_NRings(geom) AS nrings,
ST_ExteriorRing((ST_Dump(geom)).geom) AS geom
FROM my_spatial_table
WHERE ST_NRings(geom) > 1
) s
GROUP BY gid, nrings
HAVING nrings > COUNT(gid)
) a
WHERE t.gid = a.gid;
QGIS offers a tool called Delete holes
that you can call via Python in this way:
import processing
vLayer = iface.activeLayer() # Select the layer in QGIS layer tree before calling this
outPath = "/path/to/output.shp"
processing.runalg( "qgis:deleteholes", vLayer, outPath )
You could iterate through QGIS layers to run such tool on your polygon layers.
You could also access the source code of the tool to see how it works: https://github.com/qgis/QGIS/blob/release-2_14/python/plugins/processing/algs/qgis/DeleteHoles.py Note that the algorithm will change in QGIS v3.0, since there'll be a new geometry().removeInteriorRings()
method: https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/qgis/DeleteHoles.py
If you are comfortable using Python, including ArcPy... Try this...
You can read the shapefiles using:
Step 1:
import shapefile # Required plugin is PyShp
sf = shapefile.Reader("Path to shapefile...")
'sf' will now contain your shapefile information.
Step 2:
Use ArcPy to find the intersection of polygons. If the interior polygons intersect the larger polygon (which they obviously will), delete the shape.
More information about PyShp could be found here.
More information of ArcPy can be found here, including about the Intersection function
-
1
-
-
1The question do not mention that holes are separate interior polygons so I believe that they are only inner rings in the big polygons. Sep 10, 2014 at 6:43
-
Yes... That clarifies the problem... If I understand correctly - A polygon with internal rings (input) needs a complete polygon without rings (output). Right?– AkhilSep 10, 2014 at 6:51
-
That's correct Akhil - I specifically wanted to do it using python and PyQGis to integrate the functionality within one of my scripts. I think I'll manage thanks to all of your answers, cheers! Sep 11, 2014 at 7:08
shapely can do this by simply removing the .interiors
of a the polygon.
Here is a complete example for removing the interiors of the first geometry of the the first feature of a geojson, assuming it's a valid Polygon
:
from shapely.geometry import mapping, shape, Polygon
def load_first_feature(path):
with open(path) as f:
root = json.load(f)
if "features" in root: # collection case, take first
assert len(root["features"]) == 1
root = root["features"][0]
return shape(root["geometry"]
def noholes(path):
holy = load_first_feature(path)
assert(isinstance(holy, Polygon))
poly = Polygon(holy.exterior.coords)
with open('noholes.geojson', 'w') as f:
f.write(json.dumps(mapping(poly)))
noholes('polygon-with-hole.geojson')
Sample input/output
{
"type": "Feature",
"properties": { "name": "union-with-holes" },
"geometry": {
"type": "Polygon",
"coordinates": [
[ [ 7, 47 ], [ 2, 49 ], [ 6, 50.6 ], [ 6, 51 ], [ 7, 51 ], [ 8, 51 ], [ 8, 48 ], [ 6, 48 ], [ 7, 47 ] ],
[ [ 5, 49 ], [ 6, 48 ], [ 6, 50 ], [ 5, 49 ] ]
]
}
}
{"type": "Polygon", "coordinates": [[[6.0, 48.0], [7.0, 47.0], [2.0, 49.0], [6.0, 50.6], [6.0, 51.0], [7.0, 51.0], [8.0, 51.0], [8.0, 48.0], [6.0, 48.0]]]}