2

With a line geometry layer, I can use a "Marker Line" symbology element to regularly space markers along a line, for example every 20mm, even if there's no line vertexes there. I can also use the "Rotate marker to follow line direction" alongside the "Average angle over.." boxes to get a marker that points consistently along or across the line. Like this:

enter image description here

But if the track heads west, the car is pointing in the wrong direction!

What I'd like to do is vary aspects of the marker, depending on some geometric property of the marker being made at each point. My use case would be to select a different SVG icon based on the set rotation of the marker as controlled by the "Marker Line" machinery. Try as a I might I can't get any properties of the marker in the expression dialog anywhere, all I seem to get is properties of the feature being drawn.

I have tried making a Marker Line >>> Marker >>> Geometry Generator but still couldn't get the rotation angle into an expression.

I don't really want to generate a new points layer with all the marker points and a rotation attribute since this is a dynamic process, but I suppose if the marker points and rotations could be made as a virtual layer that would work (although I suspect if that could be done then the rotation could be gotten by the marker symbology...).

2 Answers 2

1

It can be done with a geometry generator, but you would need to have 2 SVGs, one pointing to the right and one pointing to the left. (unless someone knows a way of mirroring an SVG)

Based on this post, we know it is possible to rotate a point using the angle of a nearby line. In our case, the points come from the geometry generator and the nearby line is the original line.

Create a marker line, with points every 20mm, don't rotate the symbols.
Change the marker to be of type geometry generator
On the marker tab, we need to set the rotation and the svg to be displayed

Click on the epsilon icon beside the rotation box, edit the data defined override and add the following expression

with_variable('angle', line_interpolate_angle(
geometry:=intersection(buffer(geometry:=$geometry, distance:=0.0001, segments:=3),
overlay_nearest(layer:=@layer, expression:=$geometry)[0]), distance:=0.0001),if(@angle>180, @angle+90, @angle-90))

Here since it is in 4326 I created a buffer of 0.0001 degrees (anything, but should be small so a single nearby line is found, and we know the points are exactly on top of the line).

Then, using the angle of the line near the point, it adds or remove 90degrees to the angle so our left/right looking SVG are properly orientated.

Now click on the epsilon icon beside the svg path and edit the data override.
Using an if and the same expression as above, we instruct to use the left or right facing SVG

if(line_interpolate_angle(
geometry:=intersection(buffer(geometry:=$geometry, distance:=0.0001, segments:=3),
overlay_nearest(layer:=@layer, expression:=$geometry)[0]), distance:=0.0001)<180,'C:/path_to_svg/bicycle-share-15.svg','C:/path_to_svg/grocery-15.svg')

enter image description here

2
  • Looks good but I can't get this to work - I'm getting one of my SVG's, always pointing in the same direction. Clearly I'm doing something wrong. Am using a EPSG4326 layer that's about 2x2 degrees bounds... I'll take those expressions apart and see if I can debug it...
    – Spacedman
    Aug 25, 2023 at 16:43
  • (I'm on qgis 3.32-1)
    – Spacedman
    Aug 25, 2023 at 17:33
0

You should use the data defined override to :

  • Set the path to different SVG file depending on line orientation (you will need two SVG, one facing est the other facing west)

AND

  • Set the rotation of your SVG depending on line orientation (otherwise it will appear upside down)

The orientation of the line could be approximated by calculating the azimut between the first and last vertex by using this expression :

degrees( azimuth(  start_point( @geometry),  end_point( @geometry)))

and use it in a data defined override expression for the SVG path :

CASE
    WHEN  degrees( azimuth(  start_point( @geometry),  end_point( @geometry)))  BETWEEN 0 AND 180 THEN 'Path to eastward SVG'
    ELSE 'Path to westward SVG'
END

and for the rotation :

CASE
    WHEN  degrees( azimuth(  start_point( @geometry),  end_point( @geometry)))  BETWEEN 0 AND 180 THEN 0
    ELSE 180
END

enter image description here

You may also use a similar expression to set the line offset for your marker line if you want your SVG to ride on the top of the line rather than centered on it (I did it in the image above)

This solution does not deal with looping line (some symbol will appear upside down as seen for the line starting near Madagascar), if that's a problem @JGH solution should be preferred.

3
  • Isn't the bottom loop what the OP tried to avoid?
    – JGH
    Aug 25, 2023 at 15:56
  • Yes, maybe I wasn't too clear. This styling uses the same symbol depending on the start and finish of the whole line feature, not the direction of the segment. The Indian Ocean loop here finishes west of the start so the logic uses the "car" svg.
    – Spacedman
    Aug 25, 2023 at 16:14
  • @JGH Yes this is more a "mostly work" quick fix than a full solution to the problem, I edited the question to reflect that
    – J.R
    Aug 25, 2023 at 16:27

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.