Circumpolar mapping (by Chloe Beaupré)
It’s been over three years since a blog post. Does that even count as a blog? Since the last entry, I’ve become a professor. Which (a) takes a big old chunk out of time to do anything else, but (b) provides access to a hitherto unavailable resource known as graduate students (a subset of the spectacular lab members phenomenon). One of these, Chloe Beaupré, provides the following hot tips for dealing with some very particular mapping issues in R.
background
For a presentation I wanted to create a plot of the Arctic, showing the circumpolar distribution of Rangifer populations next to a map of a map of Arctic Indigenous languages. Turns out mapping a circumpolar projection in R is not as easy a making a pretty map of a smaller area at lower latitudes. Here is an outline to recreate (one of) these plots.
packages <- list("dplyr","sf", "ggplot2","mapview", "viridis","maptools", "raster")
sapply(packages, require, character = TRUE)
shapefiles
We’ll plot the map of arctic Indigenous Peoples languages and dialects since the data are made publicly available by the Arctic Indigenous languages and revitalization project. Shapefiles can be downloaded here.
After downloading the shapefiles, read them into your environment and re-project to the EPSG 3995 projection (arctic polar stereographic) using the sf
package
lang <- st_read("data/Arctic_Indigenous_Peoples_languages_and_revitalization_-_Languages_and_Dialects.shp") %>%
st_transform(st_crs(3995))
mapview fails us
We usually love plotting spatial data using the mapview
package because it’s fast and interactive but it defaults to the dreaded Mercator projection.
mapview(lang)
[ed. note - interactivity of mapview disabled on blog]
Looks horrifying.
There is an argument in mapview to use the native CRS (which we’ve transformed to Arctic Polar Stereographic EPSG 3995), let’s try it out.
mapview(lang, native.crs = TRUE, map.types="Esri.WorldImagery")
This looks better! But there isn’t a basemap for the North Pole so we have our language polygons floating in the ether. We can do better.
coastlines
The maptools
package provides a simple whole-world coastline data set. We set the y-limit to 45 degrees North, so only keep the coastlines in the north.
data("wrld_simpl", package = "maptools")
w <- raster::crop(wrld_simpl, extent(-180, 180, 45, 90))
plot(w)
After downloading the northern coastlines, convert to a simple feature multipolygon, then re-project to the arctic polar stereographic (EPSG 3995).
# make it into an sf object, transform the crs
w_sf <- w %>%
st_as_sf(coords = c("long", "lat"), crs = 4326) %>%
st_transform(st_crs(3995))
putting it all together
The following code uses ggplot2
to plot the arctic coastlines and overlays the arctic Indigenous Peoples languages shapefile.
ggplot() +
geom_sf(data=w_sf, fill = "grey", colour = "black", alpha = 0.5) +
geom_sf(data = lang[!is.na(lang$LangFamily),], aes(fill = LangFamily), alpha = 0.8) +
scale_fill_viridis_d()+
scale_x_continuous(breaks = NULL) +
labs(title = "", x = "", y = "", fill = "Language family") +
theme(axis.text = element_blank(),
axis.ticks = element_blank(),
panel.background = element_blank(),
text = element_text(size = 12),
legend.key.size = unit(.2, 'cm')) # make the legend smaller
The remaining code creates a similar plot using base R if that’s more your style (cough owner-of-this-blog-Elie cough).
cols <- viridis(n = length(unique(lang$LangFamily)))
labs <- levels(as.factor(lang$LangFamily))
# set margins
par(mar = rep(.1,4))
layout(matrix(1:2, ncol =2), widths = c(1.5,1))
# plot the coastlines, to initiate the area
plot(st_geometry(w_sf), lwd=1, lty=1, col= alpha("grey", .5), border = "darkgrey")
# plot the language data
plot(lang["LangFamily"], lwd = 1, lty = 1, pal = alpha(cols, .8), add = TRUE, border = NA)
par(mar = c(0,0,6,0), xpd = NA); plot.new()
mtext(side = 3, "Language family", font = 1, line = 0, adj = 0)
legend(x = "topleft", ncol = 1, cex = 0.8, legend = labs, col = cols, pch=15, pt.cex = 1.5, bty = "n")
- Chloe Beaupré
editorial (cough-cough) comment
Maybe you’re bothered by the weirdly straight polygon line in Chloe’s otherwise lovely map? Try this:
data("wrld_simpl", package = "maptools")
w.equator <- raster::crop(wrld_simpl, extent(-180, 180, 0, 90)) %>%
st_as_sf(coords = c("long", "lat"), crs = 4326) %>%
st_transform(st_crs(3995))
donut <- list(cbind(x = c(0:360, 360:0, 0), y = c(rep(45, 361), c(rep(0,361)), 45))) %>% st_polygon %>%
st_sfc(crs = 4326) %>% st_transform(st_crs(3995))
fortyfive <- cbind(x = 0:360, y = rep(45, 361)) %>% st_linestring() %>%
st_sfc(crs = 4326) %>% st_transform(st_crs(3995))
Now … put it all together
par(mar = rep(.1,4))
layout(matrix(1:2, ncol =2), widths = c(1.5,1))
plot(fortyfive, col = "darkgrey")
plot(st_geometry(w.equator), col = "antiquewhite", add = TRUE, border = "darkgrey")
plot(donut, add = TRUE, col = "white", border = NA)
plot(lang["LangFamily"], lwd = 1, lty = 1, pal = alpha(cols, .8), add = TRUE, border = NA)
par(mar = c(0,0,6,0), xpd = NA); plot.new()
mtext(side = 3, "Language family", font = 1, line = 0, adj = 0)
legend(x = "topleft", ncol = 1, cex = 0.8, legend = labs, col = cols, pch=15, pt.cex = 1.5, bty = "n")
Do that in ggplot!