I am having difficulty creating a map with scale_fill_manual
to fill regions on the map based on some property in the region. I am using tidyterra
with terra
and the tidyverse
packages. I am unaware of methods for writing self-contained map files in r, so I provide a link to download a map file that is suitable for demonstrating the issue I am having (from Birds Canada).
The problem appears as though the factor levels were unordered or ordered incorrectly. Similar to these posts here and here. However, I am explicitly naming factors created by cut
, which has an argument to order the factor output. Here is a link to download the 24 MB zip file with the map.
library(terra)
library(tidyterra)
library(tidyverse)
map_file <- vect("~/Downloads/BCR_Terrestrial/BCR_Terrestrial_master.shp") %>%
project("+proj=lcc +lat_0=49 +lon_0=-95 +lat_1=49 +lat_2=77 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs") %>%
subset(., !(.$OBJECTID %in% c(1, 354, 371, 372)) & .$COUNTRY != "MEXICO")
set.seed(123)
properties <- tibble(
Mean = rnorm(9, mean = 0, sd = 0.25),
BCR = rep(c(6,11,17),3),
Process = c(1,1,1,2,2,2,3,3,3)) %>%
mutate(
Mu = cut(Mean, breaks = c(-Inf, -0.4, -0.3, -0.2, -0.1, -0.05, 0.05, 0.1, 0.2, 0.3, 0.4, Inf),
labels = c("< 0.4", "-0.4 : -0.3", "-0.3 : -0.2", "-0.2 : -0.1", "-0.1 : -0.05", "-0.05 : 0.05",
"0.05 : 0.1", "0.1 : 0.2", "0.2 : 0.3", "0.3 : 0.4", "> 0.4"),
ordered_result = T))
sp_map <- merge(subset(map_file, map_file$BCR %in% properties[["BCR"]]), properties)
ggplot(sp_map)+
geom_spatvector(aes(fill = Mu))+
facet_wrap(~Process)+
geom_spatvector(data = subset(map_file, !map_file$BCR %in% sp_map$BCR), fill = "white", size = 0.1)+
scale_fill_manual(values = c("#a50026",
"#d73027",
"#f46d43",
"#fdae61",
"#fee090",
"#ffffbf",
"#e0f3f8",
"#abd9e9",
"#74add1",
"#4575b4",
"#313695"),
name = "Spice", drop = F)+
theme_minimal()+
theme(axis.title = element_blank())
Running this code, you will see that the colours in the map legend are ordered correctly but the factor levels themselves are not. Also, the drop = F
argument is ignored (not every level of the factor is in the legend).
Alternatively, if I name the values
vector argument like this:
ggplot(sp_map)+
geom_spatvector(aes(fill = Mu))+
facet_wrap(~Process)+
geom_spatvector(data = subset(map_file, !map_file$BCR %in% sp_map$BCR), fill = "white", size = 0.1)+
scale_fill_manual(values = c("< 0.4"="#a50026",
"-0.4 : -0.3"="#d73027",
"-0.3 : -0.2"="#f46d43",
"-0.2 : -0.1"="#fdae61",
"-0.1 : -0.05"="#fee090",
"-0.05 : 0.05"="#ffffbf",
"0.05 : 0.1"="#e0f3f8",
"0.1 : 0.2"="#abd9e9",
"0.2 : 0.3"="#74add1",
"0.3 : 0.4"="#4575b4",
"> 0.4"="#313695"),
name = "Spice", drop = F)+
theme_minimal()+
theme(axis.title = element_blank())
The order of the colours is respected among factor levels but not in the legend. Furthermore, still not all levels are represented in the legend. I am stumped. The two attempts here are my most salient efforts but I have tried reordering in another factor
call, remove labels, changing labels, so on...
Any guidance here would be very much appreciated.
For posterity, I'll post the answer to resolve the two issues that arose in my code.
Although the factor levels were defined and ordered in the data object, it seems like the spatvector
map object converted all the factors back into characters. To fix this, I converted the argument to fill
back into a factor in place in the ggplot call. This allowed the drop=F
argument and the order to be respected.
ggplot(sp_map)+
geom_spatvector(aes(fill = factor(Mu, levels = c("< 0.4", "-0.4 : -0.3", "-0.3 : -0.2", "-0.2 : -0.1", "-0.1 : -0.05", "-0.05 : 0.05",
"0.05 : 0.1", "0.1 : 0.2", "0.2 : 0.3", "0.3 : 0.4", "> 0.4"))))+
facet_wrap(~Process)+
geom_spatvector(data = subset(map_file, !map_file$BCR %in% sp_map$BCR), fill = "white", size = 0.1)+
scale_fill_manual(values = c("< 0.4"="#a50026",
"-0.4 : -0.3"="#d73027",
"-0.3 : -0.2"="#f46d43",
"-0.2 : -0.1"="#fdae61",
"-0.1 : -0.05"="#fee090",
"-0.05 : 0.05"="#ffffbf",
"0.05 : 0.1"="#e0f3f8",
"0.1 : 0.2"="#abd9e9",
"0.2 : 0.3"="#74add1",
"0.3 : 0.4"="#4575b4",
"> 0.4"="#313695"),
name = "Spice", drop = F)+
theme_minimal()+
theme(axis.title = element_blank())
Generates:
I don't know why the spatvector has this behaviour, whether I am misusing the package or made some error somewhere. In any event, hopefully this is useful someone else down the road!