# Make donut polygons with python in QGIS

I am having trouble in translating the geometry of donut polygons to layer. I am drawing polygons using the list of points as follows;

``````feature.setGeometry(QgsGeometry.fromPolygon([pLine]))
``````

Here pLine is the list containing all the points to be drawn. But, the results are quite different from expectation. Can someone please tell me how can I draw donut polygons through python script since the current code draws in sequence of the points and does not have any information of the hollow region between it?

• Is it possible to do like a big circle and to remove from this circle (with the difference tool) the small one? Jan 25, 2018 at 10:57
• I am unable to categorize the points that constitute the inner boundary or the outer boundary of the feature. So that rules out the inner and outer circle difference. Also I need to have a python script to do that. Jan 25, 2018 at 11:02

You are trying to construct a polygon with a long list of coordinates, that won't do. List of points is how you define a polyline, yet QgsGeometry::fromPolygon() expects a polygon.

Moreso, you want to create many polygons, yet you are feeding it only one sequential list of all coordinates, hence why it's creating one polygon that goes through each coordinate.

Polygons are defined as a closed loop linestring. Polygons with rings inside them (as in a donut) are defined with one closed loop linestring for its exterior ring (the outer boundary) and one for its inner ring (the inner boundary). You do this in PyQGIS with a list of lists, where each inner list is a closed loop list of coordinates. As such:

``````from qgis.core import QgsGeometry, QgsPoint

# Define exterior ring coordinates, notice how the first point repeats at the end of the list.
p1 = QgsPoint(0, 0)
p2 = QgsPoint(10, 0)
p3 = QgsPoint(10, 10)
p4 = QgsPoint(0, 10)
li_out_ring = [p1, p2, p3, p4, p1]

# Define interior ring coordinates, notice how the first point repeats at the end of the list.
p5 = QgsPoint(2, 2)
p6 = QgsPoint(8, 2)
p7 = QgsPoint(8, 8)
p8 = QgsPoint(2, 8)
li_inn_ring = [p5, p6, p7, p8, p5]

# Creates geometry.
polygon = QgsGeometry.fromPolygon([li_ext_ring, li_inn_ring])
``````

This is the result: You'll have to repeat that for every polygon you want to create that comprises your final shape.

UPDATE: What to do when you have a list of coordinates without distinction between inner and outer rings?

If your polygon only has one inner ring, and your exterior ring has a convex shape (which is the case of a donut), you can find it and separate the geometries. How you do this is, you first collect all your points into one Multipoint geometry. Then you find the exterior ring by creating a Convex Hull. Finally, you compare the points that are in the convex hull geometry with the full set of points, and whichever points are left out will be part of the inner ring.

``````def create_wkt_from_list(li_points):
# li_points must be a python list() of QgsPoint objects.

wkt = 'MULTIPOINT('
for point in li_points:
x = point.x()
y = point.y()
wkt += '({} {}), '.format(x, y)
wkt = wkt[:-1] + ')'
return wkt

wkt = create_wkt_from_list(li_points)
multipoint = QgsGeometry.fromWkt(wkt)
out_ring = multipoint.convexHull()
``````

From here you can construct an iterator which will go through every vertex of your out_ring and compare it with your initial list, or something like it. There's probably a more efficient way to do it, but this is the gist of it.

What if my polygon isn't convex? There are methods to finding the Concave Hull, but they are not deterministic, hence do not bode well to differentiating between outer and inner rings.

What if my polygon has many inner rings? The method above will still work, but there will be no way of differentiating between the rings, so you'll end up with only one, likely weirdly-shaped, inner ring.

• So in a way, you do need to know which point are inner ring ones and which ones are outer ring. Otherwise, the problem seems very more complex. Jan 25, 2018 at 11:17
• That's where the problem lies. I am unable to recognize between the inner and out polygons. Jan 25, 2018 at 11:33
• So it's more a kind of algorithmic question. You could maybe try to begin by creating a big union of all polygons possible or look for the biggest polygon by area (I was thinking of convex hull but it doesn't fit) to select the points on the outer part. That would give the groups maybe. Just thinking. Jan 25, 2018 at 11:51
• @raosaeedali Updated my answer to address this specific problem. Jan 25, 2018 at 11:56