For this example, we will use the following packages.

library(tidyverse)
library(cowplot)     # for theme_map()
library(colorspace)  # for scale_fill_continuous_sequential()
library(sf)          # for manipulation of simple features objects

The dataset is provided as practicalgg::texas_income. Let’s look at it in table form and in a simple longitude–latitude plot.

# attach data set
data(texas_income, package = "practicalgg")

texas_income
## Simple feature collection with 254 features and 5 fields
## geometry type:  MULTIPOLYGON
## dimension:      XY
## bbox:           xmin: -106.6456 ymin: 25.83738 xmax: -93.50829 ymax: 36.5007
## geographic CRS: NAD83
## First 10 features:
##    GEOID                    NAME   variable estimate  moe
## 1  48001  Anderson County, Texas B19013_001    41327 1842
## 2  48003   Andrews County, Texas B19013_001    70423 6038
## 3  48005  Angelina County, Texas B19013_001    44223 1611
## 4  48007   Aransas County, Texas B19013_001    41690 3678
## 5  48009    Archer County, Texas B19013_001    60275 5182
## 6  48011 Armstrong County, Texas B19013_001    59737 4968
## 7  48013  Atascosa County, Texas B19013_001    52192 3005
## 8  48015    Austin County, Texas B19013_001    53687 3810
## 9  48017    Bailey County, Texas B19013_001    37397 8652
## 10 48019   Bandera County, Texas B19013_001    49863 7193
##                          geometry
## 1  MULTIPOLYGON (((-96.0648 31...
## 2  MULTIPOLYGON (((-103.0647 3...
## 3  MULTIPOLYGON (((-95.00488 3...
## 4  MULTIPOLYGON (((-96.8229 28...
## 5  MULTIPOLYGON (((-98.95382 3...
## 6  MULTIPOLYGON (((-101.6294 3...
## 7  MULTIPOLYGON (((-98.80479 2...
## 8  MULTIPOLYGON (((-96.62085 3...
## 9  MULTIPOLYGON (((-103.0469 3...
## 10 MULTIPOLYGON (((-99.60332 2...
ggplot(texas_income, aes(fill = estimate)) + 
  geom_sf()

Transform to more appropriate coordinate system, EPSG:3083, a Texas-centric Albers equal area projection.

# EPSG:3083 Texas-centric Albers equal area
# https://epsg.io/3083
texas_crs <- "+proj=aea +lat_1=27.5 +lat_2=35 +lat_0=18 +lon_0=-100 +x_0=1500000 +y_0=6000000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs"

texas_transf <- st_transform(texas_income, crs = texas_crs) 

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf()

Use different color scale.

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf(color = "white") +
  scale_fill_continuous_sequential(
    palette = "Blues", rev = TRUE,
    na.value = "grey60"
  )

We want the legend to be oriented horizontally and placed to the lower left of Texas. Therefore, we need to change the plot limits to create space for the legend. We choose limits of -110, -93.5 degrees longitude at 30 degrees latitude, transformed to X, Y with EPSG:3083. We could do the transformation with st_transform(), as above, or we can do it manually at https://epsg.io/transform.

# https://epsg.io/transform#s_srs=4326&t_srs=3083&x=-110.0000000&y=30.0000000
# https://epsg.io/transform#s_srs=4326&t_srs=3083&x=-93.5000000&y=30.0000000
# (-110, 30) is (538250.08, 7363459.44)
# (-93.5, 30) is (2125629.02, 7338358.43)

texas_xlim <- c(538250, 2125629)

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf(color = "white") +
  coord_sf(xlim = texas_xlim) +
  scale_fill_continuous_sequential(
    palette = "Blues", rev = TRUE,
    na.value = "grey60"
  )

Now we can move the legend into place.

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf(color = "white") +
  coord_sf(xlim = texas_xlim) +
  scale_fill_continuous_sequential(
    palette = "Blues", rev = TRUE,
    na.value = "grey60",
    guide = guide_colorbar(
      direction = "horizontal",
      label.position = "bottom",
      title.position = "top",
      barwidth = grid::unit(3.0, "in"),
      barheight = grid::unit(0.2, "in")
    )
  ) +
  theme(
    legend.title.align = 0.5,
    legend.text.align = 0.5,
    legend.justification = c(0, 0),
    legend.position = c(0.02, 0.1)
  )

Fine-tune legend title and breaks.

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf(color = "white") +
  coord_sf(xlim = texas_xlim) +
  scale_fill_continuous_sequential(
    palette = "Blues", rev = TRUE,
    na.value = "grey60",
    name = "annual median income (USD)",
    limits = c(18000, 90000),
    breaks = 20000*c(1:4),
    labels = c("$20,000", "$40,000", "$60,000", "$80,000"),
    guide = guide_colorbar(
      direction = "horizontal",
      label.position = "bottom",
      title.position = "top",
      barwidth = grid::unit(3.0, "in"),
      barheight = grid::unit(0.2, "in")
    )
  ) +
  theme(
    legend.title.align = 0.5,
    legend.text.align = 0.5,
    legend.justification = c(0, 0),
    legend.position = c(0.02, 0.1)
  )

Switch the theme to a simple, mostly empty theme suitable for a map.

ggplot(texas_transf, aes(fill = estimate)) + 
  geom_sf(color = "white") +
  coord_sf(xlim = texas_xlim) +
  scale_fill_continuous_sequential(
    palette = "Blues", rev = TRUE,
    na.value = "grey60",
    name = "annual median income (USD)",
    limits = c(18000, 90000),
    breaks = 20000*c(1:4),
    labels = c("$20,000", "$40,000", "$60,000", "$80,000"),
    guide = guide_colorbar(
      direction = "horizontal",
      label.position = "bottom",
      title.position = "top",
      barwidth = grid::unit(3.0, "in"),
      barheight = grid::unit(0.2, "in")
    )
  ) +
  theme_map(12) +
  theme(
    legend.title.align = 0.5,
    legend.text.align = 0.5,
    legend.justification = c(0, 0),
    legend.position = c(0.02, 0.1)
  )