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 @RPanczak and Thomas Gratier @ThomasG77:
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:
- use arrow heads and scale arrow width according to flow,
- enlarge arrow heads for thin flows, and
- use nodes to arrange flows and avoid overlaps of arrow heads and flows
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:
scale_linear("weight",0,10,0.1,3)
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:
scale_linear("weight",0,10,0.1,1.5)+1.5
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:
difference( difference( $geometry, 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!
PS: To draw a curved arrow, the line needs to have one intermediate point between start and end – so three points in total. Depending on the intermediate point’s position, the line is more or less curved.
Hello, nice post.
Could you share the database used here?
Thanks
Hi Ricardo! The dataset in this example is completely made up. I’m current working on a follow-up with actual migration data.
Looks great, I might try it out!
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!!
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.
Yes, it was also my problem! Thank you for putting up the answer here!
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.
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.
Thank you Anita! Now is ok
Hello,
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.
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.
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)
+1.5
* 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.Hi, I have found a workaround for this by using the intermediate point as the reference for the label. This won´t bend the label in any way but has done the trick for my visualization.
Is there a way to use the gradient fill to make the first part of the line transparent? I have a hard time fiddling with this and have not found a solution so far. The only line which should be transparent is the one which starts at the point – so only the outgoing ones, not the ingoing ones.
It’s a bit tricky indeed, I’ve posted the answer in https://anitagraser.com/2017/02/03/gradient-arrows/
Great post! How can I control the styling of the circular nodes created with the geometry generator? Mine don’t seem to have a border – or maybe it is white?!
Hi Brent, you need to adjust the style of the buffer polygon’s fill symbol layer.
Thanks for the reply. I still can’t get it to work. When you say :
“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:”
My lines are shortened, but I am not getting the”circles on the map” part.
I am adding a “Geometry generator” using the “LineString / MultiLineString” “Geometry Type”.
Do I need to add a second “Geometry generator”? I so, what options should I use?
That’s correct. As shown in the post’s last screenshot, there are two additional geometry generators which define buffers around the line start and end points.
Ok, I got it work using a second Geometry generator:
Geometry type – Polygon/MultiPolygon
Expression – buffer(start_point($geometry), 1000)
I had to separate the two Geometry generators into two layers (one to shorten the lines and another for the node buffers) in order to get my node buffers (white fill, black outline) to draw on top of the lines to mask the lines passing under the node buffers. When using both Geometry generators in the same layer, the lines always draw on top of the nodes, irrespective of the order they a placed in the symbol layer hierarchy. I’m using QGIS 2.18.3 on Windows.
Thanks again for this awesome post! I had been following the flow maps research at http://cartography.oregonstate.edu/cartography.html and your implementations are really impressive!
Hello, thank you for this nice trick. I still can’t figure how to make those node and therefore cut arrow.
Also I can’t find where you make your flow “round”and not a straight line.
Thank you again
Please see the previous comments. Both issues have been discussed there.
Ok I got it, but had to read a couple of times each comments.
To made the nodes you need to had 2 layers (1 for the beginning of the line and 1 for the ending). With parameters given by Brent Edwards
And if you whant to make the lines “round” you have to add manually a point in the midle of the line then drag this point. There is no automatic way to do it right now.
Regards
Could you post a step by step explanation of how to create these curved lines? I have read through the post, other posts, and all the comments multiple times and I am unable to figure out the steps I need to take. E.g. what did the input(s) consist of? Could I use one shapefile with a beginpoint geom and an endpoint geom? Then, how do I make a line? How do I add a third point in and how do I manually drag it to create a curve? This is a beautiful style and I’d love to be able to recreate it.
Hi Anita, how are you? I accompany your posts about QGIS and congratulate you for your excellent explanations, so I am having difficulty producing my first flow map regarding slave labor here in the state of Maranhão in Brazil, so I wanted to learn the first steps to the elaboration Of this kind of very complex map, since I could not find a tutorial or video of this nature. Thank you for your attention.
Hi Igor,
The basic flow map is really just lines that are styled using an arrow symbol layer. There is a short but to-the-point description of arrow symbol layers in the official documentation: http://docs.qgis.org/testing/en/docs/user_manual/working_with_vector/style_library.html#line-symbols
Ah yes, I understood the process of creating the arrows, but Anita after looking at the official document that you indicated to me and your last post, I realized that when I open my property of the layer and I try to edit the symbol layer type appear only three options: Geometry generator, marker line and simple line, unfortunately, does not appear the ARROW option as you demonstrate in your gif, how do I display this option? I use QGIS 2.14.8.
You need at least 2.16 to get arrow symbol layers, see http://changelog.qgis.org/en/qgis/version/2.16.0/#category-3
Thank you so much Anita,you really helped me, thank you for your attention and your patience hahaha.
Regards
Hi Anita
I used your ideas in my article on the GoGeomatics site: https://gogeomatics.ca/mapping-migration-flows-with-qgis-flowmapper-plugin/ Hope you like it!
Thanks for sharing Jim!
Hello,
What a nice Styling this one :)
Is there a way to automate the process of creation of the new point to make the lines curved?
As far as I know, there are no ready-to-use tools yet.
Hi Anita
Great post but as a beginner user I got lost in step 3.
First of all how do I ensure the line dataset is in CARD metre? Also can you explain how the expression works? I presume the command is to get the program to render the line with buffer at the start and end, with the two polygon geometry of 10000m at both ends?
Thanks
As stated above, the expression is “cutting off a short section at the beginning and end of the lines”. Drawing the circles is handled separately and not influenced by this expression.
To ensure that the CRS units are in meters, check your dataset’s metadata and reproject if necessary.
Thanks for the prompt response. So did you draw the circles manually (as a separate point layer) or was it generated by the expression?
If you check the last screenshot, you’ll see that the symbol has 3 geometry generator layers: 1 for the arrows and 2 for the circles: one creating circles at the line starting point and the other at the line end point. These two geometry generators just contain a buffer expression.