Professional Documents
Culture Documents
In the previous part, we presented general concepts with a map with little information (country borders
only). The modular approach of ggplot2 allows to successively add additional layers, for instance
study sites or administrative delineations, as will be illustrated in this part.
Getting started
Many R packages are available from CRAN, the Comprehensive R Archive Network, which is the
primary repository of R packages. The full list of packages necessary for this series of tutorials can be
installed with:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 1/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
We start by loading the basic packages necessary for all maps, i.e. ggplot2 and sf . We also suggest
to use the classic dark-on-light theme for ggplot2 ( theme_bw ), which is more appropriate for maps:
library("ggplot2")
theme_set(theme_bw())
library("sf")
The package rnaturalearth provides a map of countries of the entire world. Use ne_countries to
pull country data and choose the scale ( rnaturalearthhires is necessary for scale = "large" ).
The function can return sp classes (default) or directly sf classes, as defined in the argument
returnclass :
library("rnaturalearth")
library("rnaturalearthdata")
## [1] "sf"
## [1] "data.frame"
## longitude latitude
## 1 -80.14401 26.47901
## 2 -80.10900 26.83000
The quickest way to add point coordinates is with the general-purpose function geom_point , which
works on any X/Y coordinates, of regular data points (i.e. not geographic). As such, we can adjust all
characteristics of points (e.g. color of the outline and the filling, shape, size, etc.), for all points, or using
grouping from the data (i.e defining their “aesthetics”). In this example, we add the two points as
diamonds ( shape = 23 ), filled in dark red ( fill = "darkred" ) and of bigger size ( size = 4 ):
ggplot(data = world) +
geom_sf() +
geom_point(data = sites, aes(x = longitude, y = latitude), size = 4,
shape = 23, fill = "darkred") +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 3/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
A better, more flexible alternative is to use the power of sf : Converting the data frame to a sf object
allows to rely on sf to handle on the fly the coordinate system (both projection and extent), which can
be very useful if the two objects (here world map, and sites) are not in the same projection. To achieve
the same result, the projection (here WGS84, which is the CRS code #4326) has to be a priori defined in
the sf object:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 4/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
ggplot(data = world) +
geom_sf() +
geom_sf(data = sites, size = 4, shape = 23, fill = "darkred") +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 5/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
Note that coord_sf has to be called after all geom_sf calls, as to supersede any former input.
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 6/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
library("maps")
states <- st_as_sf(map("state", plot = FALSE, fill = TRUE))
head(states)
State names are part of this data, as the ID variable. A simple (but not necessarily optimal) way to add
state name is to compute the centroid of each state polygon as the coordinates where to draw their
names. Centroids are computed with the function st_centroid , their coordinates extracted with
st_coordinates , both from the package sf , and attached to the state object:
Note the warning, which basically says that centroid coordinates using longitude/latitude data (i.e.
WGS84) are not exact, which is perfectly fine for our drawing purposes. State names, which are not
capitalized in the data from maps , can be changed to title case using the function toTitleCase from
the package tools :
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 7/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
library("tools")
states$ID <- toTitleCase(states$ID)
head(states)
To continue adding to the map, state data is directly plotted as an additional sf layer using geom_sf .
In addition, state names will be added using geom_text , declaring coordinates on the X-axis and Y-
axis, as well as the label (from ID ), and a relatively big font size.
ggplot(data = world) +
geom_sf() +
geom_sf(data = states, fill = NA) +
geom_text(data = states, aes(X, Y, label = ID), size = 5) +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 8/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
We can move the state names slightly to be able to read better “South Carolina” and “Florida”. For this,
we create a new variable nudge_y , which is -1 for all states (moved slightly South), 0.5 for Florida
(moved slightly North), and -1.5 for South Carolina (moved further South):
states$nudge_y <- -1
states$nudge_y[states$ID == "Florida"] <- 0.5
states$nudge_y[states$ID == "South Carolina"] <- -1.5
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 9/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
To improve readability, we also draw a rectangle behind the state name, using the function geom_label
instead of geom_text , and plot the map again.
ggplot(data = world) +
geom_sf() +
geom_sf(data = states, fill = NA) +
geom_label(data = states, aes(X, Y, label = ID), size = 5, fontface = "bold",
nudge_y = states$nudge_y) +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 10/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
County lines can now be added in a very simple way, using a gray outline:
ggplot(data = world) +
geom_sf() +
geom_sf(data = counties, fill = NA, color = gray(.5)) +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 11/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
We can also fill in the county using their area to visually identify the largest counties. For this, we use the
“viridis” colorblind-friendly palette, with some transparency:
ggplot(data = world) +
geom_sf() +
geom_sf(data = counties, aes(fill = area)) +
scale_fill_viridis_c(trans = "sqrt", alpha = .4) +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 12/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 13/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
library("googleway")
key <- "put_your_google_api_key_here" # real key needed
flcities <- data.frame(state = rep("Florida", 5), city = c("Miami",
"Tampa", "Orlando", "Jacksonville", "Sarasota"))
coords <- apply(flcities, 1, function(x) {
google_geocode(address = paste(x["city"], x["state"], sep = ", "),
key = key)
})
flcities <- cbind(flcities, do.call(rbind, lapply(coords, geocode_coordinates)))
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 14/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
ggplot(data = world) +
geom_sf() +
geom_sf(data = counties, fill = NA, color = gray(.5)) +
geom_sf(data = flcities) +
geom_text(data = flcities, aes(x = lng, y = lat, label = city),
size = 3.9, col = "black", fontface = "bold") +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 15/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
This is not really satisfactory, as the names overlap on the points, and they are not easy to read on the
grey background. The package ggrepel offers a very flexible approach to deal with label placement
(with geom_text_repel and geom_label_repel ), including automated movement of labels in case of
overlap. We use it here to “nudge” the labels away from land into the see, and connect them to the city
locations:
library("ggrepel")
ggplot(data = world) +
geom_sf() +
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 16/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
Final map
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 17/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
For the final map, we put everything together, having a general background map based on the world
map, with state and county delineations, state labels, main city names and locations, as well as a theme
adjusted with titles, subtitles, axis labels, and a scale bar:
library("ggspatial")
ggplot(data = world) +
geom_sf(fill = "antiquewhite1") +
geom_sf(data = counties, aes(fill = area)) +
geom_sf(data = states, fill = NA) +
geom_sf(data = sites, size = 4, shape = 23, fill = "darkred") +
geom_sf(data = flcities) +
geom_text_repel(data = flcities, aes(x = lng, y = lat, label = city),
fontface = "bold", nudge_x = c(1, -1.5, 2, 2, -1), nudge_y = c(0.25,
-0.25, 0.5, 0.5, -0.5)) +
geom_label(data = states, aes(X, Y, label = ID), size = 5, fontface = "bold",
nudge_y = states$nudge_y) +
scale_fill_viridis_c(trans = "sqrt", alpha = .4) +
annotation_scale(location = "bl", width_hint = 0.4) +
annotation_north_arrow(location = "bl", which_north = "true",
pad_x = unit(0.75, "in"), pad_y = unit(0.5, "in"),
style = north_arrow_fancy_orienteering) +
coord_sf(xlim = c(-88, -78), ylim = c(24.5, 33), expand = FALSE) +
xlab("Longitude") + ylab("Latitude") +
ggtitle("Observation Sites", subtitle = "(2 sites in Palm Beach County, Florida)") +
theme(panel.grid.major = element_line(color = gray(0.5), linetype = "dashed",
size = 0.5), panel.background = element_rect(fill = "aliceblue"))
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 18/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
This example fully demonstrates that adding layers on ggplot2 is relatively straightforward, as long as
the data is properly stored in an sf object. Adding additional layers would simply follow the same logic,
with additional calls to geom_sf at the right place in the ggplot2 sequence.
r-spatial
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 19/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 20/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
Sponsored
Kommentare
ALSO ON R-SPATIAL
view raw Rmd “ExtendingR”,by John M. Earth observation data, or [view raw Rmd] The
Chambers;Paperback satellite imagery, is one of problem Scientists working
$69.95, May 24, 2016 by … the richest sources to find … with collections and time …
⚠ r-spatial requires you to verify your email address before posting. Send verification email to ×
abismo.monteiro@gmail.com
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 21/22
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 2: Layers
@g
Sponsored
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-2.html 22/22