TransWikia.com

Geometry Generator using polygon boundary expression only returning Exterior Ring QGIS

Geographic Information Systems Asked by Baswein on February 28, 2021

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):
    """
    <style>
    span { color: red }

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

    make_zigzagline(<span>geometry</span>,<span>distance(s)</span>,<span>offset</span>)<br/>

    <table>
        <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>
    </table>
    <br/><br/>
    Examples:
    <ul>
        <li>make_zigzagline($geometry,'15,30',15) -> zig-zag line</li>
        <li>make_zigzagline($geometry,15,15) -> zig-zag line</li>
    </ul>

    Use smooth function to create wavelines:<br/><br/>
    Example:
    <ul><li>smooth(make_zigzagline($geometry,'15,30',15),3)</li></ul>
    """

    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
            distances.append(dist_sum)

    # 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)
        points.append(pt)
        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)  

One Answer

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.

Answered by Ian Turton on February 28, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP