Skip to contents

This is a compendium of questions arisen on the use of the tidyterra package and the potential solutions to it (mostly related with the use of terra and ggplot2 at this stage). You can ask for help or search previous questions in the following links.

NA values are shown in gray color

This is the default behavior produced by the ggplot2 package. tidyterra color scales (i.e., scale_fill_whitebox_c(), etc.), has by default the parameter na.value set to NA, that prevents NA values to be plotted.

library(terra)
library(tidyterra)
library(ggplot2)

# Get a raster data, out beloved volcano on hi-res
volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}
r <- volcanotemp %>%
  rast() %>%
  filter(elevation > 80 & elevation < 180)

# Default
def <- ggplot() +
  geom_spatraster(data = r)

def +
  labs(
    title = "Default on ggplot2",
    subtitle = "NA values in grey"
  )


# Modify with scales
def +
  scale_fill_continuous(na.value = NA) +
  labs(
    title = "Default colors on ggplot2",
    subtitle = "But NAs are not plotted"
  )


# Use a different scale provided by ggplot2
def +
  scale_fill_viridis_c(na.value = "orange") +
  labs(
    title = "Use any fill_* scale of ggplot2",
    subtitle = "Note that na.value = 'orange'"
  )

Labeling contours

Thanks to fortify.SpatRaster() you can use your SpatRaster straight away with the metR package. Use the parameter(s) bins/binwidth/breaks to align both labels and lines:

library(terra)
library(tidyterra)
library(ggplot2)
library(metR)

volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}


r <- rast(volcanotemp)

ggplot(r) +
  geom_spatraster_contour(data = r) +
  geom_text_contour(
    aes(x, y, z = elevation),
    check_overlap = TRUE,
    stroke = 0.2,
    stroke.colour = "white"
  ) +
  labs(
    title = "Labelling contours",
    width = 2,
    x = "", y = ""
  )


# Modify number or bins

ggplot(r) +
  geom_spatraster_contour(
    data = r,
    binwidth = 25
  ) +
  geom_text_contour(
    aes(x, y, z = elevation),
    binwidth = 25,
    check_overlap = TRUE,
    stroke = 0.2, stroke.colour = "white"
  ) +
  labs(
    title = "Labelling contours",
    subtitle = "Aligning breaks",
    width = 2,
    x = "", y = ""
  )

Using a different color scale

Since tidyterra leverages on ggplot2, please refer to ggplot2 use of scales:

library(terra)
library(tidyterra)
library(ggplot2)

volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}


r <- rast(volcanotemp)

# Hillshade with grey colors
slope <- terrain(r, "slope", unit = "radians")
aspect <- terrain(r, "aspect", unit = "radians")
hill <- shade(slope, aspect, 10, 340)

ggplot() +
  geom_spatraster(data = hill, show.legend = FALSE) +
  # Note the scale, grey colours
  scale_fill_gradientn(colours = grey(0:100 / 100), na.value = NA) +
  labs(title = "A hillshade plot with grey colors")

Can I change the default palette of my maps?

Yes, use options("ggplot2.continuous.fill") to modify the default colors on your session.

library(terra)
library(tidyterra)
library(ggplot2)

volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}


r <- rast(volcanotemp)

p <- ggplot() +
  geom_spatraster(data = r)


# Set options
tmp <- getOption("ggplot2.continuous.fill") # store current setting
options(ggplot2.continuous.fill = scale_fill_terrain_c)

p


# restore previous setting
options(ggplot2.continuous.fill = tmp)


p

My map tiles are blurry

This is probably related with the tile itself rather than the package. Most base tiles are provided in EPSG:3857, so check first if your tile has this CRS and not a different one. Not having EPSG:3857 may be an indication that the tile has been reprojected, implied some sort of sampling that causes the blurriness on your data. Also, modify the parameter maxcell to avoid resampling and force the ggplot2 map to be on EPSG:3857 with ggplot2::coord_sf(crs = 3857):

library(terra)
library(tidyterra)
library(ggplot2)
library(sf)
library(maptiles)

# Get a tile from a point on sf format

p <- st_point(c(-97.09, 31.53)) %>%
  st_sfc(crs = 4326) %>%
  st_buffer(750)

tile1 <- get_tiles(p, provider = "Stamen.Terrain", zoom = 14, cachedir = ".")

ggplot() +
  geom_spatraster_rgb(data = tile1) +
  geom_sf(data = p, fill = NA) +
  labs(title = "This is a bit blurry...")


st_crs(tile1)$epsg
#> [1] 4326

# The tile was in EPSG 4326

# get tile in 3857
p2 <- st_transform(p, 3857)


tile2 <- get_tiles(p2, provider = "Stamen.Terrain", zoom = 14, cachedir = ".")

st_crs(tile2)$epsg
#> [1] 3857

# Now the tile is EPSG:3857

ggplot() +
  geom_spatraster_rgb(data = tile2, maxcell = Inf) +
  geom_sf(data = p, fill = NA) +
  # Force crs to be 3857
  coord_sf(crs = 3857) +
  labs(
    title = "See the difference?",
    subtitle = "Init crs=3857 and maxcell modified"
  )

Avoid degrees labeling on axis

Again, this is the ggplot2 default, but can be modified with ggplot2::coord_sf(datum) argument:

library(terra)
library(tidyterra)
library(ggplot2)
library(sf)

volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}


r <- rast(volcanotemp)

ggplot() +
  geom_spatraster(data = r) +
  labs(
    title = "Axis auto-converted to lon/lat",
    subtitle = paste("But SpatRaster is EPSG:", st_crs(r)$epsg)
  )



# Use datum

ggplot() +
  geom_spatraster(data = r) +
  coord_sf(datum = pull_crs(r)) +
  labs(
    title = "Axis on the units of the SpatRaster",
    subtitle = paste("EPSG:", st_crs(r)$epsg)
  )

Modifying the number of breaks on axis

The best option is to pass your custom breaks to ggplot2::scale_x_continous() or ggplot2::scale_y_continous(). You will need to provide the breaks in lon/lat even if your data is projected (¿?):

library(terra)
library(tidyterra)
library(ggplot2)
library(sf)

volcanotemp <- file.path(tempdir(), "volcano2hires.tif")

# Download example file
volcanourl <- paste0(
  "https://github.com/dieghernan/tidyterra/blob/main/",
  "data-raw/volcano2hires.tif?raw=true"
)

if (!file.exists(volcanotemp)) {
  download.file(volcanourl, volcanotemp, mode = "wb")
}

r <- rast(volcanotemp)

ggplot() +
  geom_spatraster(data = r) +
  labs(title = "Default axis breaks")


# Modify y breaks

# Get extent

ext <- r %>%
  project("EPSG:4326", mask = TRUE) %>%
  ext() %>%
  as.vector()

br_y <- seq(ext["ymin"], ext["ymax"], length.out = 1000) %>%
  pretty(n = 3) %>%
  round(3) %>%
  unique()

ggplot() +
  geom_spatraster(data = r) +
  scale_y_continuous(
    expand = expansion(mult = 0.05),
    breaks = br_y
  ) +
  labs(title = "Three breaks on y")

Plotting SpatRasters with color tables

You can extract the corresponding colors with terra::coltab(), name them with the name of the corresponding factor level and use ggplot2::scale_fill_manual():

library(terra)
library(tidyterra)
library(ggplot2)

# Get a SpatRaster with coltab
r_coltab <- rast(system.file("raster/nlcd.tif", package = "spDataLarge"))

has.colors(r_coltab)
#> [1] TRUE

r_coltab
#> class       : SpatRaster 
#> dimensions  : 1359, 1073, 1  (nrow, ncol, nlyr)
#> resolution  : 31.5303, 31.52466  (x, y)
#> extent      : 301903.3, 335735.4, 4111244, 4154086  (xmin, xmax, ymin, ymax)
#> coord. ref. : NAD83 / UTM zone 12N (EPSG:26912) 
#> source      : nlcd.tif 
#> color table : 1 
#> categories  : levels 
#> name        :   levels 
#> min value   :    Water 
#> max value   : Wetlands

# Native handling
plot(r_coltab)



# If we try with tidyterra we don't get the right colors

ggcoltab <- ggplot() +
  geom_spatraster(data = r_coltab)

ggcoltab



# We extract levels and colors to create a custom palette

levs <- r_coltab %>% pull(levels) %>% levels()

levs
#> [1] "NA"         "Water"      "Developed"  "Barren"     "Forest"    
#> [6] "Shrubland"  "Herbaceous" "Cultivated" "Wetlands"

# Coltab by positions
tb <- coltab(r_coltab)[[1]] %>% slice(seq_len(length(levs)))
tb
#>   value red green blue alpha
#> 1     0 255   255  255   255
#> 2     1  71   107  160   255
#> 3     2 170     0    0   255
#> 4     3 178   173  163   255
#> 5     4 104   170   99   255
#> 6     5 165   140   48   255
#> 7     6 201   201  119   255
#> 8     7 219   216   61   255
#> 9     8 186   216  234   255

# Create named palette
coltabpal <- grDevices::rgb(tb[, 2:5], maxColorValue = 255) %>%
  setNames(levs)

coltabpal
#>         NA      Water  Developed     Barren     Forest  Shrubland Herbaceous 
#>  "#FFFFFF"  "#476BA0"  "#AA0000"  "#B2ADA3"  "#68AA63"  "#A58C30"  "#C9C977" 
#> Cultivated   Wetlands 
#>  "#DBD83D"  "#BAD8EA"

# And apply to out initial ggplot2

ggcoltab +
  scale_fill_manual(values = coltabpal)