Reputation: 8198
I am trying to add mean and SD to panel plot created using tmap
R package. But each panel contains all the annotations overlapped. Now how can I have single annotations per panel? Here is a minimal reproducible code
library(tmap)
library(stars)
library(terra)
#Read some data
tif = system.file("tif/L7_ETMs.tif", package = "stars")
x = terra::rast(tif)
# Summarize data (one summary per raster layer)
summ <- x |>
as.data.frame() |>
pivot_longer(everything()) |>
group_by(name) |>
summarise(Mean = mean(value, na.rm = TRUE),
SD = sd(value, na.rm = TRUE)) %>%
mutate(across(c(Mean, SD), ~round(., 3))) %>%
mutate(lab = paste0("Mean = ", Mean, "\nSD = ", SD)) %>%
dplyr::select(name, lab)
# Add coordinates for bottom-left placement
bbox <- st_bbox(x)
annotations <- summ %>%
mutate(
x = bbox["xmin"] + 0.05 * (bbox["xmax"] - bbox["xmin"]), # Slightly offset from xmin
y = bbox["ymin"] + 0.1 * (bbox["ymax"] - bbox["ymin"]) # Slightly offset from ymin
)
# Convert annotations to sf object
annotations_sf <- st_as_sf(annotations, coords = c("x", "y"), crs = st_crs(x))
#Plot it using tmap r package
map <- tm_shape(x) +
tm_raster(style="quantile") +
tm_facets(nrow = 3) +
tm_layout(panel.labels = names(x), attr.outside = T,
attr.outside.position = "bottom", attr.just = "right",
legend.outside = T, legend.outside.position = "right",
legend.position = c("left", "top"),
legend.text.size = 0.8, legend.title.size = 0.9,
legend.format = list(digits = 2, text.separator = "-"))+
tm_compass(position = c("left", "top"))+
tm_scale_bar(text.size = 1, position = c("RIGHT", "top"))
# Add annotations
map +
tm_shape(annotations_sf) +
tm_text("lab", xmod = 0.05, ymod = -0.05, col = "black", just = "left", size = 1)
Upvotes: 1
Views: 59
Reputation: 909
You can solve this issue by using tm_credits()
with a character vector instead of adding another tm_shape() and annotating it with a tm_text()
aesthetic. Simply take the text from annotations_sf$lab
and pass them as the first argument in tm_credits()
. For the position within the facet, you can specify this with the argument position=c("bottom", "left")
. Finally, you will need to slightly alter your call to tm_layout()
. Instead of setting attr.outside=T
, set it to attr.outside=F
this will ensure the credits are plotted within the facets. You can still plot the legend outside of the facets by setting legend.outside=T
, which will override the previous argument. Here is the full, reproducible solution:
library(tmap)
library(stars)
library(terra)
library(dplyr)
library(tidyr)
#Read some data
tif = system.file("tif/L7_ETMs.tif", package = "stars")
x = terra::rast(tif)
# Summarize data (one summary per raster layer)
summ <- x |>
as.data.frame() |>
pivot_longer(everything()) |>
group_by(name) |>
summarise(Mean = mean(value, na.rm = TRUE),
SD = sd(value, na.rm = TRUE)) %>%
mutate(across(c(Mean, SD), ~round(., 3))) %>%
mutate(lab = paste0("Mean = ", Mean, "\nSD = ", SD)) %>%
dplyr::select(name, lab)
# Add coordinates for bottom-left placement
bbox <- st_bbox(x)
annotations <- summ %>%
mutate(
x = bbox["xmin"] + 0.05 * (bbox["xmax"] - bbox["xmin"]), # Slightly offset from xmin
y = bbox["ymin"] + 0.1 * (bbox["ymax"] - bbox["ymin"]) # Slightly offset from ymin
)
# Convert annotations to sf object
annotations_sf <- st_as_sf(annotations, coords = c("x", "y"), crs = st_crs(x))
#Plot it using tmap r package
tm_shape(x) +
tm_raster(style="quantile") +
tm_credits(text=st_drop_geometry(annotations_sf$lab), position=c("left", "bottom"), col = "black", just = "left", size = 2)+
tm_facets(nrow=3)+
tm_layout(panel.labels = names(x), attr.outside = F,
attr.outside.position = "bottom", attr.just = "right",
legend.outside = T, legend.outside.position = "right",
legend.position = c("left", "top"),
legend.text.size = 0.8, legend.title.size = 0.9,
legend.format = list(digits = 2, text.separator = "-"))+
tm_compass(position = c("left", "top"))+
tm_scale_bar(text.size = 1, position = c("RIGHT", "top"))
Upvotes: 1