In one project, I am trying to adopt marvellous styles for flow maps created by @underdark a.k.a. Anita Graser.

At this moment I have the following output


Shapefile was created with the usage of Flow maps (Oursins) plugin.

Lines have different directions, i.e. the starting point of first is the ending point of the second and vice versa. So basically the table includes lines with both directions but without any description of those directions.


However, I faced several issues

1. Varying buffers' sizes

Buffers around points shall inherit parameters from a different layer, i.e. they are not the same size.

How to make them respond to the size of the circles' radiuses? Is there any kind of scaling factor, so to make my buffers "dynamic"?

    buffer( start_point($geometry), 500 )
  buffer( end_point( $geometry), 500 )

Maybe I to apply layer_property() or dbvaluebyid() or represent_value() if I want to extract the dynamic value from a different layer?

I know that the buffer values in the expression above only produce appropriate results for line datasets which use a CRS in meters and will have to be adjusted for other units.

2. Proper allocation of arrows

I am using an expression for Arrow type > Plain

WHEN x(start_point($geometry)) - x(end_point($geometry)) < 0 THEN 1

but still, I can not properly allocate orange halves of Arrows on the opposite side of blues.

3. The width of the arrows

I want to adjust the line width accordingly to the values of a field "FLUX". For that reason, I am using an expression for Arrow width.

scale_linear( "FLUX" ,0,10,0.1,3) * CASE WHEN "FLUX" > 1000 THEN 1 ELSE 0 END

How shall I handle those issues?

My desired output should look like this, curved lines can be ignored.



  • Thinking loud: Could you half the width of your arrows and then place them on one or the other side of your line? Like CASE WHEN x(start_point($geometry)) - x(end_point($geometry)) < 0 THEN -0.5 ELSE 0.5 END entered as "offset"-value (or use 1 instead of 0.5). Or is that not desirable for your intended style? – Erik Mar 1 at 13:36
  • Unfortunately, not that much, I have to have a good looking map for further investigations and research. Your idea makes sense, but not what I dream about. – Taras Mar 1 at 13:40
  • What's wrong with the direction and width of the arrows? It would help if you added an image of what you want the arrows to look like. – csk Mar 1 at 18:00

1. Varying buffers size.

I would recommend you to join the attributes of both layers instead of fetching layer properties. These are the expressions I used in this example:

Point layer size:

scale_exp(  "attributeBuffer" ,minTotalFlux,maxTotalFlux, @min , @max ,exponent)

Geometry generator of line layer:

  buffer( start_point($geometry), scale_exp(   "attributeBufferStart" ,minTotalFlux,maxTotalFlux, (@min /2),( @max /2),exponent),
  buffer( end_point( $geometry), scale_exp(   "attributeBufferEnd" ,minTotalFlux,maxTotalFlux, (@min /2),( @max /2),exponent)))

Where "attributeFromStart" and "attributeFromEnd" are the values used to buffer the points. You need to use map units and not millimeters in your point since the geometry generator of lines will be working with map units. @project_variables where used to easily change sizes in the project properties without having to enter the changes in each layer.

2. Proper allocation of arrows.

I think your problem is switching your direction in two layers. Your line geometries have opposite directions already so both should be going to the same side. It is like cars, you stick to one side of the road, otherwise both ways would end up using the same lane.

  • Thank you. Your answer makes a lot of sense. So, basically, you suggest applying the join functionality and then managing two layers attributeFromStart and attributeFromEnd accordingly, am I right? Do you know if I can somehow extract those values in a certain expression? I am calculating them with "Value" / ( "Size" ^ 2)). – Taras Mar 4 at 8:47
  • I think you have edited the question while I was answering. Anyway, the difference() function is to make your line start and end according to your point values (same you use to buffer the point layer). For this, I joined the values in my line features. I also had some @project_variables so I could easily adjust all sizes (point buffers and line start/ends) easily. – Albert Mar 4 at 8:58
  • Can you please explain what do you mean by @minimum_variable, @maximmum_variable, @minimum_variable2,@maximmum_variable2? – Taras Mar 4 at 9:01
  • Do you min/max of that certain field, e.g. "attributeFromStart" or "attributeFromEnd"? – Taras Mar 4 at 9:04
  • 1
    Let us continue this discussion in chat. – Albert Mar 4 at 12:37

1. Varying buffers' sizes

First of all Switch to Map Unit.

With suggestions from @Albert, I have joined buffer values from a layer for start_point and end_point points and moreover, I edited the circles and visualized them as Simple marker. Using the formula scale_linear("Value", minimum("Value"), maximum("Value"), 1, 6)

The required buffer size obeyed uses a scaling factor of 2, notice the difference in range_min, range_max between two functions.

        WHEN "From_Value" IS NOT NULL THEN scale_linear("From_Value", minimum("From_Value"), maximum("From_Value"), 2, 12)
        ELSE 0
    WHEN "To_Value" IS NOT NULL THEN scale_linear("To_Value", minimum("To_Value"), maximum("To_Value"), 2, 12)
    ELSE 0

Do not forget to double check values of start_point(), end_point() and min&max, they have to be the same between point and polyline layers.

2. Proper allocation of arrows

The correct expression

WHEN x(start_point($geometry)) - x(end_point($geometry)) < 0 THEN 2
WHEN x(start_point($geometry)) - x(end_point($geometry)) > 0 THEN NULL

The output will look like


Another solution can be achieved with the creation of two layers via a 'Virtual Layer' and then adjusting the styles manually. However, it is a bit time-consuming approach.

Layer 1 (Left/Exterior half):

FROM flow_map_sample
WHERE x(ST_StartPoint(geometry)) - x(ST_EndPoint(geometry)) < 0

Layer 2 (Right/Exterior half):

FROM flow_map_sample
WHERE x(ST_StartPoint(geometry)) - x(ST_EndPoint(geometry)) > 0

3. The width of the arrows

I needed to use: scale_linear("FLUX",minimum("FLUX"),maximum("FLUX"),0.1,3) * CASE WHEN "FLUX" > 1000 THEN 1 ELSE 0 END

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.