I am interesting in creating a map that looks like this (found at https://www.axios.com/2017/12/15/the-flow-of-goods-between-states-1513304375):
Specifically, I want to depict flows between regions on a map with curved lines, and indicate bigger flows with wider lines, and also use arrows to show the direction of the flow. If possible, I would also like the line from A to B to not be on top of the line from B to A in order for the viewer to distinguish between the two. And preferably use ggplot2
, though I am open to other solutions.
I will note that there are related questions (such as How can I add directional arrows to lines drawn on a map in R?, How to create a map chart with direction arrows in R?, plotting email flow in map using R, and https://flowingdata.com/2011/05/11/how-to-map-connections-with-great-circles/), but I was wondering if there is a solution that allows me to incorporate all the elements at once. (And I am not sure if prior solutions tackle the problem of not having A to B and B to A overlap.)
Yes, it is possible with ggplot2
(tidyverse
) and sf
. The curvature
of geom_curve()
need to be different of 0, ex. 0.5, in order to create an ellipse without overlapping arrows, A--> B and B-->A.
Here is a quick attempt. You might want creating bins for alpha and the linewidht.
library(sf)
library(tidyverse)
# Shapefile of US in a almost random CRS, downloaded from
# https://www2.census.gov/geo/tiger/GENZ2018/shp/cb_2018_us_state_20m.zip
us_shp = st_read( "cb_2018_us_state_20m/cb_2018_us_state_20m.shp") %>%
st_transform("ESRI:102003")
# State center (arrow start and end), from datasets::
state_center = state.center %>% as_tibble() %>% st_as_sf(coords=c("x","y") ,
crs = 4326, remove=FALSE)%>%
st_transform("ESRI:102003") %>%
mutate(state_id = 1:n()) %>%
dplyr::mutate(x = sf::st_coordinates(.)[,1],
y = sf::st_coordinates(.)[,2])
# Creation of a 50*50 dataframe, but we will select some states only at the end
state_exchange = expand.grid(state1= 1:50, state2 = 1:50 ) %>%
mutate( values = rnorm(50*50)) %>%
as_tibble() %>%
filter(state1!=state2) %>%
filter( state1 <4, state2<4)
# Adding start-end
state_exchange_sf = state_exchange %>%
left_join( state_center %>% st_drop_geometry(),
by = c("state1" = "state_id"),
suffix= c("","start") ) %>%
left_join( state_center %>% st_drop_geometry(),
by = c("state2" = "state_id"),
suffix= c("","end"))
ggplot()+geom_sf(data= us_shp)+
geom_sf(data=state_center)+
geom_curve( data =state_exchange_sf %>%
st_as_sf(coords= c("x","y"), crs= "ESRI:102003", remove=FALSE),
aes(x= x, y= y, xend= xend, yend=yend,
alpha= values , linewidth = values),
curvature = -0.5 , arrow= grid::arrow() )