# Generating Distributive Flow Maps with ArcGIS

Editor: Please refer to this blog posting for an updated distributive flow tool.

This blog post was written by Brad Simantel, a summer intern in the Applications Prototype Lab.

Flow maps are used to show the movement of goods or people from one place to another. These maps use lines to symbolize the movement, often varied in width to represent the quantity of the flow, and fall into one of three categories: radial, network, and distributive. Radial flow maps are used to show relationships between one source and many destinations. If there are more than a handful of destinations and you still want to show the the quantity of flow, however, the lines overlap too much to discern individual values. Network flow maps are used to show the quantity of flow over some existing network — transportation and communication networks being the most common. Distributive flow maps are similar to radial flow maps, but rather than having individual lines from the source to each destination, lines are joined together, only forking once they get close to their destinations.

A distributive flow map made by Charles Joseph Minard showing coal exports from England in 1864.

Unfortunately, there are no built-in tools in ArcGIS to generate distributive flow maps — the only way to make one with the software currently is to draw and attribute it by hand. So I wrote my own.

How it Works

First, the tool takes source and destination features and converts them to points, if they’re not already. It then generates a grid using the Create Fishnet tool, which will ultimately give the finished flow lines some structure, and converts that grid to points. Next, it creates three Euclidean distance rasters — one based on distance from the source, one based on the destinations, and one based on the grid centroids — and divides them into an equal number of slices, depending on the settings when you run the tool. These three rasters are then added together, creating a surface that can be used to determine least-cost paths from the source to the destinations. After that, it runs the Flow Accumulation tool to determine the quantity of goods or people upstream of any given pixel, and then converts the output to vector lines (which now have the quantity of upstream goods or people as an attribute).

Results

Here’s the output of the tool when run using the data from Minard’s coal export map, seen above:

This is without any finessing – just the raw output of the tool. See below for information about how this raw output can be improved.

Here’s another example, showing migration from California in 2010:

Again, this is the raw output.

Generating Flow Maps

If you’d like to generate some flow maps yourself, you can download my tool here. Once you’ve downloaded it, the first step before running the tool is making sure your data is suitable. You’ll need:

• A source feature class containing one single feature.
• A destination feature class containing all destinations and a Z-value attribute (any features with a Z-value of null or zero will be discarded). Basically anything you could use to make a choropleth or graduated symbol map you can use to make a flow map.
• Optionally, a feature class which you would like the lines to avoid. An impassable feature class, if you will.
• Lastly, you’ll need all your data to be projected, ideally in the same projection as your data frame.

If you don’t have any data of your own but want to follow along, you can download the Minard-based dataset seen above here.

To run the tool, open ArcMap and find the FlowMapGenerator Python toolbox you’ve just downloaded using the catalog window, then double-click it to run the FlowMapGenerator tool. You should see this:

Let’s go through these fields one by one.

• Source Feature Class: This is the source of your flow, and must be a feature class containing only one feature. In the Minard example, that would be England.
• Destination Feature Class: These are the destinations of your flow. The features in this feature class also have to have a quantity attribute that will ultimately be inherited by the line leading to it.
• Z-Value: This is the quantity attribute I was referring to, and will be used to symbolize the magnitude of flow to a given destination.
• Impassable Feature Class: An optional feature class which allows you to influence how the flow lines are drawn. Whichever feature class you select will be treated as nearly impassable (the lines will cross the features in this feature class only when there are no other options). In the Minard example, I used a land feature class for this field.
• Processing Extent: For raster analysis you have to choose a processing extent. I recommend using the Same as Display option and setting your extent visually.
• Cell Size: This is also for raster analysis purposes. On a global scale, I’d recommend something in the ten to twenty kilometer range.
• Source Weight: This field lets you choose how much influence you’d like the distance from source to have over your flow lines. The bigger the number, the more likely your lines are to beeline for the source.
• Destination Weight: Same as Source Weight, but for destinations. If you want your lines to prefer going through or near your destinations, increase this number.
• Grid Weight: Again, the same. If you want your lines to clump together for longer before fanning out, use a higher number here.
• Grid Size: This field determines the size of the grid. If you use a larger grid, your lines are more likely to clump together (rather than fanning out and all heading in the same direction). The grid will be your cell size multiplied by this number.
• Output Feature Class: Choose the name and location of your output polyline feature class.

Finessing and Symbolization

Once you’ve run the tool, you’ll be left with a fairly inorganic-looking flow map. If you like that look, congratulations! You’re done! If you want to smooth out some of those rough edges, though, we can run the Smooth Line tool. For the Minard map below, I ran it with a tolerance of 1,000 kilometers. Smoothing isn’t enough, though — there are still too many straight lines. Unfortunately, the only way to fix that is by hand. At least you didn’t have to draw and attribute everything manually, though! Here’s my final result after a little bit of finessing:

This is symbolized using graduated symbols with ten classes ranging in width from 0.5 pts to 7 pts. I’d prefer to use proportional symbols so you could see the the actual values, but then the source overwhelms the map.

