Is there a QGIS function or plugin to draw wiggly line?
I have used Spline Tool to manually draw some waves, but it is time consuming. If possible, I would like to draw something like:
Function Plotter (
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It only takes a minute to sign up.Sign up to join this community
I propose a solution using PyQGIS. It should work both for Linestring and MultiLineString layers.
This solution is based on the creation of semicircular rings, so you need to set a value for the diameter (i.e. the
step variable in the code below). The step you choose won't be the real step used because it is adjusted on the basis of the line length (but it would be really similar to the value initially set). You need to do some attempts before finding the best value for the
The code also requires a second (optional) parameter (called
crv_angle), which helps for decreasing or increasing the curvature for the rings (I performed a few tests for it, so I suggest leaving 45 degrees as default angle since it would lead to real circular rings).
You only need to run this code from the Python Console:
from math import sin, cos, radians step = 3 # choose the proper value (e.g. meters or degrees) with reference to the CRS used crv_angle = 45 # degrees def segment(polyline): for x in range(0, len(polyline) - 1): first_point = polyline[x] second_point = polyline[x +1] seg = QgsGeometry.fromPolyline([first_point, second_point]) tmp_azim = first_point.azimuth(second_point) len_feat = seg.length() parts = int(len_feat/step) real_step = len_feat/parts # this is the real step applied points =  current = 0 up = True while current < len_feat: if up: round_angle = radians(90 - (tmp_azim - crv_angle)) up = False else: round_angle = radians(90 - (tmp_azim + crv_angle)) up = True first = seg.interpolate(current) coord_x, coord_y = (first.asPoint().x(), first.asPoint().y()) p1=QgsPointV2(coord_x, coord_y) dist_x, dist_y = ((real_step*sin(rad_crv_angle))* cos(round_angle), (real_step*sin(rad_crv_angle)) * sin(round_angle)) p2 = QgsPointV2(coord_x + dist_x, coord_y + dist_y) points.extend([p1, p2]) current += real_step second = seg.interpolate(current + real_step) p3=QgsPointV2(second.asPoint().x(), second.asPoint().y()) points.append(p3) circularRing = QgsCircularStringV2() circularRing.setPoints(points) # set points for circular rings fet = QgsFeature() fet.setGeometry(QgsGeometry(circularRing)) prov.addFeatures([fet]) layer = iface.activeLayer() # load the input layer as you want crs = layer.crs().toWkt() rad_crv_angle = radians(crv_angle) # Create the output layer outLayer = QgsVectorLayer('Linestring?crs='+ crs, 'wiggly_line' , 'memory') prov = outLayer.dataProvider() fields = layer.pendingFields() prov.addAttributes(fields) outLayer.updateFields() for feat in layer.getFeatures(): geom = feat.geometry() polyline = geom.asPolyline() segment(polyline) # Add the layer to the Layers panel QgsMapLayerRegistry.instance().addMapLayer(outLayer)
and it will create a new line memory layer with the expected result:
Short answer: you can get it using a custom SVG. See bottom of this post for one.
I believe it is better to represent it than to modify the line geometry. Should you want to move an edge or do other actions on the geometry, it would be a nightmare to manage if the wiggles are part of the geometry instead of just a representation of a straight line.
To get this, you would style the line with two Marker lines. Each Marker line is made of a Simple Marker, the half-circle. The 1st one is rotated by 180. Both are set to transparent.
On the Marker line, you instruct one of them to be offset so the two symbols are not drawn in front of each other, but side by side. If you use offest = 1/2 * interval size, the output will be a sinusoidal curve. I suggest you play with the interval size, offset and symbol sizes.
The main limitation with this approach is the diameter line of the half circles, which sums to the original line. If your background is white (or any plain color), you could add a 3rd simple line using the background color.
** EDIT **
Another option to get rid of the center line is to create a new SVG symbol. I modified the half-curve, only living the rounded part. It works, though a 1/2 ellipse might be more appealing. The screenshot was done using symbol size 10, interval 4, offset 2.
save the code below in a file half_circle_line.svg and make sure the path to the svg is set in
QGIS // Settings / Options / System / SVG Paths
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="11.2889mm" height="11.2889mm" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> <title>Qt Svg Document</title> <desc>Generated with Qt</desc> <defs> </defs> <g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" > <g fill="#ffffff" fill-opacity="0" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)" font-family="MS Shell Dlg 2" font-size="8.25" font-weight="400" font-style="normal" > <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M19.1181,16 C19.1181,16 19.1181,14.2779 17.7221,12.8819 16,12.8819 C14.2779,12.8819 12.8819,14.2779 12.8819,16"/> </g> </g> </svg>