Details of good flow maps

In my previous post, I shared a flow map style that was inspired by a hand drawn map. Today’s post is inspired by a recent academic paper recommended to me by Radoslaw Panczak  and Thomas Gratier :

Jenny, B., Stephen, D. M., Muehlenhaus, I., Marston, B. E., Sharma, R., Zhang, E., & Jenny, H. (2016). Design principles for origin-destination flow maps. Cartography and Geographic Information Science, 1-15.

Jenny et al. (2016)  performed a study on how to best design flow maps. The resulting design principles are:

  • number of flow overlaps should be minimized;
  • sharp bends and excessively asymmetric flows should be avoided;
  • acute intersection angles should be avoided;
  • flows must not pass under unconnected nodes;
  • flows should be radially arranged around nodes;
  • quantity is best represented by scaled flow width;
  • flow direction is best indicated with arrowheads;
  • arrowheads should be scaled with flow width, but arrowheads for thin flows should be enlarged; and
  • overlaps between arrowheads and flows should be avoided.

Many of these points concern the arrangement of flow lines but I want to talk about those design principles that can be implemented in a QGIS line style. I’ve summarized the three core ideas:

  1. use arrow heads and scale arrow width according to flow,
  2. enlarge arrow heads for thin flows, and
  3. use nodes to arrange flows and avoid overlaps of arrow heads and flows

This slideshow requires JavaScript.

To get started, we can use a standard QGIS arrow symbol layer. To represent the flow value (“weight”) according to the first design principle, all arrow parameters are data-defined:


To enlarge the arrow heads for thin flow lines, as required by the second design principle, we can add a fixed value to the data-defined head length and thickness:



The main issue with this flow map is that it gets messy as soon as multiple arrows end at the same location. The arrow heads are plotted on top of each other and at some point it is almost impossible to see which arrow starts where. This is where the third design principle comes into play!

To fix the overlap issue, we can add big round nodes at the flow start and end points. These node buffers are both used to render circles on the map, as well as to shorten the arrows by cutting off a short section at the beginning and end of the lines:

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

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


It’s great to have some tried and evaluated design guidelines for our flow maps. As always: Know your cartography rules before you start breaking them!

    • Hi Ricardo! The dataset in this example is completely made up. I’m current working on a follow-up with actual migration data.

  1. dn said:

    Looks great, I might try it out!

  2. Renato Arbex said:

    Hi, great post. I am having difficulty trying to implement this style. How to properly use the geometry generator? I’ve chosen single symbol, added the arrows (ok), then tried inserting the Geometry generator symbol with the expression in the post. But it does not work. Any thoughts? Thanks!!

    • Renato Arbex said:

      Now it works ok! My the layer should be in the correct CRS (was using lat/lon and not UTM). And to show the circles one would need another Geometry Generator for the circles using buffer(end_point($geometry), 500) for example, or adding another layer with the points. Hope it helps.

      • Antony Zonato said:

        Yes, it was also my problem! Thank you for putting up the answer here!

  3. Wojtek said:

    Which CRS is ok?

    • Any CRS with units in meters will be ok. For other units, the values in the styles have to be adjusted.

      • Wojtek said:

        Excuse me. I thought about doing a curved arrows I can’t do that. I marked the curved arrows, but it doesn’t work.

      • To draw a curved arrow, the line needs to have one intermediate point between start and end. Depending on the intermediate point’s position, the line is more or less curved.

      • Wojtek said:

        Thank you Anita! Now is ok

  4. Edmond said:

    if we create one intermediate point between start and end, there will is a new circle?
    No ?

    • A circle piece will be constructed that goes through start, intermediate, and end point.

  5. Kristoffer said:

    Such a nice styling, you have made there!
    Is it possible to make the labels follow the curved lines?

    • Unfortunately, currently it is not possible to automatically generate labels that follow the shape of curved arrow symbol layers, as far as I know.

      • Kristoffer said:

        Thanks for your reply! Hopefully it will be possible in the future :-).

        Another question…I can see that you put in the following in the data defined override:

        scale_linear(“weight” ,1000,10000,0.1,8)
        * CASE WHEN “weight” < 1000 THEN 0 ELSE 1 END

        What does the * do in relation to the expression syntax?

      • * CASE WHEN “weight” < 1000 THEN 0 ELSE 1 END is just a multiplication with 0 or 1.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: