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: 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. 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.
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)