18

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

11

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)
  • Outstanding solution! My only question left is if this algorithm can be applied on polylines. – Gabor Farkas Oct 24 '14 at 21:31
  • 1
    @GaborFarkas: The example uses a polyline. Do you perhaps mean a layer containing several disjoint polylines (a multi-polyline)? That works as well. – Jake Oct 25 '14 at 11:47
3
+250

I've tried to do this before and haven't had much luck.

qGIS places repeated symbols on a line based on one reference point (by default, the center, though you can set it to top/middle/bottom x left/center/right), and rotates that symbol based on the slope of the line at that point. On a straight line, where the slope doesn't change from one symbol placement to the next, each symbol will line up perfectly with the previous. On a curve, though, no point on one symbol will perfectly match the corresponding point on the next symbol.

repeated marker symbol in qGIS

So, if the red line is the line itself, repeating a symbol along that line results in gaps between symbols along the outside of a curve, and overlaps on the inside of a curve.

To completely eliminate the gaps and overlaps, every symbol square would need to be reshaped as a rhombus of varying size--similar to how stones on an arch are beveled to match the curve. As far as I know, it's not possible to simulate something like that. But, you can decrease the distortion by densifying and smoothing your line geometry so that the change in angle is less extreme. The generalizer plugin can help with that (try using it with Chaiken's algorithm).

smoothed repeater marker symbol in qGIS

Also, breaking your symbol into smaller segments and placing each in succession, so that again you decrease the angle between each subsequent marker, would help. E.g., break your V symbol into a \ and a /, load both on the marker line and for each, set an x-offset equal to half their width, positive for one and negative for the other.

Lastly, a slightly thicker symbol stroke with rounded ends would help mask the slight distortion.

This is still a bit of a hack--would love to hear if anyone else has a more reliable approach.

Edit:

another thought: the misalignment from one symbol to another caused by the rotation of the symbol along the curve is greatest at the top/bottom of the symbol, but less pronounced at the middle. So a pattern that starts and terminates at the symbol center will have smaller gaps than a pattern that starts/terminates at the top/bottom. E.g.

zigzag

...still a hack--still not foolproof

1

I don't think that this is a feature in QGIS. However I would try to do it this way:

  1. make two copies of the layer using the Affine tool plugin. One of the layers with a slightly larger scale and one with a slightly smaller scale.

  2. Densify the geometry of the layers. That means add more nodes.

  3. Go to the attribute table and name each feature node consequently 1,2,3,... in one layer and 1b,2b,3b,... in the second layer.

  4. merge both layers and sort the attribute layer --> this should give you a zigzag line.

Maybe this works.

  • 1
    Thanks for the answer! However, I don't think this will work except for very specific cases like a circular arc with equidistant vertices, otherwise you'll end up with an uneven zigzag line (due to the scaling and due to the densification). – Jake Oct 18 '14 at 16:10

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.