09-3: R as GIS: Creating maps from vector data

Before you start


Learning objectives

Learn how to create maps using the ggplot2 package.


Table of contents

  1. Creating maps from sf objects
  2. Tips to make maps look professional


Pre-requisite

Basic understanding of ggplot2 is necessary. Here are some resources:

Tips to make the most of the lecture notes


Interactive navigation tools

  • Click on the three horizontally stacked lines at the bottom left corner of the slide, then you will see table of contents, and you can jump to the section you want

  • Hit letter “o” on your keyboard and you will have a panel view of all the slides


Running and writing codes

  • The box area with a hint of blue as the background color is where you can write code (hereafter referred to as the “code area”).
  • Hit the “Run Code” button to execute all the code inside the code area.
  • You can evaluate (run) code selectively by highlighting the parts you want to run and hitting Command + Enter for Mac (Ctrl + Enter for Windows).
  • If you want to run the codes on your computer, you can first click on the icon with two sheets of paper stacked on top of each other (top right corner of the code chunk), which copies the code in the code area. You can then paste it onto your computer.
  • You can click on the reload button (top right corner of the code chunk, left to the copy button) to revert back to the original code.

Create maps from sf using the ggplot2 package

  • Creating maps differs from creating non-spatial figures in some ways. However, the underlying principle and syntax under ggplot2 to create maps and non-spatial figures are very similar.

  • Indeed, you will find map making very intuitive and rather easy if you already have some knowledge of how ggplot2 works even if you have not created maps using ggplot2. The only major difference between them is the choice of geom_*() types.

To create a map from sfs, we use geom_sf().


Check how each of them looks like using plot():

Instruction

  • We can use geom_sf() to create maps from sf objects

  • geom_sf() automatically detects the geometry type of spatial objects stored in an sf object and draw maps accordingly


Syntax

ggplot() +
  geom_sf(data = sf)

Try to create a map using one of wells_ne_sf, ne_counties, and railroads_ne.

  • Sometime, you would like to print texts on a map like below

When you want to print labels on a map, you can use geom_sf_text(). geom_sf() cannot do it.


Syntax

geom_sf_text(aes(label = var_name)) 
  • var_name: name of the label variable


Example

  • We can do faceting just like we do with other types of figures you can create with ggplot2.
  • Remember, map is just a special case of ggplot2 figures.
  • We use corn_acres_ne for demonstration. This is a county-level corn harvested acres data observed annually from 2020 through 2023.

  • Notice that a single county has multiple rows (one row for one year) with the identical geometry

Specifying aesthetics

Here are some of the aesthetic variables for points:

  • color: color of the points
  • fill: available for some shapes (but likely useless)
  • shape: shape of the points
  • size: size of the points (rarely useful)
  • color: dependent on gw_extracted (the amount of groundwater extraction)
  • size: constant across the points (bigger than the default)
  • color: constant across the points (blue)
  • size: dependent on gw_extracted
  • shape: constant at 2 across the points (square)
  • color: constant across the points (blue)
  • fill: constant across the points (red)
  • size: constant at 1.5 across the points
  • shape: constant at 22 across the points (square)

Here are some of the aesthetic variables for polygons:

  • color: color of the borders of the polygons
  • linewidth: width of the borders of the polygons
  • fill: color of the inside of the polygons
  • shape: not available
  • size: not available
  • color: constant at “red” across the polygons
  • fill: constant at “darkgreen” across the polygons
  • linewidth: constant at 0.4 across the polygons
  • color: depends on name
  • fill: constant at “darkgreen” across the polygons
  • color: constant at “red” across the polygons
  • fill: depends on “countyfp”

Here are some of the aesthetic variables for lines:

  • color: color of the lines
  • linewidth: width of the lines
  • color: constant at “blue” across the lines
  • linewidth: constant at 0.5 across the lines

Plotting multiple spatial objects in one figure

  • It is often the case that you want to create a map using more than one spatial objects.

  • For example, you want to have county boundary (ne_counties), railroads (railroads_ne), and wells (wells_ne_sf) all in one map.

  • You can create layers with geom_sf() by setting different sf objects as the datasets individually, and then simply add them so they appear in a single map.

  • Remember that when you specify data in ggplot(), all subsequent geom_*() functions will use this data unless otherwise specified.



Instruction

  • Uncomment line 3 and add + add the end of line 2, run, and see what happens
  • Confirm the first one works fine because data is set globally to ne_couties in line 1.
  • Confirm the second one does not because global dataset is not set.

geom_sf()s that are added later are superimposed on top of the existing layers

Wells are hidden beneath the county layer:

Instruction

Hide the railroads beneath the county layer.


ggplot() uses the CRS of the sf to draw a map by default.

Currently, ne_counties is unprojected:


