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
- 1for arcpy, you'll need an ArcGIS licence.radouxju– radouxju2014-09-10 06:39:04 +00:00Commented Sep 10, 2014 at 6:39
-
- 1The question do not mention that holes are separate interior polygons so I believe that they are only inner rings in the big polygons.user30184– user301842014-09-10 06:43:52 +00:00Commented 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?Akhil– Akhil2014-09-10 06:51:11 +00:00Commented Sep 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!Caroline Even– Caroline Even2014-09-11 07:08:30 +00:00Commented 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]]]}