#install.packages("ggplot2")
library("ggplot2")Warning: package 'ggplot2' was built under R version 4.4.3
library(ggspatial)Warning: package 'ggspatial' was built under R version 4.4.3
As you have seen so far in this workshop, R has some useful tools for visualizing spatial data built in and there are packages that you can work with to make maps that are more refined. Next we’ll talk about the ggplot2 package so you can compare and contrast three of the most popular ways to make maps in R.
ggplot2 (commonly referred to as “ggplot”) is a popular package for plotting data in R. It has been extended to work with spatial data. Like tmap, it also uses the concepts of a Grammar of Graphics to layer data as well as visualization rules. ggplot is a big topic that could be its own workshop, so today we’ll cover the basics so you can learn more on your own if this sparks your interest.
A common point of confusion is the difference between ggplot and ggmap. ggmap is another popular package for making maps in R that extends the ggplot2 concept. ggmap assumes you want to use a basemap with every plot. We will focus on ggplot in this workshop.
First, we load the libraries we’ll need:
#install.packages("ggplot2")
library("ggplot2")Warning: package 'ggplot2' was built under R version 4.4.3
library(ggspatial)Warning: package 'ggspatial' was built under R version 4.4.3
Grammar of Graphics tools typically follow a pattern: first you indicate which data you want to work with, then you indicate the way the data should be styled. Let’s map the states data to see a basic no-frills example:
ggplot(states) + #indicate which data to plot
geom_sf() #use the default style for sf objects
With ggplot2, if we want to add multiple layers to the map, we can think of each layer as a separate map that we stack together. For our map, we’ll first make the layer that contains the states, then we’ll add a layer that contains the lakes, then a final layer that contains the monsters.
aoi <- st_bbox(c(xmin = -96, xmax = -73, ymax = 50, ymin = 40), crs = st_crs(4326))
ggplot() +
geom_sf(data = states) + #plot the states
geom_sf(data = lakes) + #plot the lakes and make them blue
geom_sf(data = monsters) + #plot the monsters
coord_sf( #set the zoom based on our AOI bounding box
xlim=c(aoi[1], aoi[3]),
ylim=c(aoi[2], aoi[4]))
The goal of styling our data is to help it communicate better. Dots on top of state outlines are definitely difficult to understand. Let’s change the plotting arguments to make something more readable and to fit the story we want to tell.
gg_monsters <- ggplot() + #assign the results of the plot to a variable
geom_sf(data = states,
fill = "white", #fill the states with white
color = "gray30") + #make the outline a dark gray
geom_sf(
data = lakes,
fill = "lightblue", #fill the lakes with blue
color = "lightblue4") + #make the outline a gray-blue
geom_sf(data = monsters,
size = 3 #make the points larger
) +
coord_sf(
xlim=c(aoi[1], aoi[3]),
ylim=c(aoi[2], aoi[4]))
ocean_color <- "lightblue3" #pick a color for the ocean
gg_monsters + #add theme parameters to the plot & remove lines
theme(
panel.background = element_rect(fill = ocean_color, color = ocean_color),
panel.grid.major = element_line(color = ocean_color),
panel.grid.minor = element_line(color = ocean_color),
panel.border = element_rect(color = "black", fill = NA) #add a black neatline
)
Let’s add some text labels so we know what the names of each of the monsters are. We can just update the last line of the code we wrote above, adding the labeling details.
gg_monsters +
theme(
panel.background = element_rect(fill = ocean_color, color = ocean_color),
panel.grid.major = element_line(color = ocean_color),
panel.grid.minor = element_line(color = ocean_color),
panel.border = element_rect(color = "black", fill = NA)
) +
geom_sf_text(data=monsters, aes(label = Name), nudge_y = -.3) #add the label text and nudge them down so the letters aren't on top of the pointWarning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
give correct results for longitude/latitude data

Labeling of points is challenging for all of the methods we’ve looked at. ggplot2 has some options to avoid label collisions (overlaps) that you you can explore more on your own. If you want finer label placement control, export a pdf and edit it in a vector illustration software like Inkscape or Adobe Illustrator.
ggplot2 does not come with the ability to add map elements like a north arrow or scale bar to your map, but we can add this capability with another package: ggspatial.
gg_monsters + #add theme parameters to the plot & remove lines
theme(
panel.background = element_rect(fill = ocean_color, color = ocean_color),
panel.grid.major = element_line(color = ocean_color),
panel.grid.minor = element_line(color = ocean_color),
panel.border = element_rect(color = "black", fill = NA)
) +
geom_sf_text(data=monsters, aes(label = Name), nudge_y = -.3)+
annotation_scale(location = "bl", pad_x = unit(2, "cm"))+ #add the scale bar and move it over a little to the right
annotation_north_arrow(location = "bl", which_north = "grid") #add the north arrowWarning in st_point_on_surface.sfc(sf::st_zm(x)): st_point_on_surface may not
give correct results for longitude/latitude data
Scale on map varies by more than 10%, scale bar may be inaccurate

This map is far from beautiful, but it’s a good first draft. If you like ggplot2, there’s a lot more you can learn about refining placement and style options, but you’ve seen the basics to know what’s possible and you’ve got a starting point to learn more on your own.
ggmap is another popular package for making maps in R that extends the ggplot2 concept. ggmap assumes you want to use a basemap with every plot.
Remember that even if a tile service makes a dataset easy to use, you still need to comply with the terms of service for that specific dataset.
Unfortunately, it seems that the current version of ggmap requires you to have a Google API Key to access even non-Google tiles. That’s outside the scope of this workshop, but there is documentation on the internet for how to use this tool to build ggplot2 maps on top of a basemaps.
We’ve seen that we can build a map in layers using ggplot2 in a similar manner to the workflow we used in the base plot() and tmap workflow. ggplot2 is an entire universe of plot creation with many options for fine control and attractive themes that you can explore. It has perhaps the steepest learning curve, but also has big rewards if you have the patience to stick with it.