I am using a custom QGIS expression function that creates a zigzag line. See the function here: Wavy Edge on polygon in QGIS using geometry generators The function needs a line string and not a polygon so I use the boundary expression like this:  make_zigzagline(boundary($geometry),3,3) This works except if it has interior rings. If the polygons have interior rings I get this error

Eval Error: MultiLineString geometry cannot be converted to a polyline. Only single line or curve types are permitted.

And it only shows on polygons without rings. boundary only If use this expression instead:

collect_geometries(make_zigzagline(exterior_ring( $geometry),1.5,3), make_zigzagline(interior_ring_n( $geometry,1),1.5,3))

It only shows those polygons with at least one ring and doesn't show the second ring if it exists. exterior and 1st interior rings

Is there some way I can have it figure out how many rings it has an use that? Or is there something I can change in this function I can change to have it accept MultiLineString geometry?

from qgis.core import qgsfunction,QgsExpressionContextUtils,QgsExpression,QgsProject,QgsPoint,QgsGeometry
@qgsfunction(args='auto', group='Custom', usesGeometry=False, referencedColumns=[])
def make_zigzagline(geom,dist,offset,feature,parent):
    span { color: red }

    <h2>converts a linestring to a zig-zag line</h2>


        <tr><td><span>geometry</span></td><td>linestring geometry</td></tr>
        <tr><td><span>distance(s)</span></td><td>linear point distances (single number or a string of comma separated numbers)</td></tr>
        <tr><td><span>offset</span></td><td>perpendicular offset</td></tr>
        <li>make_zigzagline($geometry,'15,30',15) -> zig-zag line</li>
        <li>make_zigzagline($geometry,15,15) -> zig-zag line</li>

    Use smooth function to create wavelines:<br/><br/>

    if not type(dist) is str:
        dist = str(dist)

    dist = [float(n) for n in dist.split(',')]
    l = geom.length()
    dist_sum = 0
    distances = []
    while dist_sum + round(sum(dist),2) < l:
        for d in dist:
            dist_sum += d

    # interpolate points on linestring
    points2d = [(lambda g: (g.x(), g.y()))(geom.interpolate(d).asPoint()) for d in distances]
    vertices = geom.asPolyline()
    start = (vertices[0].x(),vertices[0].y())
    end = (vertices[-1].x(),vertices[-1].y())

    points2d.insert(0,start) # prepend start point
    points = [QgsPoint(start[0],start[1])]
    i = 0
    n = 0
    b = -90
    for point in points2d[1:]:
        pt1 = QgsPoint(points2d[i][0],points2d[i][1])
        pt2 = QgsPoint(point[0],point[1])
        a = pt1.azimuth(pt2) + b
        pt = pt2.project(offset, a)
        i += 1
        n += 1
        if n == len(dist):
            n = 0
            b = -b

    points.append(QgsPoint(end[0],end[1])) # append end point
    return QgsGeometry.fromPolyline(points)  

1 Answer 1


To find out how many interior rings there are you need to use num_interior_rings and then make use of interior_ring_n to fetch each one in turn.

  • Thanks Ian- How would I write the expression so that it will apply the style to features with 0, 1 and 2 interior rings? Right now if I tell it to show an interior ring it turns off all the polygons without an interior ring.
    – Baswein
    Jul 31, 2020 at 13:33
  • see the answer to gis.stackexchange.com/a/370296/79 for a function to do just this
    – Ian Turton
    Aug 3, 2020 at 8:17
  • See my new customLineStyle solution for all possible geometry types: gis.stackexchange.com/questions/376411/…
    – christoph
    Oct 13, 2020 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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