Drawing a World Flight Route Map with R

About the author

taoyan:a pretend coder, R language enthusiast, loves open source. Personal blog: https://ytlogos.github.io/


I. Introduction

This article visualizes world flight routes on top of NASA’s nighttime map ((https://www.nasa.gov/specials/blackmarble/2016/globalmaps/BlackMarble_2016_01deg.jpg), referencing several blog posts and visualization examples., referencing several blog posts and visualization examples.) 1. Package loading This post uses quite a few packages, loaded with the p_load() function from the pacman package.

library(pacman) p_load(tidyverse, data.table, geosphere, grid, jpeg, plyr)

2. Data preparation The data used comes from OpenFlights.org ((https://openflights.org/data.html)..) 3. Data download

download.file(“https://raw.githubusercontent.com/jpatokal/openflights/master/data/airlines.dat”,destfile = “airlines.dat”, mode = “wb”) download.file(“https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat”,destfile = “airports.dat”, mode = “wb”) download.file(“https://raw.githubusercontent.com/jpatokal/openflights/master/data/routes.dat”,destfile = “routes.dat”, mode = “wb”)

4. Data import

airlines <- fread(“airlines.dat”, sep = “,”, skip = 1) airports <- fread(“airports.dat”, sep = “,”) routes <- fread(“routes.dat”, sep = “,”)

5. Data wrangling

Add column namescolnames(airlines) <- c(“airline_id”, “name”, “alias”, “iata”, “icao”, “callisign”, “country”, “active”) colnames(airports) <- c(“airport_id”, “name”, “city”, “country”,”iata”, “icao”, “latitude”, “longitude”,”altitude”, “timezone”,”dst”,”tz_database_time_zone”,”type”, “source”) colnames(routes) <- c(“airline”, “airline_id”, “source_airport”, “source_airport_id”,”destination_airport”,”destination_airport_id”,”codeshare”, “stops”,”equipment”)

Type conversionroutes$airline_id <- as.numeric(routes$airline_id)

Merge the airlines and routes dataflights <- left_join(routes, airlines, by=”airline_id”)

Merge the flights and airports dataairports_orig <- airports[,c(5,7,8)] colnames(airports_orig) <- c(“source_airport”,”source_airport_lat”, “source_airport_long”) airports_dest <- airports[, c(5, 7, 8)] colnames(airports_dest) <- c(“destination_airport”, “destination_airport_lat”, “destination_airport_long”) flights <- left_join(flights, airports_orig, by = “source_airport”) flights <- left_join(flights, airports_dest, by = “destination_airport”)

Drop missing valuesflights <- na.omit(flights, cols = c(“source_airport_long”, “source_airport_lat”, “destination_airport_long”, “destination_airport_lat”))

The final data looks like thishead(flights[,c(1:5)])

6. Next, prepare the geographic data This article mainly visualizes connections between points on geographic data, which is easily done with the gcIntermediate() function from the geosphere package. For detailed usage, see here ((http://flowingdata.com/2011/05/11/how-to-map-connections-with-great-circles/)..)

Split the dataset by airlinesflights_split <- split(flights, flights$name)

Calculate intermediate points between each two locations flights_all <- lapply(flights_split, function(x) gcIntermediate(x[, c(“source_airport_long”, “source_airport_lat”)], x[, c(“destination_airport_long”, “destination_airport_lat”)], n=100, breakAtDateLine = FALSE, addStartEnd = TRUE, sp = TRUE))

Convert to a data frameflights_fortified <- lapply(flights_all, function(x) ldply(x@lines, fortify))

Unsplit lists flights_fortified <- do.call(“rbind”, flights_fortified)

Add and clean column with airline names flights_fortified$name <- rownames(flights_fortified) flights_fortified$name <- gsub(“..*”, “”, flights_fortified$name)

Extract first and last observations for plotting source and destination points (i.e., airports) flights_points <- flights_fortified %>% group_by(group) %>% filter(row_number() == 1 | row_number() == n())

II. Visualization Next comes the visualization. As mentioned earlier, we’re simply mapping data onto the nighttime Earth image provided by NASA, so first we need to get that background map. 1. Get and render the image

Download the imagedownload.file(“https://www.nasa.gov/specials/blackmarble/2016/globalmaps/BlackMarble_2016_01deg.jpg”,destfile = “BlackMarble_2016_01deg.jpg”, mode = “wb”)

Load and render the imageearth <- readJPEG(“BlackMarble_2016_01deg.jpg”, native = TRUE) earth <- rasterGrob(earth, interpolate = TRUE)

2. Data mapping Since there are so many airlines, we’ll pick a few well-known ones to visualize. (1) Lufthansa

ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.0, size = 0.0, data = flights_fortified) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, color = “#f9ba00”, data = flights_fortified[flights_fortified$name == “Lufthansa”, ]) + geom_point(data = flights_points[flights_points$name == “Lufthansa”, ], aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”),panel.grid.major = element_blank(),panel.grid.minor = element_blank(),axis.title = element_blank(),axis.text = element_blank(),axis.ticks.length = unit(0, “cm”),legend.position = “none”) +annotate(“text”, x = -150, y = -18, hjust = 0, size = 14,label = paste(“Lufthansa”), color = “#f9ba00”, family = “Helvetica Black”) + annotate(“text”, x = -150, y = -26, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

(2) Emirates

ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.0, size = 0.0, data = flights_fortified) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, color = “#ff0000”, data = flights_fortified[flights_fortified$name == “Emirates”, ]) + geom_point(data = flights_points[flights_points$name == “Emirates”, ], aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks.length = unit(0, “cm”), legend.position = “none”) + annotate(“text”, x = -150, y = -18, hjust = 0, size = 14, label = paste(“Emirates”), color = “#ff0000”, family = “Fontin”) + annotate(“text”, x = -150, y = -26, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

(3) British Airways

ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.0, size = 0.0, data = flights_fortified) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, color = “#075aaa”, data = flights_fortified[flights_fortified$name == “British Airways”, ]) + geom_point(data = flights_points[flights_points$name == “British Airways”, ], aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks.length = unit(0, “cm”), legend.position = “none”) + annotate(“text”, x = -150, y = -18, hjust = 0, size = 14, label = paste(“BRITISH AIRWAYS”), color = “#075aaa”, family = “Baker Signet Std”) + annotate(“text”, x = -150, y = -26, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

(4) Air China

ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.0, size = 0.0, data = flights_fortified) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, color = “#F70C15”, data = flights_fortified[flights_fortified$name == “Air China”, ]) + geom_point(data = flights_points[flights_points$name == “Air China”, ], aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks.length = unit(0, “cm”), legend.position = “none”) + annotate(“text”, x = -150, y = -18, hjust = 0, size = 14, label = paste(“Air China”), color = “#F70C15”, family = “Times New Roman”) + annotate(“text”, x = -150, y = -26, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

(5) China Southern Airlines

ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.0, size = 0.0, data = flights_fortified) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, color = “#004D9D”, data = flights_fortified[flights_fortified$name == “China Southern Airlines”, ]) + geom_point(data = flights_points[flights_points$name == “China Southern Airlines”, ], aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks.length = unit(0, “cm”), legend.position = “none”) + annotate(“text”, x = -150, y = -18, hjust = 0, size = 14, label = paste(“China Southern Airlines”), color = “#004D9D”, family = “Times New Roman”) + annotate(“text”, x = -150, y = -26, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

(6) Mapping the routes of several airlines at once

Extract the data subsetflights_subset <- c(“Lufthansa”, “Emirates”, “British Airways”) flights_subset <- flights_fortified[flights_fortified$name %in% flights_subset, ] flights_subset_points <- flights_subset%>% group_by(group)%>% filter(row_number()==1|row_number()==n())

Visualization ggplot() + annotation_custom(earth, xmin = -180, xmax = 180, ymin = -90, ymax = 90) + geom_path(aes(long, lat, group = id, color = name), alpha = 0.2, size = 0.3, data = flights_subset) + geom_point(data = flights_subset_points, aes(long, lat), alpha = 0.8, size = 0.1, colour = “white”) + scale_color_manual(values = c(“#f9ba00”, “#ff0000”, “#075aaa”)) + theme(panel.background = element_rect(fill = “#05050f”, colour = “#05050f”), panel.grid.major = element_blank(), panel.grid.minor = element_blank(), axis.title = element_blank(), axis.text = element_blank(), axis.ticks.length = unit(0, “cm”), legend.position = “none”) + annotate(“text”, x = -150, y = -4, hjust = 0, size = 14, label = paste(“Lufthansa”), color = “#f9ba00”, family = “Helvetica Black”) + annotate(“text”, x = -150, y = -11, hjust = 0, size = 14, label = paste(“Emirates”), color = “#ff0000”, family = “Fontin”) + annotate(“text”, x = -150, y = -18, hjust = 0, size = 14, label = paste(“BRITISH AIRWAYS”), color = “#075aaa”, family = “Baker Signet Std”) + annotate(“text”, x = -150, y = -30, hjust = 0, size = 8, label = paste(“Flight routes”), color = “white”) + annotate(“text”, x = -150, y = -34, hjust = 0, size = 7, label = paste(“ytlogos.github.io || NASA.gov || OpenFlights.org”), color = “white”, alpha = 0.5) + coord_equal()

III. SessionInfo

sessionInfo()> R version 3.4.3 (2017-11-30) Platform: x86_64-w64-mingw32/x64 (64-bit) Running under: Windows >= 8 x64 (build 9200) Matrix products: default locale: [1] LC_COLLATE=Chinese (Simplified)_China.936 LC_CTYPE=Chinese (Simplified)_China.936 [3] LC_MONETARY=Chinese (Simplified)_China.936 LC_NUMERIC=C [5] LC_TIME=Chinese (Simplified)_China.936 attached base packages: [1] grid stats graphics grDevices utils datasets methods base other attached packages: [1] plyr_1.8.4 jpeg_0.1-8 geosphere_1.5-7 data.table_1.10.4-3 [5] forcats_0.2.0 stringr_1.2.0 dplyr_0.7.4 purrr_0.2.4 [9] readr_1.1.1 tidyr_0.8.0 tibble_1.4.2 ggplot2_2.2.1.9000 [13] tidyverse_1.2.1 pacman_0.4.6 loaded via a namespace (and not attached): [1] Rcpp_0.12.15 cellranger_1.1.0 pillar_1.1.0 compiler_3.4.3 bindr_0.1 [6] tools_3.4.3 lubridate_1.7.1 jsonlite_1.5 nlme_3.1-131 gtable_0.2.0 [11] lattice_0.20-35 pkgconfig_2.0.1 rlang_0.1.6 psych_1.7.8 cli_1.0.0 [16] rstudioapi_0.7 yaml_2.1.16 parallel_3.4.3 haven_1.1.1 bindrcpp_0.2 [21] xml2_1.2.0 httr_1.3.1 knitr_1.19 hms_0.4.1 glue_1.2.0 [26] R6_2.2.2 readxl_1.0.0 foreign_0.8-69 sp_1.2-7 modelr_0.1.1 [31] reshape2_1.4.3 magrittr_1.5 scales_0.5.0.9000 rvest_0.3.2 assertthat_0.2.0 [36] mnormt_1.5-5 colorspace_1.3-2 stringi_1.1.6 lazyeval_0.2.1 munsell_0.4.3 [41] broom_0.4.3 crayon_1.3.4