We all know the route maps shown by airlines and the news, those arched lines connecting two points on a map to visualize the path a plane is traveling. Well, how cool would it be if you could do that with the places you have flown? These maps are not difficult to make and make a large visual impact, so please place your tray tables in the upright position and prepare for take-off.
The Data
For this project we will need a dataframe with six columns. They are as follows: an origin city, a destination city, latitude of origin city, longitude of origin city, latitude of destination city, and longitude of destination city. The latitude and longitude can be looked up on the internet or if you have a long list of cities you can use geocoding, but that’s a topic for another time. For this example, I generated five flights out of New York because I may or may not live around there….
origin_city<-c('New York, NY','New York, NY','New York, NY','New York, NY','New York, NY')
destination_city<- c('Dallas, TX', 'Burlington, VT', 'Tampa, FL', 'Los Angeles, CA', 'Seattle, WA')
o_lat<-c(40.71836, 40.71836, 40.71836, 40.71836, 40.71836)
o_long<-c(-73.97022, -73.97022, -73.97022, -73.97022, -73.97022)
d_lat<-c(32.81273, 44.47404, 28.00625, 33.98267, 47.46841)
d_long<-c(-96.97200, -73.14792, -82.51319, -118.10430, -122.27470)
df<-data.frame(origin_city,destination_city,o_lat,
o_long,d_lat,d_long)
The Base Map
To begin, lets throw down the packages we will need for this map.
library(ggplot2) #for the good stuff
library(ggrepel) #for labels
library(maps) #for the map
library(dplyr) #for bonus stuff
Using the map
s package we will ask it to generate a map of the lower 48 United States that will be the base layer we will work on.
Lower48 <- map_data("state")
Initial Mapping
Whenever I make a complicated visualization with ggplot2
I like to take advantage of the layering aspect of the package. The graph will be built one layer at a time to make sure everything works then the code for the map can be put into one big block so final adjustments can be made.
Let’s start by laying down our map of the lower 48. This time the borders will be gray and the fill will be tan. I used a hex code to show that these functions take both hex formats and regular color names.
g<-ggplot() + geom_polygon( data=Lower48, aes(x=long, y=lat, group=group), color="#7D7978", fill="tan" )
The City Dots
Next let’s put in the points that represent the cities. This is done once for the destination cities then another line is used for the origin cities. Since all five origin cities are New York, NY this line of code actually places five dots on top of each other, but it is impossible to tell on the map, so I will leave it this way.
g<- g+geom_point(data=df, aes(x = d_long, y = d_lat), col = "black")+
geom_point(data=df, aes(x = o_long, y = o_lat), col = "black")
The City Labels
Now we can add the names of the cities next to the points. For this we can use geom_text()
. For the origin and destination cities everything works nicely, but we are writing New York, NY five times on top of each other. If you are getting sick of me doing this I will add the remedy at the end.
g<- g+geom_text (data=df, aes(x = d_long,y = d_lat, label = destination_city), col = "black")+
geom_text (data=distinct(df,origin_city,.keep_all = T),aes(x = o_long, y = o_lat, label = origin_city), col = "black")
Adding the Flight Lines
Finally, the fun part, the flight paths. These can be added with the geom_curve()
function. You need to give the curve a start point (x and y) and an end point (xend and yend). The following arguments are for the size of the line and the intensity of the curve.
g<- g + geom_curve(data=df, aes(x = o_long, y = o_lat, xend = d_long, yend = d_lat),col = "red", size = 1, curvature = .2)
Putting it All Together
Now that each layer of the map works well together we can combine the lines and make a single block of code to produce the map.
ggplot() + geom_polygon( data=Lower48, aes(x=long, y=lat, group=group), color="#7D7978", fill="tan" ) +
geom_curve(data=df, aes(x = o_long, y = o_lat, xend = d_long, yend = d_lat), col = "red", size = 1, curvature = .3) +
geom_point(data=df, aes(x = d_long, y = d_lat), col = "black")+
geom_point(data=df, aes(x = o_long, y = o_lat), col = "black")+
geom_text_repel (data=df, aes(x = d_long,y = d_lat, label = destination_city), col = "black")+
geom_text_repel (data=distinct(df,origin_city,.keep_all = T), aes(x = o_long, y = o_lat, label =origin_city), col = "black")
Bonus Information
If you have a keen eye for R code you’ll notice I made a few changes. These are aesthetic points that aren’t necessary, but will give your map that extra sparkle.
- The order of the statements is changed to map, curves, dots, labels. This way your curves don’t obscure the dots and in turn the dots don’t obscure the city labels.
geom_text()
has been changed togeom_repel()
this is a more complicated function that will prevent labels form bumping into other elements of the map. If you make a lot of complex maps I highly recommend looking it up.- The line for the labels of the origin cities doesn’t draw directly from the original dataframe. If you remember the origin cities are all New York, NY so by adding the
distinct()
function from the packagedplyr
we can create a dataframe with unique origin cities. In our case it just returns one row with New York, NY. Try runningdistinct(df,origin_city,.keep_all = T)
for clarification.
Conclusion
In this post we went through how to add points, text, and lines to a map to generate a flight map. I hope you were able to see my method of making complex visualizations in ggplot2
and that a complex map or plot is really just a lot of simple elements strung together. If you want to take this project further I’d encourage you to play with some of the themes and titles that ggplot2
has to offer. Maybe even try this on a world map. If you have any cool ideas for improvement I’d love to hear them or if you make your own map I’d love to see it. Thanks for the read and happy flying!
Categories: Uncategorized