diff --git a/R/VizEquiMap.R b/R/VizEquiMap.R index 2633cebc9c96ed3980dd881cf7b16e4b5eb9d17c..07db73715c60953bf1ba8b2ec6d964f7d2117d72 100644 --- a/R/VizEquiMap.R +++ b/R/VizEquiMap.R @@ -38,6 +38,10 @@ #' 'title_scale'. #'@param sizetit Scale factor for the figure top title provided in parameter #' 'toptitle'. Deprecated. Use 'title_scale' instead. +#'@param caption A character string of the caption located at the left-bottom of +#' the plot. Captions with multiple lines can be constructed using string +#' manipulation functions like \code{paste()} or \code{paste0()}, using +#' \code{"\n"} to indicate line breaks. #'@param units Title at the top of the colour bar, most commonly the units of #' the variable provided in parameter 'var'. #'@param brks,cols,bar_limits,triangle_ends Usually only providing 'brks' is @@ -177,6 +181,9 @@ #'@param drawleg Whether to plot a color bar (legend, key) or not. Defaults to #' TRUE. It is not possible to plot the colour bar if 'add = TRUE'. Use #' ColorBar() and the return values of PlotEquiMap() instead. +#'@param vertical TRUE/FALSE for vertical/horizontal colour bar. Default is +#' FALSE. Parameters 'width' and 'height' might need to be modified to +#' accommodate the vertical colour bar. #'@param boxlim Limits of a box to be added to the plot, in degrees: #' c(x1, y1, x2, y2). A list with multiple box specifications can also be #' provided. @@ -188,6 +195,8 @@ #' the format c(y1, x1, y2, x2). Defaults to rep(1, 4). If drawleg = TRUE, #' then margin_scale[1] is subtracted 1 unit. #'@param title_scale Scale factor for the figure top title. Defaults to 1. +#'@param caption_size Scale factor for the figure caption. Default is 0.8 (1 if +#' vertical = TRUE). #'@param numbfig Number of figures in the layout the plot will be put into. #' A higher numbfig will result in narrower margins and smaller labels, #' axe labels, ticks, thinner lines, ... Defaults to 1. @@ -242,7 +251,9 @@ #' #'VizEquiMap(var[1, 1, 1, 1, , ], lon = lons, lat = lats, #' toptitle = 'Near-surface temperature anomaly, Nov. 2000', -#' filled.continents = FALSE, title_scale = 0.7) +#' filled.continents = FALSE, title_scale = 0.7, +#' caption = paste0("This is a test caption.", "\n", +#' "This is a new line.") #' } #'@import graphics maps utils #'@importFrom grDevices dev.cur dev.new dev.off gray @@ -250,8 +261,8 @@ #' @importFrom s2dv InsertDim #'@export VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, - toptitle = NULL, sizetit = NULL, units = NULL, - brks = NULL, cols = NULL, bar_limits = NULL, + toptitle = NULL, sizetit = NULL, caption = NULL, + units = NULL, brks = NULL, cols = NULL, bar_limits = NULL, triangle_ends = NULL, col_inf = NULL, col_sup = NULL, colNA = NULL, color_fun = ClimPalette(), square = TRUE, filled.continents = FALSE, @@ -271,7 +282,7 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, intylat = 20, intxlon = 20, xlonshft = 0, ylatshft = 0, xlabels = NULL, ylabels = NULL, axes_tick_scale = 1, axes_label_scale = 1, - drawleg = TRUE, subsampleg = NULL, + drawleg = TRUE, vertical = FALSE, subsampleg = NULL, bar_extra_labels = NULL, draw_bar_ticks = TRUE, draw_separators = FALSE, triangle_ends_scale = 1, bar_label_digits = 4, bar_label_scale = 1, @@ -279,7 +290,7 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, bar_extra_margin = rep(0, 4), boxlim = NULL, boxcol = 'purple2', boxlwd = 5, margin_scale = rep(1, 4), title_scale = 1, - numbfig = NULL, fileout = NULL, + caption_size = 0.8, numbfig = NULL, fileout = NULL, width = 8, height = 5, size_units = 'in', res = 100, include_lower_boundary = TRUE, include_upper_boundary = TRUE, ...) { @@ -523,13 +534,27 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, title_scale <- sizetit } + # Check caption + if (!is.null(caption)) { + if (!is.character(caption)) { + stop("Parameter 'caption' must be a character string.") + } else { + num_lines <- length(strsplit(caption, "\n")[[1]]) + } + } + # Check include_lower_boundary and include_upper_boundary if (!is.null(include_lower_boundary) && (!is.logical(include_lower_boundary) || length(include_lower_boundary) != 1)) { stop("Parameter 'include_lower_boundary' must be a logical element.") } if (!is.null(include_upper_boundary) && (!is.logical(include_upper_boundary) || length(include_upper_boundary) != 1)) { stop("Parameter 'include_upper_boundary' must be a logical element.") - } + } + + # Check vertical + if (!is.logical(vertical)) { + stop("Parameter 'vertical' must be TRUE or FALSE.") + } tmp <- .create_var_limits(data = var, brks = brks, bar_limits = bar_limits, drawleg = drawleg) @@ -540,8 +565,8 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, # draw_separators, triangle_ends_scale, label_scale, units, units_scale, # bar_label_digits # Build: brks, cols, bar_limits, col_inf, col_sup - colorbar <- ColorBarContinuous(brks, cols, FALSE, subsampleg, bar_limits, var_limits, - triangle_ends, col_inf, col_sup, color_fun, FALSE, + colorbar <- ColorBarContinuous(brks, cols, vertical = vertical, subsampleg, bar_limits, + var_limits, triangle_ends, col_inf, col_sup, color_fun, FALSE, extra_labels = bar_extra_labels, draw_ticks = draw_bar_ticks, draw_separators = draw_separators, triangle_ends_scale = triangle_ends_scale, @@ -941,6 +966,16 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, if (!is.numeric(title_scale)) { stop("Parameter 'title_scale' must be numeric.") } + + # Check caption_size + if (!is.numeric(caption_size)) { + stop("Parameter 'caption_size' must be numeric.") + } + if (vertical) { + if (missing(caption_size)) { + caption_size <- 1 + } + } # Check axes_tick_scale if (!is.numeric(axes_tick_scale)) { @@ -1004,7 +1039,8 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, # ~~~~~~~~~~~~~~~~~~~~~ # if (drawleg) { - margin_scale[1] <- margin_scale[1] - 1 + margin_scale[1] <- margin_scale[1] - 1 + } margins <- rep(0.4, 4) * margin_scale margins[4] <- margins[4] + 1 @@ -1012,7 +1048,7 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, cex_axes_labels <- 1.3 * axes_label_scale cex_axes_ticks <- -0.5 * axes_tick_scale spaceticklab <- 0 - if (axelab) { + if (axelab) { # Y axis label if (!is.null(ylabels)) { ypos <- seq(latmin, latmax, intylat) + ylatshft @@ -1083,7 +1119,33 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, } if (drawleg) { - layout(matrix(1:2, ncol = 1, nrow = 2), heights = c(5, 1)) + if (!is.null(caption)) { + margins[2] <- margins[2] + num_lines*0.5 + margins[4] <- margins[4] + num_lines*0.5 + if (vertical) { # vertical bar, caption + layout(matrix(c(1, 2, 3, 3), ncol = 2, nrow = 2, byrow = TRUE), + widths = c(5, 1.3), + heights = c(5, 0.2 + num_lines*caption_size/6)) + + } else { # horizontal bar, caption + layout(matrix(c(1, 2, 3), ncol = 1, nrow = 3), + heights = c(5, 1, 0.2 + num_lines*caption_size/4)) + } + } else { + if (vertical) { # vertical bar, no caption + layout(matrix(c(1, 2, 1, 3), ncol = 2, nrow = 2, byrow = TRUE), + widths = c(5, 1.3), + heights = c(0.1, 5)) + } else { # horizontal bar, no caption + layout(matrix(1:2, ncol = 1, nrow = 2), heights = c(5, 1)) + } + } + } else { + if (!is.null(caption)) { + margins[2] <- margins[2] + num_lines*0.4 + margins[4] <- margins[4] + num_lines*0.4 + layout(matrix(1:2, ncol = 1, nrow = 2), heights = c(5, 0.1 + num_lines*caption_size/4)) + } } plot.new() # Load the user parameters @@ -1152,6 +1214,7 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, col = contour_color, drawlabels = contour_draw_label) } + # # Adding black dots or symbols # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1166,7 +1229,7 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, lwd = dot_size[counter] * 3 / sqrt(sqrt(length(var)))) } } - + # # Adding a mask # ~~~~~~~~~~~~~~~ @@ -1334,13 +1397,49 @@ VizEquiMap <- function(var, lon, lat, varu = NULL, varv = NULL, #come back to the previous xpd value par(xpd = xpdsave) } + + # + # Adding a caption + # ~~~~~~~~~~~~~~~~~ + # + if (!is.null(caption)) { + if (drawleg) { + if (vertical) { + par(mfg = c(2, 1)) + at_value <- par("usr")[1] - (0.38 + (num_lines - 1) * 0.15) + } else { + par(mfg = c(3, 1)) + at_value <- NA + } + } else { + par(mfg = c(2, 1)) + at_value <- NA + } + base_line <- 1 + mtext(caption, side = 1, line = base_line, + at = at_value, # left placement + adj = 0, + cex = caption_size, col = "black") + } + # # Colorbar # ~~~~~~~~~~ # if (drawleg) { - ColorBarContinuous(brks, cols, FALSE, subsampleg, bar_limits, var_limits, - triangle_ends, col_inf = col_inf, col_sup = col_sup, + if (vertical) { + if(is.null(caption)) { + par(mfg = c(2, 1)) + } else { + par(mfg = c(1, 2)) + } + } else { + if (!is.null(caption)) { + par(mfg = c(2, 1)) + } + } + ColorBarContinuous(brks, cols, vertical = vertical, subsampleg, bar_limits, + var_limits, triangle_ends, col_inf = col_inf, col_sup = col_sup, extra_labels = bar_extra_labels, draw_ticks = draw_bar_ticks, draw_separators = draw_separators, title = units, title_scale = units_scale, triangle_ends_scale = triangle_ends_scale, diff --git a/man/VizEquiMap.Rd b/man/VizEquiMap.Rd index 7fadb87685d85a5543a7a5a9df1a0cb5e324e76f..bad14aec2aa0a98b0fcfa1bd5d1890204d2b23cb 100644 --- a/man/VizEquiMap.Rd +++ b/man/VizEquiMap.Rd @@ -12,6 +12,7 @@ VizEquiMap( varv = NULL, toptitle = NULL, sizetit = NULL, + caption = NULL, units = NULL, brks = NULL, cols = NULL, @@ -63,6 +64,7 @@ VizEquiMap( axes_tick_scale = 1, axes_label_scale = 1, drawleg = TRUE, + vertical = FALSE, subsampleg = NULL, bar_extra_labels = NULL, draw_bar_ticks = TRUE, @@ -78,6 +80,7 @@ VizEquiMap( boxlwd = 5, margin_scale = rep(1, 4), title_scale = 1, + caption_size = 0.8, numbfig = NULL, fileout = NULL, width = 8, @@ -125,6 +128,11 @@ longitudinal and latitudinal coordinate dimensions are interchanged.} \item{sizetit}{Scale factor for the figure top title provided in parameter 'toptitle'. Deprecated. Use 'title_scale' instead.} +\item{caption}{A character string of the caption located at the left-bottom of +the plot. Captions with multiple lines can be constructed using string +manipulation functions like \code{paste()} or \code{paste0()}, using \code{"\n"} +to indicate line breaks.} + \item{units}{Title at the top of the colour bar, most commonly the units of the variable provided in parameter 'var'.} @@ -304,6 +312,10 @@ and latitude axes.} TRUE. It is not possible to plot the colour bar if 'add = TRUE'. Use ColorBar() and the return values of PlotEquiMap() instead.} +\item{vertical}{TRUE/FALSE for vertical/horizontal colour bar. Default is FALSE. +Parameters 'width' and 'height' might need to be modified to accommodate the +vertical colour bar.} + \item{draw_separators, triangle_ends_scale, bar_label_digits}{Set of parameters to control the visual aspect of the drawn colour bar (2/3). See ?ColorBar for a full explanation.} @@ -328,6 +340,9 @@ then margin_scale[1] is subtracted 1 unit.} \item{title_scale}{Scale factor for the figure top title. Defaults to 1.} +\item{caption_size}{Scale factor for the figure caption. Default is 0.8 (1 if +vertical = TRUE).} + \item{numbfig}{Number of figures in the layout the plot will be put into. A higher numbfig will result in narrower margins and smaller labels, axe labels, ticks, thinner lines, ... Defaults to 1.} @@ -401,6 +416,8 @@ lons <- attr(map_temp$exp, "Variables")$common$lon VizEquiMap(var[1, 1, 1, 1, , ], lon = lons, lat = lats, toptitle = 'Near-surface temperature anomaly, Nov. 2000', - filled.continents = FALSE, title_scale = 0.7) + filled.continents = FALSE, title_scale = 0.7, + caption = paste0("This is a test caption.", "\n", + "This is a new line.") } } diff --git a/tests/testthat/test-VizEquiMap.R b/tests/testthat/test-VizEquiMap.R index 8ae3f21f856b32dfbddc3e0954e1b2a0cc45e23d..b3e366c66b64e1dc82750dc4d26c0e3a78f29466 100644 --- a/tests/testthat/test-VizEquiMap.R +++ b/tests/testthat/test-VizEquiMap.R @@ -58,6 +58,12 @@ test_that("1. Input checks", { "Parameter 'toptitle' must be a character string." ) + # Check caption + expect_error( + VizEquiMap(var = data1, lon = lons1, lat = lats1, caption = 1), + "Parameter 'caption' must be a character string." + ) + # Check include_lower_boundary and include_upper_boundary expect_error( VizEquiMap(var = data1, lon = lons1, lat = lats1, include_lower_boundary = 1), @@ -68,6 +74,11 @@ test_that("1. Input checks", { "Parameter 'include_upper_boundary' must be a logical element." ) + expect_error( + VizEquiMap(var = data1, lon = lons1, lat = lats1, vertical = "test"), + "Parameter 'vertical' must be TRUE or FALSE." + ) + # Check colNA expect_error( VizEquiMap(var = data1, lon = lons1, lat = lats1, colNA = "test"), @@ -268,6 +279,12 @@ test_that("1. Input checks", { "Parameter 'title_scale' must be numeric." ) + # Check caption_size + expect_error( + VizEquiMap(var = data1, lon = lons1, lat = lats1, caption_size = "test"), + "Parameter 'caption_size' must be numeric." + ) + # Check axes_tick_scale expect_error( VizEquiMap(var = data1, lon = lons1, lat = lats1, axes_tick_scale = "test"),