Drawing Beautiful Maps Programmatically With R, SF and Ggplot2 - Part 3 - Layouts
Drawing Beautiful Maps Programmatically With R, SF and Ggplot2 - Part 3 - Layouts
EDIT: Following a suggestion from Adriano Fantini and code from Andy South, we replaced rworlmap
by rnaturalearth . We also largely edited the text.
After the presentation of basic map concepts, and the flexible approach in layers implemented in
ggplot2 , this part illustrates how to achieve complex layouts, for instance with map insets, or several
maps combined. Depending on the visual information that needs to be displayed, maps and their
corresponding data might need to be arranged to create easy to read graphical representations. This
tutorial will provide different approaches to arranges maps in the plot, in order to make the information
portrayed more aesthetically appealing, and most importantly, convey the information better.
Getting started
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 1/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
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 more appropriate for maps:
library("ggplot2")
theme_set(theme_bw())
library("sf")
The package rnaturalearth provides a map of countries of the entire world. Using ne_counties to
pull the country data and,choose the scale. For returnclass , the setting can for be ‘sf’ and/or ‘sp’:
library("rnaturalearth")
library("rnaturalearthdata")
## [1] "sf"
## [1] "data.frame"
General concepts
There are 2 solutions to combine sub-maps:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 2/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
Using “grobs”, i.e. graphic objects from ggplot2 , which can be inserted in the plot region using plot
coordinates;
Using ggdraw from package cowplot , which allows to arrange new plots anywhere on the
graphic device, including outer margins, based on relative position.
Here is a simple example illustrating the difference between the two, and their use. We first prepare a
simple graph showing 11 points, with regular axes and grid ( g1 ):
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 3/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
Graphs from ggplot2 can be saved, like any other R object. That allows to reuse and update the graph
later on. For instance, we store in g1_void , a simplified version of this graph only the point data, but no
decoration:
The function annotation_custom allows to arrange graphs together in the form of grobs (generated
with ggplotGrob ). Here we first plot the full graph g1 , and then add two instances of g1_void in the
upper-left and bottom-right corners of the plot region (as defined by xmin , xmax , ymin , and ymax ):
g1 +
annotation_custom(
grob = ggplotGrob(g1_void),
xmin = 0,
xmax = 3,
ymin = 5,
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 4/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
ymax = 10
) +
annotation_custom(
grob = ggplotGrob(g1_void),
xmin = 5,
xmax = 10,
ymin = 0,
ymax = 3
)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 5/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
An alternative using the function ggdraw from the package cowplot allows to use relative positioning
in the entire plot device. In this case, we build the graph on top of g1 , but the initial call to ggdraw
could actually be left empty to arrange subplots on an empty plot. Width and height of the subplots are
relative from 0 to 1, as well x and y coordinates ([0,0] being the lower-left corner, [1,1] being the upper-
right corner). Note that in this case, subplots are not limited to the actual plot region, but can be added
anywhere on the device:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 6/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
In this example, a zoom in on the Gulf of Mexico is placed on the side of the world map (including its
legend). This illustrates how to use a custom grid, which can be made a lot more complex with more
elements.
We now prepare the subplots, starting by the world map with a rectangle around the Gulf of Mexico (see
Section 1 and 2 for the details of how to prepare this map):
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 7/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
The second map is very similar, but centered on the Gulf of Mexico (using coord_sf ):
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 8/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
Finally, we just need to arrange these two maps, which can be easily done with annotation_custom .
Note that in this case, we use an empty call to ggplot to position the two maps on an empty
background (of size 3.3 × 1):
ggplot() +
coord_equal(xlim = c(0, 3.3), ylim = c(0, 1), expand = FALSE) +
annotation_custom(ggplotGrob(plot1), xmin = 0, xmax = 1.5, ymin = 0,
ymax = 1) +
annotation_custom(ggplotGrob(plot2), xmin = 1.5, xmax = 3, ymin = 0,
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 9/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
ymax = 1) +
theme_void()
The second approach using the function plot_grid from cowplot to arrange ggplot figures, is
quite versatile. Any ggplot figure can be arranged just like the figure above. Several arguments adjust
map placement, such as nrow and ncol which define the number of row and columns, respectively,
and rel_widths which establishes the relative width of each map. In our case, we want both maps on
a single row, the first map gworld to have a relative width of 2.3 , and the map ggulf a relative
width of 1 .
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 10/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
The argument align can be used to align subplots horizontally ( align = "h" ), vertically ( align =
"v" ), or both ( align = "hv" ), so that the axes and plot region match each other. Note also the
existence of get_legend ( cowplot ), which extract the legend of a plot, which can then be used as
any object (for instance, to place it precisely somewhere on the map).
Both maps created above (using ggplot and annotation_custom , or using cowplot and
plot_grid ) can be saved as usual using ggsave (to be used after plotting the desired map):
Map insets
To inset maps directly on a background map, both solutions presented earlier are viable (and one might
prefer one or the other depending on relative or absolute coordinates). We will illustrate this using a map
of the 50 states of the United States, including Alaska and Hawaii (note: both Alaska and Hawaii will not
be to scale).
We start by preparing the continental states first, using the reference US National Atlas Equal Area
projection (CRS 2163). The main trick is to find the right coordinates, in the projection used, and this
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 11/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
may cause some fine tuning at each step. Here, we enlarge the extent of the plot region on purpose to
give some room for the insets:
The Alaska map is plotted using the reference Alaska Albers projection (CRS 3467). Note that graticules
and coordinates are removed with datum = NA :
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 12/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
And now the map of Hawaii, plotted using the reference Old Hawaiian projection (CRS 4135):
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 13/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
The final map can be created using ggplot2 only, with the help of the function annotation_custom. In this
case, we use arbitrary ratios based on the size of the subsets above (note the difference based on
maximum minus minimum x/y coordinates):
mainland +
annotation_custom(
grob = ggplotGrob(alaska),
xmin = -2750000,
xmax = -2750000 + (1600000 - (-2400000))/2.5,
ymin = -2450000,
ymax = -2450000 + (2500000 - 200000)/2.5
) +
annotation_custom(
grob = ggplotGrob(hawaii),
xmin = -1250000,
xmax = -1250000 + (-154 - (-161))*120000,
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 14/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
ymin = -2450000,
ymax = -2450000 + (23 - 18)*120000
)
The same can be achieved with the same logic using cowplot and the function draw_plot, in which case
it is easier to define the ratios of Alaska and Hawaii first:
## [1] 0.575
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 15/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
## [1] 0.7142857
ggdraw(mainland) +
draw_plot(alaska, width = 0.26, height = 0.26 * 10/6 * ratioAlaska,
x = 0.05, y = 0.05) +
draw_plot(hawaii, width = 0.15, height = 0.15 * 10/6 * ratioHawaii,
x = 0.3, y = 0.05)
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 16/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
We start by creating the general map, here a map of Florida with the site locations (see Tutorial 2 for the
details):
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 17/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 18/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 19/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
As we want to connect the two subplots to main map using arrows, the coordinates of the two arrows will
need to be specified before plotting. We prepare a data.frame storing start and end coordinates (x1 and
x2 on the x-axis, y1 and y2 on the y-axis):
Using ggplot only, we simply follow the same approach as before to place several maps side by side,
and then add arrows using the function geom_segment and the argument arrow = arrow():
ggplot() +
coord_equal(xlim = c(0, 28), ylim = c(0, 20), expand = FALSE) +
annotation_custom(ggplotGrob(florida), xmin = 0, xmax = 20, ymin = 0,
ymax = 20) +
annotation_custom(ggplotGrob(siteA), xmin = 20, xmax = 28, ymin = 11.25,
ymax = 19) +
annotation_custom(ggplotGrob(siteB), xmin = 20, xmax = 28, ymin = 2.5,
ymax = 10.25) +
geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowA,
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 20/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
The package cowplot (with draw_plot) can also be used for a similar result, with maybe a somewhat
easier syntax:
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 22/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
r-spatial
ALSO ON R-SPATIAL
4 years ago • 6 comments 3 years ago • 1 comment 4 years ago • 2 comments 4 years ago • 3 comments
Introduction Spatial indexes give you fast The analysis of non-trivial [view raw Rmd] Stevens’s
results on spatial amounts of Earth measurement scales S.S.
queries,such as finding … Observation (EO) data is … Stevens’s classical 1946 …
⚠ r-spatial requires you to verify your email address before posting. Send verification email to ×
[email protected]
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 23/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
I see the point of using site coordinates to adjust the lines precisely (if that is desirable — I don't think it
is necessarily the case here, as having aligned arrows on the X-axis gives a nice output). Do I
understand correctly though that you still have to figure out precisely what the points are on the device?
Would that work for instance if there were hundreds of points plotted on the map?
△ ▽ • Reply • Share ›
However, the code is mostly automated; it works off the locations of the points that you have
added to each map. There is some mental work required to figure out which point is which, but
that can then be enshrined in code. So the idea should scale.
The other manual aspect is details like offsetting the start or end points by a few mm and
dictating angles of incidence. These may scale less well which may mean less fine tuning is
possible at larger scale.
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html
△ ▽ • Reply • Share › 24/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
△ ▽ • Reply • Share ›
Then all you have to do is to convert it to sf, and eventually plot it as a ggplot layer using geom_path.
△ ▽ • Reply • Share ›
✉ d
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html ⚠ 25/26
23/10/2020 Drawing beautiful maps programmatically with R, sf and ggplot2 — Part 3: Layouts
https://www.r-spatial.org/r/2018/10/25/ggplot2-sf-3.html 26/26