2

In QGIS, I have a line layer with straight lines, consisting only of two vertices: start- end endpoint. I can now use Geometry Generator (see below for the expression I use) to create a third point in the middle of the line and shift it perpendicular to the line to get a triangle-shape line (see screenshot: yellow point, black dotted line). This is to apply an arrow symbol-layer style with curved arrows (blue arrows).

If I use the same Geometry Generator expression to create the triangle, but add a smooth() function, I also a curved line (red line), however, it's a different shape.

Question: what does the arrow line represent, how is it geometrically constructed? I would like to recreate the blue (arrow) line with Geometry Generator. It seems to look like a circular segment, however, I'm not sure how to construct it.

Screenshot: highlighted in yellow the expression to create the red lines: enter image description here

The expression I use to create the triangle-shaped line looks as follows:

make_line (
    start_point($geometry),
    project(
        centroid($geometry),
        400,
        azimuth(
            start_point($geometry),
            end_point($geometry)
        )-radians(90)
    ),
    end_point($geometry)
)
Babel
  • 71,072
  • 14
  • 78
  • 208
  • 1
    Check my answer (Solution 3) here: https://gis.stackexchange.com/a/463435/99589. Chaikin's Algorithm is implemented via the smooth() function. You can read more in this PDF: https://www.cs.unc.edu/~dm/UNC/COMP258/LECTURES/Chaikins-Algorithm.pdf – Taras Dec 23 '23 at 11:47
  • This is a great answer and helps to understand how smooth() works. However, as far as I see, it does not address the question how to create a curved line through all three points. – Babel Dec 23 '23 at 11:53
  • 1
    No, I want to recreate the blue (arrow) line: a curve through 1) line's start point, 2) yellow point, 3) line's end point – Babel Dec 23 '23 at 12:02
  • Maybe check the Changelog for QGIS 2.16, where it says: "In curved mode, nodes of the line layer this symbol layer is applied to are used as control points for circle arcs." Basically a Circular arc built through three points. – Taras Dec 23 '23 at 12:07
  • Yes, and I try to find out how to construct these arcs. – Babel Dec 23 '23 at 12:09
  • https://gis.stackexchange.com/questions/427120/curved-labels-on-curved-arrows – MrXsquared Dec 23 '23 at 12:51

2 Answers2

2

Unfortunately, it is not a desirable Geometry Generator, however, I can suggest a PyQGIS approach. It is primarily based on the segmentizeArc() method of the QgsGeometryUtils class. Perhaps this tool would be a nice add-on to QGIS functions.

Let's assume there is a polyline layer (five features), see the image below. Line visualization imitates OP's example.

input

Proceed with Plugins > Python Console > Show Editor (Ctrl+Alt+P) and paste the script below:

# imports
from qgis.core import (QgsProject, QgsVectorLayer, QgsGeometryUtils,
                       QgsFeature, QgsGeometry, QgsPoint)

getting the original polyline layer by its name

layer = QgsProject.instance().mapLayersByName("YOUR_LAYER_NAME")[0]

creating a polyline layer for the output

vl = QgsVectorLayer(f"LineString?crs={layer.crs().authid()}&index=yes", "arrows_like", "memory") provider = vl.dataProvider()

adding all fields from the original layer

provider.addAttributes(layer.fields()) vl.updateFields()

looping over each original feature

for feat in layer.getFeatures(): # initiating a new feature new_feat = QgsFeature()

# accessing original polyline geometry as a list of QgsPointXY
geom_points = feat.geometry().asPolyline()
# converting each QgsPointXY to QgsPoint in the list
points = list(map(lambda point: QgsPoint(point), geom_points))
# building arcs from three points
arc_as_points_seq = QgsGeometryUtils.segmentizeArc(points[0], points[1], points[2])
# converting a sequence of points to a polyline
new_geom = QgsGeometry.fromPolyline(arc_as_points_seq)

# setting up new geometry
new_feat.setGeometry(new_geom)
# setting up initial attributes
new_feat.setAttributes(feat.attributes())
# finalizing a new feature
provider.addFeature(new_feat)

QgsProject.instance().addMapLayer(vl)

Press Run script run script and get the output that will look like:

output

Taras
  • 32,823
  • 4
  • 66
  • 137
2

There is an option with Geometry Generator as well with the expression from below where you have the option to set the radius of the curve in line 8. If you change the value there to negative values, the curve will be created on the other side of the initial line.

Red lines created by the expression, based on the original black line: enter image description here

The expression is as follows:

with_variable(
    'azim',
    degrees(azimuth (start_point ($geometry), end_point ($geometry))),
with_variable(
    'point',
    project(
        centroid($geometry),
        $length*0.4,  -- change radius of curve here (smaller values result in larger radius)
        radians (@azim-90)
    ),
with_variable(
    'wedge_azim',
    degrees(
        azimuth(
            @point, 
            intersection(
                boundary(
                    make_circle (
                        @point,
                        distance (@point,start_point ($geometry)),
                        100
                    )
                ),
                extend (
                    make_line (@point, centroid($geometry)),
                    0,
                    length($geometry)
                )
            )
        )
    ),
    difference (
        boundary(
            wedge_buffer(
                @point,
                @wedge_azim,
                2*degrees(
                    atan( 
                        (length($geometry)/2) /
                        distance (@point, centroid($geometry))
                )
            ),
            distance (@point, start_point($geometry))
        )
    ),
    buffer (
        make_line (start_point($geometry), @point, end_point($geometry)),
        length($geometry)/10000
    )
)

)))

Babel
  • 71,072
  • 14
  • 78
  • 208