[GIS] Zigzag Line Symbol in QGIS

linemarkersqgissymbology

I'm looking for a zigzag line symbol in QGIS. Is there perhaps an easy way to do this that I'm missing?
I have tried creating a marker line using a simple triangle marker (^) and adjusting the size of the marker and the marker placement interval until the traingles touched each other and appeared to make a nice zigzag line. This works for straight lines but around curves there are gaps between the triangles because the triangles aren't actually connected. Is there perhaps a way to join the markers together? Or another way to go about this? I would be very grateful for any suggestions!
(using QGIS 2.4.0)
My attempt at a zigzag line

Best Answer

It seems like there's no way to just symbolise the line as a zigzag: unfortunately, you'll have to alter the underlying data.

You can get a reasonably good zigzag line by first splitting the original line into many equidistant line segments, and then offsetting every other point by a fixed amount.

Here's a Python script that does this, taking NathanW's answer to How can I create random points along a polyline in QGIS? as a starting point. Save the code chunk into a file called zigzag.py in your ~/.qgis/python directory (or {User Directory}\.qgis\python\ on Windows), and then import it in the QGIS Python console by typing import zigzag. Then you can select one or more lines that you want to zigzagify, and type zigzag.createZigzag(<wavelength>, <amplitude>) in the QGIS Python console, where <wavelength> and <amplitude> are the "length" and "width" of the zigzag segments, in map units.

Here's an example:

As you can see, the zigzags aren't very nice near the original line's corners, but at least the zigzag line doesn't have any breaks.

If you use James Conkling's suggestion of smoothing the line first using Chaiken's Algorithm, the result gets much nicer:


Here's the script:

from qgis.utils import iface
from qgis.core import *
import numpy as np
from cmath import rect, phase


# Function for calculating the mean of two angles.
# Based on http://rosettacode.org/wiki/Averages/Mean_angle#Python
def meanAngle(a1, a2):
    return phase((rect(1, a1) + rect(1, a2)) / 2.0)


def createZigzag(wavelength, amplitude):
    # Create a new memory layer to store the zigzag line.
    vl = QgsVectorLayer("LineString", "Zigzag", "memory")
    pr = vl.dataProvider()

    # For each selected object in the current layer
    layer = iface.mapCanvas().currentLayer()
    for feature in layer.selectedFeatures():
        geom = feature.geometry()

        # Number of zigzag segments
        length = geom.length()
        segments = np.round(length / wavelength)

        # Find equally spaced points that approximate the line
        points = [geom.interpolate(distance).asPoint() for
            distance in np.linspace(0, length, segments)]

        # Calculate the azimuths of the approximating line segments
        azimuths = np.radians(
            [points[i].azimuth(points[i + 1]) for i in range(len(points) - 1)])

        # Average consecutive azimuths and rotate 90 deg counterclockwise
        zigzagazimuths = [azimuths[0] - np.pi / 2]
        zigzagazimuths.extend([meanAngle(azimuths[i],
            azimuths[i - 1]) - np.pi / 2 for i in range(len(points) - 1)]
        )
        zigzagazimuths.append(azimuths[-1] - np.pi / 2)

        # Offset the points along the zigzagazimuths
        zigzagpoints = []
        for i in range(len(points)):
            # Alternate the sign
            dst = amplitude * (1 - 2 * np.mod(i, 2))
            zigzagpoints.append(
                QgsPoint(points[i][0] + np.sin(zigzagazimuths[i]) * dst,
                    points[i][1] + np.cos(zigzagazimuths[i]) * dst
                )
            )

        # Create new feature from the list of zigzag points
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry.fromPolyline(zigzagpoints))

        pr.addFeatures([fet])
        vl.updateExtents()

    QgsMapLayerRegistry.instance().addMapLayer(vl)
Related Question