Drawing Beautiful Maps Programmatically With R, SF and Ggplot2 - Part 1 - Basics
Drawing Beautiful Maps Programmatically With R, SF and Ggplot2 - Part 1 - Basics
EDIT: Following a suggestion Adriano Fantini and code from Andy South, we replaced rworlmap by
rnaturalearth .
In this part, we will cover the fundamentals of mapping using ggplot2 associated to sf , and presents
the basics elements and parameters we can play with to prepare a map.
Maps are used in a variety of fields to express data in an appealing and interpretive way. Data can be
expressed into simplified patterns, and this data interpretation is generally lost if the data is only seen
through a spread sheet. Maps can add vital context by incorporating many variables into an easy to read
and applicable context. Maps are also very important in the information world because they can quickly
allow the public to gain better insight so that they can stay informed. It’s critical to have maps be
effective, which means creating maps that can be easily understood by a given audience. For instance,
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 1/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
maps that need to be understood by children would be very different from maps intended to be shown to
geographers.
Knowing what elements are required to enhance your data is key into making effective maps. Basic
elements of a map that should be considered are polygon, points, lines, and text. Polygons, on a map,
are closed shapes such as country borders. Lines are considered to be linear shapes that are not filled
with any aspect, such as highways, streams, or roads. Finally, points are used to specify specific
positions, such as city or landmark locations. With that in mind, one need to think about what elements
are required in the map to really make an impact, and convey the information for the intended audience.
Layout and formatting are the second critical aspect to enhance data visually. The arrangement of these
map elements and how they will be drawn can be adjusted to make a maximum impact.
R is a powerful and flexible tool. R can be used from calculating data sets to creating graphs and maps
with the same data set. R is also free, which makes it easily accessible to anyone. Some other
advantages of using R is that it has an interactive language, data structures, graphics availability, a
developed community, and the advantage of adding more functionalities through an entire ecosystem of
packages. R is a scriptable language that allows the user to write out a code in which it will execute the
commands specified.
Using R to create maps brings these benefits to mapping. Elements of a map can be added or removed
with ease — R code can be tweaked to make major enhancements with a stroke of a key. It is also easy
to reproduce the same maps for different data sets. It is important to be able to script the elements of a
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 2/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
map, so that it can be re-used and interpreted by any user. In essence, comparing typical GIS software
and R for drawing maps is similar to comparing word processing software (e.g. Microsoft Office or
LibreOffice) and a programmatic typesetting system such as LaTeX, in that typical GIS software
implement a WYSIWIG approach (“What You See Is What You Get”), while R implements a WYSIWYM
approach (“What You See Is What You Mean”).
The package ggplot2 implements the grammar of graphics in R, as a way to create code that make
sense to the user: The grammar of graphics is a term used to breaks up graphs into semantic
components, such as geometries and layers. Practically speaking, it allows (and forces!) the user to
focus on graph elements at a higher level of abstraction, and how the data must be structured to achieve
the expected outcome. While ggplot2 is becoming the de facto standard for R graphs, it does not
handle spatial data specifically. The current state-of-the-art of spatial objects in R relies on Spatial
classes defined in the package sp , but the new package sf has recently implemented the “simple
feature” standard, and is steadily taking over sp . Recently, the package ggplot2 has allowed the use
of simple features from the package sf as layers in a graph1. The combination of ggplot2 and sf
therefore enables to programmatically create maps, using the grammar of graphics, just as informative
or visually appealing as traditional GIS software.
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:
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 appropriate for maps:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 3/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
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"
ggplot(data = world) +
geom_sf()
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 4/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
This call nicely introduces the structure of a ggplot call: The first part ggplot(data = world) initiates
the ggplot graph, and indicates that the main data is stored in the world object. The line ends up
with a + sign, which indicates that the call is not complete yet, and each subsequent line correspond to
another layer or scale. In this case, we use the geom_sf function, which simply adds a geometry stored
in a sf object. By default, all geometry functions use the main data defined in ggplot() , but we will
see later how to provide additional data.
Note that layers are added one at a time in a ggplot call, so the order of each layer is very important.
All data will have to be in an sf format to be used by ggplot2 ; data in other formats (e.g. classes
from sp ) will be manually converted to sf classes if necessary.
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 5/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
changed to something more suitable (e.g. “Longitude” and “Latitude”), depending on the map:
ggplot(data = world) +
geom_sf() +
xlab("Longitude") + ylab("Latitude") +
ggtitle("World map", subtitle = paste0("(", length(unique(world$NAME)), " countries)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 6/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
ggplot(data = world) +
geom_sf(color = "black", fill = "lightgreen")
The package ggplot2 allows the use of more complex color schemes, such as a gradient on one
variable of the data. Here is another example that shows the population of each country. In this example,
we use the “viridis” colorblind-friendly palette for the color gradient (with option = "plasma" for the
plasma variant), using the square root of the population (which is stored in the variable POP_EST of the
world object):
ggplot(data = world) +
geom_sf(aes(fill = pop_est)) +
scale_fill_viridis_c(option = "plasma", trans = "sqrt")
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 7/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
ggplot(data = world) +
geom_sf() +
coord_sf(crs = "+proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS8
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 8/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
Spatial Reference System Identifier (SRID) or an European Petroleum Survey Group (EPSG) code are
available for the projection of interest, they can be used directly instead of the full PROJ4 string. The two
following calls are equivalent for the ETRS89 Lambert Azimuthal Equal-Area projection, which is EPSG
code 3035:
ggplot(data = world) +
geom_sf() +
coord_sf(crs = "+init=epsg:3035")
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 9/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
ggplot(data = world) +
geom_sf() +
coord_sf(crs = st_crs(3035))
The extent of the map can also be set in coord_sf , in practice allowing to “zoom” in the area of
interest, provided by limits on the x-axis ( xlim ), and on the y-axis ( ylim ). Note that the limits are
automatically expanded by a fraction to ensure that data and axes don’t overlap; it can also be turned off
to exactly match the limits provided with expand = FALSE :
ggplot(data = world) +
geom_sf() +
coord_sf(xlim = c(-102.15, -74.12), ylim = c(7.65, 33.97), expand = FALSE)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 10/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
scale_bar that allows to add simultaneously the north symbol and a scale bar into the ggplot map.
Five arguments need to be set manually: lon , lat , distance_lon , distance_lat , and
distance_legend . The location of the scale bar has to be specified in longitude/latitude in the lon
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 11/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
and lat arguments. The shaded distance inside the scale bar is controlled by the distance_lon
argument. while its width is determined by distance_lat . Additionally, it is possible to change the font
size for the legend of the scale bar (argument legend_size , which defaults to 3). The North arrow
behind the “N” north symbol can also be adjusted for its length ( arrow_length ), its distance to the
scale ( arrow_distance ), or the size the N north symbol itself ( arrow_north_size , which defaults to
6). Note that all distances ( distance_lon , distance_lat , distance_legend , arrow_length ,
arrow_distance ) are set to "km" by default in distance_unit ; they can also be set to nautical miles
with “nm”, or miles with “mi”.
library("ggspatial")
ggplot(data = world) +
geom_sf() +
annotation_scale(location = "bl", width_hint = 0.5) +
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(-102.15, -74.12), ylim = c(7.65, 33.97))
## Scale on map varies by more than 10%, scale bar may be inaccurate
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 12/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
Note the warning of the inaccurate scale bar: since the map use unprojected data in longitude/latitude
(WGS84) on an equidistant cylindrical projection (all meridians being parallel), length in (kilo)meters on
the map directly depends mathematically on the degree of latitude. Plots of small regions or projected
data will often allow for more accurate scale bars.
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 13/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
The world data set already contains country names and the coordinates of the centroid of each
country (among more information). We can use this information to plot country names, using world as
a regular data.frame in ggplot2 . The function geom_text can be used to add a layer of text to a
map using geographic coordinates. The function requires the data needed to enter the country names,
which is the same data as the world map. Again, we have a very flexible control to adjust the text at will
on many aspects:
Additionally, the annotate function can be used to add a single character string at a specific location,
as demonstrated here to add the Gulf of Mexico:
library("sf")
world_points<- st_centroid(world)
world_points <- cbind(world, st_coordinates(st_centroid(world$geometry)))
ggplot(data = world) +
geom_sf() +
geom_text(data= world_points,aes(x=X, y=Y, label=name),
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 14/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
Final map
Now to make the final touches, the theme of the map can be edited to make it more appealing. We
suggested the use of theme_bw for a standard theme, but there are many other themes that can be
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 15/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
selected from (see for instance ?ggtheme in ggplot2 , or the package ggthemes which provide
several useful themes). Moreover, specific theme elements can be tweaked to get to the final outcome:
Position of the legend: Although not used in this example, the argument legend.position allows
to automatically place the legend at a specific location (e.g. "topright" , "bottomleft" , etc.);
Grid lines (graticules) on the map: by using panel.grid.major and panel.grid.minor , grid lines
can be adjusted. Here we set them to a gray color and dashed line type to clearly distinguish them
from country borders lines;
Map background: the argument panel.background can be used to color the background, which is
the ocean essentially, with a light blue;
Many more elements of a theme can be adjusted, which would be too long to cover here. We refer
the reader to the documentation for the function theme .
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 16/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
The final map now ready, it is very easy to save it using ggsave . This function allows a graphic
(typically the last plot displayed) to be saved in a variety of formats, including the most common PNG
(raster bitmap) and PDF (vector graphics), with control over the size and resolution of the outcome. For
instance here, we save a PDF version of the map, which keeps the best quality, and a PNG version of it
for web purposes:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 17/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
ggsave("map.pdf")
ggsave("map_web.png", width = 6, height = 6, dpi = "screen")
1. Note: Support of sf objects is available since version 3.0.0 of ggplot2 , recently released on
CRAN. ↩
r-spatial
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 18/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
Sponsored
Take A Closer Look, The Photographer Was Not Expecting This Photo
Gadgetheory
Dad Puts Recording Device In Kid's Hair, Catches Teacher Doing Unthinkable
Sizzlfy
ALSO ON R-SPATIAL
[view raw Rmd] What [view raw Rmd] Suppose The analysis of non-trivial [view raw Rmd] mapedit
happened so far? in an you have the following amounts of Earth has progressed substantial
earlier blog post I … geometry, consisting of … Observation (EO) data is … since the introduction to …
⚠ r-spatial requires you to verify your email address before posting. Send verification email to ×
[email protected]
library("ggspatial")
ggplot(data = world) +
geom sf() +
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 20/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
geom_sf() +
annotation_scale(location = "bl", width_hint = 0.5) +
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(-102.15, -74.12), ylim = c(7.65, 33.97))
How do I change xlim and ylim when latitude is South and longitude is East? Thanks
△ ▽ • Reply • Share ›
ggplot(data = world) +
geom_sf(color = "black", fill = "lightgreen")+
coord_sf(crs = st_crs(3035), xlim = c(-180.0, 180.0), ylim = c(66.0, 90.0), expand = FALSE)
mapPlot(coastlineWorld,
Thanks!
△ ▽ • Reply • Share ›
When I run the second plot, I end up with the title displaying (0 Countries). The map renders correctly
otherwise.
I've tried looking for solutions online, but no luck.
△ ▽ • Reply • Share ›
Warning messages:
1: In st_centroid.sf(world) :
st_centroid assumes attributes are constant over geometries of x
2: In st_centroid.sfc(st_geometry(x), of_largest_polygon = of_largest_polygon) :
st_centroid does not give correct centroids for longitude/latitude data
I also was unable to locate the "libwgeom" library. Are these related?
△ ▽ • Reply • Share ›
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 22/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
library(sf)
library(ggplot2)
theme_set(theme_bw())
library("rnaturalearth")
library("rnaturalearthdata")
library("ggspatial")
ggplot(data=World)+
geom_sf()+
coord_sf(xlim= c(175, 180), ylim=c(-20,-12.0), expand = FALSE)
△ ▽ • Reply • Share ›
ggplot(data=world)+
geom_sf()+
coord_sf(xlim= c(175, 180), ylim=c(-20,-12.0), expand = TRUE)
With expand = TRUE is expands the map to more islands. Let me know if that is helpful.
△ ▽ • Reply • Share ›
library("ggplot2")
library("rnaturalearth")
library("rnaturalearthdata")
ggplot(data=world) +
geom_sf() +
coord_sf(
crs = 3460, # https://epsg.io/3460
xlim = c(1798028.61, 2337149.40), # limits are taken from projected bounds
ylim = c(3577110.39, 4504717.19) # of EPSG:3460
)
Let me know if that works for you and I hope this helps with your mapping!
△ ▽ • Reply • Share ›
He mentions that the graticule lines end before the edge of the plot, so they end up not
being drawn. He has a work around here:
library("ggplot2")
library("sf")
#> Linking to GEOS 3.7.2, GDAL 2.4.2, PROJ 5.2.0
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 26/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
library("rnaturalearth")
library("rnaturalearthdata")
ggplot(data = world) +
geom_sf() +
coord_sf(expand = FALSE)
I will have to update this tutorial to reflect that. Thanks and let me know if this works for
you.
△ ▽ • Reply • Share ›
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 27/28
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 1: Basics
Sponsored
Kommentare
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html 28/28