
Create Air Travel Route Maps with ggplot2

Peter Prevos |
702 words | 4 minutes
Share this content
I have been lucky to fly to a few countries around the world. Like any other bored traveller, I thumb through the airline magazines and look at the air travel route maps. These maps are beautifully stylised depictions of the world with gently curved lines between the destinations serviced by the airline. I always wanted such a map for my own travel adventures. This article explains how to create air travel route maps with ggplot2 in the style of the Emirates Airlines route map.
Create Air Travel Route Maps using ggplot2
The first step was to create a list of all the places I have flown between at least once. Paging through my travel photos and diaries, I managed to create a pretty complete list. The structure of this document is simply a list of all routes (From, To) and every flight only gets counted once. The next step finds the spatial coordinates for each airport by searching Google Maps using the geocode function from the ggmap package. You will need a Google API to enable the geocoding function.
In some instances, I had to add the country name to avoid confusion between places. To prevent errors from the Google maps API, I have added a while loop that runs until all destinations have been geocoded. We now we have a data frame of airports with their coordinates and can create air travel route maps. The data frames are merged so that we can create air travel route maps using the curve geom. The borders function of ggplot2 creates the map data. The ggrepel package helps to prevent overplotting of text. This code also removes any return flights and splits flights that crossed the date line.

In another article, I have used the same principle to create a route map of flights between islands in the Pacific Ocean using the schedules from several international airlines and to show demonstrate the nonsense of Flat Earth theories.
## Flightpath map
## https://lucidmanager.org/create-air-travel-route-map
## Init
library(tidyverse)
library(ggmap)
library(ggrepel)
api <- readLines("../google-maps.api") # Text file with the API key
register_google(key = api)
## Read flight and airports lists
#flights <- read_csv("flights.csv")
flights <- read_csv("flight-map/flights.csv")
airports_file <- "flight-map/airports.csv"
if (file.exists(airports_file)) {
airports <- read_csv(airports_file)
} else {
airports <- tibble(airport = NA, lon = NA, lat= NA)
}
## Lookup coordinates
## Some airports need country names to ensure Google finds the correct location
## The geocoding keeps looping till all coordinates have been found
destinations <- unique(c(flights$From, flights$To))
new_destinations <- destinations[!destinations %in% airports$airport]
while (length(new_destinations) > 0) {
new_airports <- geocode(new_destinations) %>%
mutate(airport = new_destinations) %>%
select(airport, lon, lat)
airports <- rbind(airports, new_airports) %>%
filter(!is.na(lon) | !is.na(lat))
new_destinations <- destinations[!destinations %in% airports$airport]
}
write_csv(airports, "flight-map/airports.csv")
## Remove country names
airports$airport <- as.character(airports$airport)
comma <- regexpr(",", airports$airport)
airports$airport[which(comma > 0)] <- substr(airports$airport[which(comma > 0)], 1, comma[comma > 0] - 1)
## Remove return flights
d <- vector()
for (i in 1:nrow(flights)) {
d <- which(paste(flights$From, flights$To) %in%
paste(flights$To[i], flights$From[i]))
flights$From[d] <- "R"
}
flights2 <- flights %>%
filter(From != "R") %>%
select(From, To)
## Add coordinates to flight list
flights <- merge(flights, airports, by.x = "From", by.y = "airport")
flights <- merge(flights, airports, by.x = "To", by.y = "airport")
flights <- flights %>%
select(From, To, lon.x, lat.x, lon.y, lat.y) %>%
as_tibble()
## Split Circumnaviation Flights at -180/180 degrees
circ <- which(abs(flights$lon.y - flights$lon.x) > 180)
flights[circ,]
flights$lon.y[circ] <- ifelse(flights$lon.y[circ] < 0, 180, -180)
flights$lat.y[circ] <- rowSums(flights[circ, c("lat.x", "lat.y")]) / 2
leg2 <- airports %>%
filter(airport %in% flights$To[circ]) %>%
mutate(From = rep("", length(circ))) %>%
mutate(lon.x = -flights$lon.y[circ], lat.x = flights$lat.y[circ]) %>%
select(From, To = airport, lon.x, lat.x, lon.y = lon, lat.y = lat)
flights <- rbind(flights, leg2)
## Plot flight routes
worldmap <- borders("world2", colour="#efede1", fill="#efede1")
ggplot() + worldmap +
geom_point(data = airports, aes(x = lon, y = lat), col = "#970027") +
geom_text_repel(data=airports, aes(x = lon, y = lat, label = airport), col = "black", size = 2, segment.color = NA) +
geom_curve(data = flights, aes(x = lon.x, y = lat.x, xend = lon.y, yend = lat.y), col = "#b29e7d", size = .4) +
theme_void()
ggsave("flights_map.png", dpi = 300)
Share this content