Assuming you’ve been following along, congratulations! You just saved yourself hours of manually drawing and attributing lines. If you have any questions, leave a comment below.

UPDATE (11/27/2012):

Expanding python toolbox will reveal one script tool as shown below:

By default, python toolboxes do not display the “pyt” file extension. To display this and other known extensions in ArcCatalog (or the catalog window in ArcMap) check the following:

This dialog is accessible in ArcCatalog by clicking Customize > ArcCatalog Options.  Unchecking the highlighted option will show the following:

I work at Esri APL
This entry was posted in Uncategorized and tagged . Bookmark the permalink.

1. billwie says:

Perhaps we could have the graphics for articles sourced from somewhere besides tumblr? I don’t agree with my work’s web filtering, but it is blocking tumblr as social media.

2. mringel says:

The instructions say to doubeclick on the python script in the catalog window to run it. But catalog doesn’t show files with a .pyt extension. Every other source on the internet says that you can’t run python scripts in this way. What is the correct way to run this script in ArcMap?

• Richie says:

Hi mringel,
The blog posting has been amended with instruction on how to show the .pyt extension in ArcCatalog or the catalog window in ArcMap. Hope this helps.

3. rpudney says:

Really like this tool, however I’m experiencing problems with the impassable feature class option – regardless of the feature class I am putting in, the script is failing at:

File “”, line 222, in execute
File “c:\program files(x86)\…\arcpy\conversion.py”, line 2389, in PointToRaster
raise e
Execute Error: Failed to Execute. Parameters are not valid.
ERROR 000366: Invalid Geometry Type

- I’ve tried multiple different land feature classes and have created even simple polygons to test this, with the same result.

The tool works perfectly without the impassable land feature class option being used, so I’m just wondering if you have any advice on why this error might be occurring?

• Rich S says:

Hi,

I’ve just had the exact same issue and someone quickly pointed out what was wrong with the code.

Line 213: desc = arcpy.Describe(sourceFeature.value)

This basically gets the input (or origin) feature layer rather than the impassable layer. If you use a polygon as your source layer then it will process without problem but anything else (such as a point) and it will return a point geometry type, which it then falls over on.

Simply edit the FlowMapGenerator.pyt in Notepad (or from ArcCatalog) and go to line 213. Change it to:

desc = arcpy.Describe(impassable.value)

This will then use the geometry type of your impassable layer rather than your source layer.

• Richie says:

Hi rpudney & Rich S,

Below are two additional list of code (in bold) that enforce two rules, that is:
1) The first parameters (source) must be either a point or polygon layer, and
2) The impassable layer must be a polygon.

These corrections should address the problems you are experiencing.

``` class FlowMapGenerator(object): def __init__(self): """Define the tool (tool name is the name of the class).""" self.label = "FlowMapGenerator" self.description = "Generates a distributive flow map from one source to many destination points. If you input a polygon feature class for your source or destinations, centroids will be used as points. Make sure that your source, destination, and (optional) impassable feature classes are all projected." self.canRunInBackground = True```

``` ```

``` def getParameterInfo(self): """Define parameter definitions.""" sourceParameter = arcpy.Parameter( displayName = "Source Point/Polygon Feature Class", name = "source", datatype = "Feature Layer", parameterType = "Required", direction = "Input") sourceParameter.filter.list = ['Point', 'Polygon'] ### Added point and polygon filter destinationParameter = arcpy.Parameter( displayName = "Destination Point/Polygon Feature Class", name = "destination", datatype = "Feature Layer", parameterType = "Required", direction = "Input") zValueParameter = arcpy.Parameter( displayName = "Z Value", name = "z_value", datatype = "Field", parameterType = "Required", direction = "Input") zValueParameter.parameterDependencies = [destinationParameter.name] impassableParameter = arcpy.Parameter( displayName = "Impassable Polygon Feature Class", name = "impassable", datatype = "Feature Layer", parameterType = "Optional", direction = "Input") impassableParameter.filter.list = ['Polygon'] ### Added polygon filter ```

• jtbrost says:

This is an awesome tool! However, even after editing the script with the above suggestions I’m still getting an output feature class that is crossed by the polygon layer I set as the impassible input. Any further suggestions on how to correct this?

4. masrudyomri says:

This is a great tool but but I keep getting either IndexError: List index out of range or Error 010244: Could not set analysis window / Error 10067: Error in executing grid expression. Not sure why though. Anyone?

5. otravaglini says:

Hello,

We are currently trying to use the tool. It’s working when we don’t add impassable shape but when we do we have the same error as the last comment posted: Error 10067: Error in executing grid expression. We don’t understand why. Anyone got an idea?
Thanks.

6. kwresinski says:

Thanks for the tool. How might I solve this?

Traceback (most recent call last):
File “”, line 180 in execute
ValueError: invalid literal for int() with base 10: ‘MAXOF’

Thanks again!

• Richie says:

@kwresinski
On the Cell Size parameter try selecting “As Specified Below” from the drop down and enter a number such as 200,000 (for global extent).

• ligia.gafitescu says:

same problem as @kwresinski, tried increasing the cell size. still no