6

I work for an engineering firm and the standard for annotating areas which require review or areas of interest is to add AutoCAD revision clouds. There is a dedicated tool for this in AutoCAD but I want to create them in QGIS.

Drawing them freehand will take too long so is there a way of symbolising a simple polygon to look like a revision cloud or alternatively is there a way of drawing a polygon which will automatically take on the shape of a revision cloud?

AutoCAD revision cloud examples:

enter image description here

enter image description here

enter image description here

2 Answers 2

5

In QGIS 3.10 it's possible to dynamically create a Revision Cloud Style with the help of the indispensable "geometry generator" and a custom Python expression function. Hat tip to @christoph and his answer here https://gis.stackexchange.com/a/354873/94350 about wavy lines that I am used as the basis for this answer. In the style properties of the polygon layer you want to have as your revision cloud layer change the Symbol Layer Type to Geometry Generator. And click on the E symbol to open up the expression dialog for the geometry generator. enter image description here Using the function editor tab create a new function using the green plus button called ZigZag and paste this script into it instead of the default text.

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)

Function editor Switching back to the expression tab insert this expression:

buffer(make_polygon(smooth(make_zigzagline(exterior_ring(smooth($geometry,1,.1)),3,2),3)),3)

You may have to play with the values to get to work at your scale. For the most part they are in the units of the layer. Meters in my case.
Here is what the final product looks like: enter image description here

2

I tried a few different approaches. I didn't quite get what you were looking for, but maybe this will give you a starting point.

First attempt: two-layer symbol

  • top layer: solid polygon
  • bottom layer: marker line, with elipses for the markers

enter image description here

The solid polygon covers up half of each elipse, creating a scalloped line effect. Downside: the interior is opaque, so it doesn't work as a frame.

enter image description here

Second attempt: single layer, marker outline with semi-circles as the markers.

  • rotation 180 degrees
  • transparent fill

enter image description here

Not perfect, but getting closer. Need to find a way to fix the corners, and get rid of the straight part of the semicircle.

Almost the same, but with a half ellipse instead of semi-circle: enter image description here

I think if you created a custom half-ellipse SVG marker without the flat line at the bottom, it would be reasonably close.

Third attempt: Convert the image of the revision cloud to SVG. Use the svg for centroid fill with SVG marker.

To convert to SVG, use this online converter, or a free program like Inkscape.

enter image description here

This gives you a fixed shape outline. You'll need to create a new SVG for each outline shape.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.