Mapping the World’s Biggest Airlines

The map above shows the routes flown by the top 7 airlines (by international passenger distance flown). The base map shows large urban areas and I have attempted to make it look a bit like the beautiful “Earth at Night” composite image produced by NASA. You can clearly see a relationship between where people live and where the big carriers fly to across Europe and the US but India and much of China have relatively few routes. I expect much of the slack is picked up by smaller airlines in these countries but they must represent key growth areas the world economy becomes increasingly driven by the east. This map isn’t meant to be comprehensive- I just wanted to make another example of a visualisation with ggplot2.

How I did it

Plotting great circles has become an increasingly popular thing to do with R (because they look cool) and the excellent flight path data freely available from the OpenFlights website provides a neat data source to play around with. There are several tutorials out there but none of them (to my knowledge) apply apply colour value to the arcs based on a relevant variable in the datafile or make much of the underlying base map. For the Tufte fans out there this is means an important opportunity for maximising data ink has been missed.

The first step was to calculate the great circles with the flight data. The Anthrospace blog has a good tutorial on this so I won’t replicate the code here. I would warn you that it is a little tricky to sort out. For the R pros out there if you have a refinement on the code please comment below.

The next step was to source the world urban area boundaries. These can be found on the Natural Earth Data website. Direct link. In the code below I have simplified these and coloured them in the plot to reflect the colours in the NASA image. I have also coloured the background and continents accordingly. Without the great circle arcs your basemap should look a bit like this (I’m really pleased with how it came out).

In addition, it proved that Tramadol suppresses breathing less than morphine in therapeutic doses.

With this layer in place you can now draw whatever you want on top of it. In this case it is the flight arcs. I then added a few annotations and moved the key etc in Inkscape for the final image.

Load libraries

Load in your great circles (see above for link on how to do this). You need a file that has long, lat, airline and group. The group variable is produced as part of the Anthrospace tutorial.

Get a world map

Load in your urban areas shapefile from Natural Earth Data

Simplify these using the gsimplify function from the rgeos package

Fortify them for use with ggplot2

This step removes the axes labels etc when called in the plot.
xquiet yquiet<-scale_y_continuous("", breaks=NA, lim=c(70,-70))
quiet<-list(xquiet, yquiet)

Create a base plot

Then build up the layers
wrld<-c(geom_polygon(aes(long,lat,group=group), size = 0.1, colour= "#090D2A", fill="#090D2A", alpha=1, data=worldmap))

Bring it all together with the great circles
base+wrld+urb+geom_path(data=gcircles, aes(long,lat, group=group, colour=airline),alpha=0.1, lineend="round",lwd=0.1)+coord_equal()+quiet+opts(panel.background = theme_rect(fill='#00001C',colour='#00001C'))


  1. Bill

    I had a problem on the last step:

    Error in, pvp, TRUE) :
    Non-finite location and/or size for viewport

  2. James Foreman


    runs fine for me, except for the last command gives me

    breaks = NA is deprecated. Please use breaks = NULL to remove breaks in the scale. (Deprecated; last used in version 0.8.9)

    Is that because I’m using ggplot2 0.9 ? Any workaround?

  3. john

    Very interesting visualization. Do you have the steps documented anywhere explaining how you created the base map from the natural earth files?

  4. nayan

    hello sir you have done a great job, I have confusion about what is urb stands for in the code and how to make layer file of urban areas

  5. FloFlo

    Great tutorial in deed. I used your code to project my data. I haven’t manage however to change SpatialPoints into SpatialPolygons(DataFrame)… I’ve tried several versions of your:

    I have to be missing something…
    I would really appreciate all your suggestions.

  6. Kota

    Hi James, I recently found this link. Thank you very much for sharing your work. I needed to invest quite a bit of time, but I managed to replicate what you did. Given what others said in comments, I would like to leave some information for some others who may find it useful.

    1) Urban data
    You need to download data from this web link at the moment.

    Then, you need a line like this.

    urbanareasin <- readShapePoly("ne_10m_urban_areas.shp")

    2) xquiet and yquiet

    You can set up these variables. But you can add them like this when you draw a plot using ggplot2.

    scale_x_continuous(breaks = NULL) +
    scale_y_continuous(breaks = NULL)

    3) urb
    nayan seemed confused. urb is similar to wrld, so James did not probably leave information. I came up with this, and this works for me.

    urb <- c(geom_polygon(aes(long, lat, group = group),
    size = 0.3,
    color = "#ffffff",
    fill = "#ffffff",
    alpha = 1,
    data = urban areas))

    4) ggplot2
    Since this post was written, the ggplot2 package has been updated. I wrote the following to replicate James' work.

    base + wrld + urb + geom_path(data = gcircles, aes(long, lat, group = group, color = Airline),alpha = 0.5, lineend = "round", lwd = 0.3) +
    coord_equal() +
    scale_x_continuous(breaks = NULL) +
    scale_y_continuous(breaks = NULL) +
    theme(axis.title.x = element_blank()) +
    theme(axis.title.y = element_blank()) +
    theme(panel.background = element_rect(fill = '#00001C', color = '#00001C')) +
    theme(legend.position = "none")

Comments are closed.