Simple distribution maps using ggplot

In a previous post, I discussed how to plot GBIF occurrence data using OpenStreetMaps. Here, I will plot a distribution map. Distribution maps differ from occurrence maps in that occurrences are aggregated and plotted as a heat map. Additionally, the map has to be projected using an equal area projection.
I will illustrate these two features by plotting the distribution of the tawny owl (Strix aluco) in Europe.

Distribution of the tawny owl in Europe

These are the libraries I will be using:


I will be plotting a map of Europe. After some trial and error, I settled for these coordinates for the map area:

LAT1 = 72 ; LAT2 = 34
LON1 = -12 ; LON2 = 40

I downloaded data from GBIF. I prefer to use the GBIF web interface to search for data and then to download the data as a zip file. I proceeded as explained in this post, and I stored the data in the file “data/”.

The data can then be loaded into a variable, and cleaned:

occurrences <- read_tsv("data//")
occurrences <- occurrences[
  !$decimalLatitude) &
  !$decimalLongitude), ]

As this data set has more than 500.000 entries, I will sub-sample 50.000 to speed things up:

occurrences.sampled <-
  occurrences[sample(nrow(occurrences), 50000),]

I aggregated the raw occurrence data into bins of one degree (for simplicity), and used the library dplyr to count the number of occurrences in each bin:

occurrences.sampled$x =
occurrences.sampled$y =
strix.dist <- occurrences.sampled %>%
  group_by(x,y) %>%

A line map of the world is available through the maps library:

world_df <- map_data("world")

The data can now be plotted using geom_tile (note that I plotted the log transformed counts), and the map using geom_path:

map <- ggplot(strix.dist) +
  geom_tile(aes(x, y, fill=log(n))) +
  geom_path(aes(x = long, y = lat, group=group), 
    data = world_df) +
  xlim(c(LON1, LON2)) + ylim(c(LAT2, LAT1)) +
  theme_bw() +
  labs(x="Longitude", y="Latitude")

This map has two problems:

  • the default color scale is too dark
  • the areas of the tiles are not equal, as the map is distorted towards the north

To correct the first problem, I added a more suitable color scale, and removed the legend, as I am plotting log transformed counts:

scale <- scale_fill_gradientn(
  colors=c("white", "yellow", "red"),

To correct the second problem, I applied Alber’s equal area projection, centered on the middle of the map (here is a list of all available projections):

proj <- coord_map(
  parameters=c(mean(LON2, LON1), mean(LAT1, LAT2)))

A better map can now be plotted:

map + scale + proj

References (16 August 2020) GBIF Occurrence Download

Leave a Reply

Your email address will not be published. Required fields are marked *