1

While trying to round out my answer to Geometry Generator using polygon boundary expression only returning Exterior Ring QGIS I am trying to determine if the geometry passed into the function is a Polygon or a MultiPolygon and then to extract it's rings to pass to the zigzag drawing code.

My first attempt was to use the solution to Remove inner rings from QgsGeometry polygon in QGIS Python gives me 'list' object has no attribute 'interpolatePoint' as an error with my code looking like:

geoms = []
if geom.isMultipart() is False: # if only simple polygon, calculate only for this
    polyg = geom.asPolygon() # transform to list of points
    for ring in polyg:
        geoms.append(ring)
else: # is multipart
    multi = geom.asMultiPolygon()
    for polyg in multi:
        for ring in polyg:
            geoms.append(ring)
# interpolate points on linestring
for gx in geoms:
    points2d = [(lambda g: (g.x(), g.y()))(gx.interpolatePoint(d)) for d in distances]
    vertices = gx.points()

So one of the asX methods returned an empty list instead of a Polygon, I think?

So next I tried to go from basics with:

geoms = []

# assume it is a polygon
if geom.wkbType() == QgsWkbTypes.Polygon:
    poly = geom.asPolygon()
    geoms.append(poly.exteriorRing())
else:
    geoms.append(geom.boundary())

Which gives the same error message when applied to polygon features. So then I tried:

geoms = []

geoms.append(geom.exteriorRing())    
for i in range(0,geom.getNumInteriorRings()):
    geoms.append(geom.getInteriorRingN(i))

But it says, Eval Error: 'QgsGeometry' object has no attribute 'exteriorRing' so I thought it needed changing to a polygon. Finally, I tried:

geoms = []

poly = geom.asPolygon()
geoms.append(poly.exteriorRing())    
for i in range(0,poly.getNumInteriorRings()):
    geoms.append(poly.getInteriorRingN(i))

But that just says 'list' object has no attribute 'exteriorRing' My test features are:

Polygon ((513366.96000000002095476 102956.35000000000582077, 513355.33000000001629815 102954.64999999999417923, 513356.32000000000698492 102947.89999999999417923, 513367.95000000001164153 102949.60000000000582077, 513366.96000000002095476 102956.35000000000582077))
Polygon ((513392.47999999998137355 102973.47999999999592546, 513401.75 102916.32000000000698492, 513424.30999999999767169 102919.85000000000582077, 513415.40000000002328306 102977.08000000000174623, 513392.47999999998137355 102973.47999999999592546),(513408.89000000001396984 102967.58999999999650754, 513415.45000000001164153 102926.5, 513407.88000000000465661 102925.28999999999359716, 513401.32000000000698492 102966.38000000000465661, 513408.89000000001396984 102967.58999999999650754))

So (currently) there is no problem with a multipolygon or anything complicated.

2
  • 1
    I'd like to help, but I don't really get the question here. Do you need a list of rings (as line strings) from an input geometry, regardless of if it is multi/single-part and if it has interior rings or not? If so, I have a function for that. Aug 1 '20 at 0:48
  • that's exactly what I need
    – Ian Turton
    Aug 1 '20 at 8:26
3

The following function (adapted from here) receives a polygon geometry and gives you a QgsMultiLineString with all polygon rings. It takes into account single and multi-parts.

def get_polygon_rings_as_lines(polygon):    
    # Input polygon can be multi or single part
    # and could have interior rings
    
    def get_rings_from_polygon_part(part_polygon):
        part_rings = list()
        part_rings.append(part_polygon.exteriorRing().clone())
        for i in range(part_polygon.numInteriorRings()):
            part_rings.append(part_polygon.interiorRing(i).clone())

        return part_rings
    
    abs_polygon = polygon.get()
    ring_list = list()
    
    if polygon.isMultipart():
        for part in range(abs_polygon.numGeometries()):
            ring_list.extend(get_rings_from_polygon_part(abs_polygon.geometryN(part)))
    else:
        ring_list.extend(get_rings_from_polygon_part(abs_polygon))
    
    rings = QgsMultiLineString()
    for ring in ring_list:
        rings.addGeometry(ring)
    
    return rings

Note that when dealing with PyQGIS geometries, sometimes you might need the QgsGeometry (which is defined as a container in the docs) and sometimes you might need the real geometry primitive (QgsAbstractGeometry).

2
  • Thanks German and Ian. I was getting some errors in the function so I added these to the top to try to fix them. from qgis.core import * from qgis.gui import * @qgsfunction(args='auto', group='Custom') def get_polygon_rings_as_lines(polygon, feature, parent): And now I don't get an error but It returns nothing in the preview where I would expect to see <Geometry: Multilinestring>. And also nothing on the screen
    – Baswein
    Aug 3 '20 at 12:22
  • Also in my research I came across this post by Nyall Dawson. That makes it seem like it should be easier in QGIS >3.6. nyalldawson.net/tag/pyqgis But I know nothing about python so am lost.
    – Baswein
    Aug 3 '20 at 12:28

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.