4

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.

enter image description here

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?

  • 1
    Is it possible to do like a big circle and to remove from this circle (with the difference tool) the small one? – kogexo Jan 25 '18 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. – raosaeedali Jan 25 '18 at 11:02
5

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:

Polygon with ring

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. – kogexo Jan 25 '18 at 11:17
  • That's where the problem lies. I am unable to recognize between the inner and out polygons. – raosaeedali Jan 25 '18 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. – kogexo Jan 25 '18 at 11:51
  • 1
    @raosaeedali Updated my answer to address this specific problem. – Roberto Ribeiro Jan 25 '18 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.