Create Air Travel Route Maps that look like airline route maps you find in aeroplane magazines using ggplot. Spatially visualise your travel diary.

Create Air Travel Route Maps with ggplot2

Peter Prevos

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.

My personal Air Travel Route Maps in ggplot
My personal Air Travel Route Maps in ggplot: A Visual Travel Diary

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

You might also enjoy reading these articles

Mapping the Ancient World: A Digital Odyssey through Ptolemy's Geography

Cheesecake Diagrams: Pie Charts with a Different Flavour

Mapping and Geocoding with ggmap and the Google API