Let’s project it to WGS 84, UTM zone 14.


  • Now, the map is drawn based on the new CRS of 32614

  • Notice that the major grid lines are no longer straight in figure at the bottom unlike the one at the top

  • X-Y labels are still in longitude and latitude (we will see how we change this)

You can use coord_sf() to alter the CRS on the map on the go, but not the CRS of the sf object itself.


  • pros: you do not have to change the CRS of the sf
  • cons: it takes time to change the CRS behind the scene every time you do this

In order to have X and Y values in the same units as that of the CRS in use on the map, you need to add datum = in coord_sf().

When there are multiple geom_sf() layers, the CRS of the first layers is automatically applied for all the layers , reconciling the difference in CRS automatically.

  • coord_sf() applies to all the layers.
  • try the codes with and without coord_sf(32614) at the end

Making maps look professional

theme_void() is a veery suitable pre-made theme for map that can get rid of many unnecessary components from default maps.


The ggspatial package lets you put a north arrow and scale bar on a map using annotation_scale() and annotation_north_arrow()


  • location: determines where the scale bar is
    • first letter is either t (top) or b (bottom)
    • second letter is either l (left) or r (right).
  • width_hint: determines the length of the scale bar relative to the plot

Try yourself

Play with location and width_hint and see what happens.

Use pad_x and pad_y options to fine-tune the location of the scale bar.

A positive number means that the scale bar will be placed further away from closest border of the plot.

  • pad_x: since the second letter of location is l, the scale bar move an inch from the left border of the map

  • pad_y: since the first letter of location is b, the scale bar move 0.3 inches from the bottom border of the map

Try yourself

Play with pad_x and pad_y and see what happens.

  • Use annotation_north_arrow() to add north arrow
  • It works just like annotation_scale()
  • use style option to pick a different type of north arrow symbol

Inset map

Inset map (like one below) provides a better sense of the geographic extent and the location of the area of interest relative to the larger geographic extent that the readers are more familiar with.

Create a map like this using ne_counties with the ggmapinset package.

Note

Visit the ggmapinset website for more examples and other functionalities beyond what is presented here, including multiple insets.

The first step of making an inset map is to create the base map layer, a part of which is going to be expanded as an inset.

We want to create a map of all the counties in Nebraska with only the three counties (Perkins, Chase, and Dundy) colored red.

Let’s first create an sf consisting of the three counties first:


We now create the base map. You use geom_sf() to create base map layers.

We now configure (specify) the inset using configure_inset(). Here is the list of parameters you want to provide:

  • centre: the geographic coordinates of the small circle from which you expand
  • translation: how much you shift in x and y from the center to display the enlarged circle
  • radius: radius of the small circle at the origin
  • scale: how much to enlarge
  • units: length unit
  • Use geom_sf_inset() and/or geom_sf_text_inset() to create layers to present as an inset.
  • Use geom_inset_frame() to add the inset frame (small circle, big circle, and the lines connecting them)
  • Use coord_sf_inset(inset = inset_config) to reflect the configuration you set up earlier.



Try yourself

  • Comment out a line of codes above, run the code, and see what each of them do.
  • Go back to the previous slide and change the value of the parameters to see what happens.
  • By default, geom_sf_inset() creates two copies of the map layer: one for the base map and the other for the inset map.

  • map_base option in geom_sf_inset() determines whether you create the copy for the base map or not.

In the code below, map_base is not specified, meaning that geom_sf_inset(data = three_counties, fill = "black") will be applied for both the base and inset maps.


Try yourself

Comment out line 2 and comment in line 3 to see what happens.

Create maps using raster data

  • We will use the gpplot2 and tidyterra package to create maps using raster data.

  • The tidyterra package provides geom_spatraster() that works specifically for SpatRaster object from the terra package.

  • There are other options like the tmap package. But, we do not talk about it in this course.

It works very much like map creation with sf. We just use SpatRaster object and use geom_spatraster() in place of geom_sf.


Note

Notice that geom_spatraster() automatically based the fill color of the cells on the attribute values without you specifying so.

By default, the cells with NA values are colored grey. You can set the color for such cells using the na.value option inside scale_*() function. For example, the following code makes the cells with NA values transparent (invisible).

How does geom_spatraster() behave with a multi-layer SpatRaster?


As the warning message suggests, both layers are plotted by default. You can specify which layer to use with aes(fill = layer_name) like below.


Note

  • But, remember faceting is often not appropriate when you are plotting multiple variable of different scales (e.g., temperature and precipitation) as they share the same legend.
  • A good use case of faceting is displaying the same variable observed at different times (e.g., temperature on different days).

When you want to create maps for individual attributes at the same time, you can add facet_wrap(~lyr).

Create a map with both sf and SpatRaster

It is very easy to achieve this. You just use geom_sf() for sf and geom_spatraster() for SpatRaster. You just add them as layers just like any figures you create with ggplot2.