I am trying to work out to draw so-called spider network
or desire line
which illustrates movement of things (person, vehicle, etc.) between specific zones by direction.
This is the data frame that I am using:
df <- data.frame(O=c(1,2,4,4,4,6,6,6,7,7,7,9,9,9,9,10,10,10,11,12,12,12,32,86,108,128,128,157,157,157,157,157),
D=c(2,1,6,7,32,4,7,157,4,6,157,10,11,12,157,9,12,157,9,9,10,157,4,128,128,86,108,6,7,9,10,12),
trip=c(971,971,416,621,330,416,620,1134,621,620,625,675,675,378,439,675,724,472,675,378,724,563,330,610,405,610,405,1134,625,439,472,563),
lon.x=c(697746.6,696929.6,696748.8,696748.8,696748.8,694906.4,694906.4,694906.4,696769.4,696769.4,696769.4,698802.2,698802.2,698802.2,698802.2,698900.5,698900.5,698900.5,699686.7,696822.0,696822.0,696822.0,698250.7,702314.7,700907.1,702839.5,702839.5,694518.9,694518.9,694518.9,694518.9,694518.9),
lat.x=c(9312405,9311051,9308338,9308338,9308338,9307087,9307087,9307087,9305947,9305947,9305947,9304338,9304338,9304338,9304338,9302314,9302314,9302314,9306300,9303080,9303080,9303080,9309423,9320738,9321302,9322619,9322619,9301921,9301921,9301921,9301921,9301921),
lon.y=c(696929.6,697746.6,694906.4,696769.4,698250.7,696748.8,696769.4,694518.9,696748.8,694906.4,694518.9,698900.5,699686.7,696822.0,694518.9,698802.2,696822.0,694518.9,698802.2,698802.2,698900.5,694518.9,696748.8,702839.5,702839.5,702314.7,700907.1,694906.4,696769.4,698802.2,698900.5,696822.0),
lat.y=c(9311051,9312405,9307087,9305947,9309423,9308338,9305947,9301921,9308338,9307087,9301921,9302314,9306300,9303080,9301921,9304338,9303080,9301921,9304338,9304338,9302314,9301921,9308338,9322619,9322619,9320738,9321302,9307087,9305947,9304338,9302314,9303080))
df
contains following fields: O
: origin of trips D
: destination of trips trip
: number of trips between O
and D
lon.x
: longitude of origin zone lat.x
: lattitude of origin zone lon.y
: longitude of destination zone lat.y
: lattitude of destination zone
Currently I can draw following figure by the script here using geom_segment
in ggplot2
package:
library(ggplot2)
ggplot() +
geom_segment(data = df, aes(x = lon.x, y = lat.x, xend = lon.y, yend = lat.y, size = trip),
color = "blue", alpha = 0.5, show.legend = TRUE,
position = position_dodge2(width = 100)) +
scale_size_continuous(range = c(0, 5), breaks = c(300, 600, 900, 1200),
limits = c(100, 1200), name = "Person trips/day (over 100 trips)") +
theme(legend.key = element_rect(colour = "transparent", fill = alpha("black", 0))) +
guides(size = guide_legend(override.aes = list(alpha = 1.0))) +
geom_point(data = df, aes(x = lon.x, y = lat.x), pch = 16, size = 2.4)
The issue is that each line from O
to D
and from D
to O
are overlapped. I would prefer to plot the segments which are dodged based on the center line to properly visualize total number of trips and to see the balance of trips between zone pairs.
An example of desired result is shown below.
Dotted center line is not necessarily displayed (I just put it to show what the balance is). It is also preferable to change color by direction, for instance, red in clockwise and blue in anti-clockwise direction. Arrows are not necessary if direction can be shown in color.
I found some examples to solve the issue, however I cannot reach desirable result at this moment.
Calculation of offset for coordinates
It is not so easy to set offset for each direction in this example as I have around 80 zones which results in 6,400 pairs of zones.
Offset geom_segment in ggplot
position_dodge2 function
It says that I can set margin between segments in width
using variable, however if I use trip
in it, it returns error. Also, it is not clear how much should I set the value for appropriate offset to make segments follow center lines.
https://ggplot2.tidyverse.org/reference/position_dodge.html
geom_curve
and arrow
It is also possible to draw lines with curve so that above issue could be solved. However curved segments are messy to observe the movements in one figure. Arrows are also a bit difficult to see the direction as the shape of arrows are not sharp though I change its style.
color=variable
and position=dodge
I also tried to spread/gather
the df
to get new variable direction
and to delete OD-pairs in opposite direction so that I thought I can easily dodge segments using color=direction
and position=dodge
in ggplot2
, however it did not work well (segments are still overlapped). Small example is shown below.
O D trip direction lon.x lat.x lon.y lat.y
1 2 971 clock 697746.6 9312405 696929.6 9311051
2 1 300 anticlock 696929.6 9311051 697746.6 9312405
4 6 416 clock 696748.8 9308338 694906.4 9307087
4 7 621 anticlock 694906.4 9307087 696748.8 9308338
I highly appreciate your idea to obtain well-designed figure.
Please also see the following figure to get actual usage of spider network
.
You could use trig functions to calculate an offset value, then plug this into the ggplot()
call. Below is an example using your dataset above. I'm not exactly sure what you mean by clockwise, so I put in a simple dummy variable.
# make a dummy "clockwise" variable for now
df$clockwise = df$O > df$D
# angle from coordinates of stations
df$angle = atan((df$lat.y - df$lat.x)/(df$lon.y - df$lon.x))
# offsets from cos/sin of orthogonal angle
# scale the distance of the offsets by the trip size so wider bars offset more
# offset them one way if the trip is clockwise, the other way if not clockwise
df$xoffset = cos(df$angle - pi/2) * df$trip/5 * (2 * df$clockwise - 1)
df$yoffset = sin(df$angle - pi/2) * df$trip/5 * (2 * df$clockwise - 1)
ggplot() +
geom_segment(data = df, aes(x = lon.x + xoffset, y = lat.x + yoffset, xend = lon.y + xoffset, yend = lat.y + yoffset, size = trip, color = clockwise),
alpha = 0.5, show.legend = TRUE) +
scale_size_continuous(range = c(0, 5), breaks = c(300, 600, 900, 1200),
limits = c(100, 1200), name = "Person trips/day (over 100 trips)") +
theme(legend.key = element_rect(colour = "transparent", fill = alpha("black", 0))) +
guides(size = guide_legend(override.aes = list(alpha = 1.0))) +
geom_point(data = df, aes(x = lon.x, y = lat.x), pch = 16, size = 2.4) +
coord_fixed()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With