From ef4191f926500be68d2723568146eaf80f545549 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 2 Sep 2025 13:42:12 +0200 Subject: [PATCH 01/26] initial draft of `vad` function --- NAMESPACE | 2 + R/vad.R | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++ man/vad.Rd | 65 ++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 R/vad.R create mode 100644 man/vad.Rd diff --git a/NAMESPACE b/NAMESPACE index 17b52b1b9..69c6803d8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -86,6 +86,7 @@ S3method(summary,scan) S3method(summary,vp) S3method(summary,vpi) S3method(summary,vpts) +S3method(vad,pvol) export("rcs<-") export("sd_vvp_threshold<-") export(apply_mistnet) @@ -151,6 +152,7 @@ export(select_vpfiles) export(sunrise) export(sunset) export(update_docker) +export(vad) export(vol2bird_version) export(write_pvolfile) importFrom(dplyr,"%>%") diff --git a/R/vad.R b/R/vad.R new file mode 100644 index 000000000..332a9c39f --- /dev/null +++ b/R/vad.R @@ -0,0 +1,142 @@ +#' Create a velocity azimuth display (VAD) plot +#' +#' @param x A polar volume from which range gates are extracted. +#' @param ... Currently not used. +#' @param vp A vertical profile to annotate the VAD plot with the fit in the vertical profile +#' @param range The distance range in to filter the range gates with. +#' If a `vp` is provided the range is taken from there and the argument `range` should not be provided. +#' @param height The height range to filter the range gates by. +#' If only one value is provided next to a `vp` then the height bin intersection with this elevation is plotted. +#' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a `DBZH` less then 20 and a `RHOHV` less then 0.95. +#' Alternative filters could be used to highlight specific effects. +#' @param point_geom The geom to visualize the range gates, the default is [geom_points()], in some cases this suffers from over plotting. +#' Alternatives that avoid over plotting could be [geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. +#' @param point_geom_args Additional arguments to the `point_geom` function. For example controling the point size or alpha. +#' @param annotate A [glue()] string that is used to annotate the plot with additional properties of the height bin of the vp. Use `NULL` if no annotation is desired. +#' +#' @export +#' @examples +#' pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") +#' example_pvol <- read_pvolfile(pvolfile) +#' vp <- calculate_vp(pvolfile) +#' vad(example_pvol, +#' range = c(5000, 30000), height = c(200, 400) +#' ) +#' vad(example_pvol, +#' vp = vp, +#' height = 400, +#' point_geom_args=list(ggplot2::aes(color=ZDR)) +#' ) +ggplot2::scale_color_gradient2() +#' vad(example_pvol, +#' vp = vp, height=c(400,1200), +#' point_geom=ggplot2::geom_bin2d, +#' annotate="sdvvp: {round(sd_vvp,2)} [m/s]", +#' ) + ggplot2::scale_fill_viridis_c() +vad <- function(x, ...) { + UseMethod("vad", x) +} +#' @rdname vad +#' @export +vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, + range_gate_filter = DBZH < 20 & RHOHV < .95, + #point_mapping = aes(), + point_geom=ggplot2::geom_point, + point_geom_args=list(), + annotate="{round(ff,1)} m/s, {round(dd)}\u00B0"){ + assertthat::assert_that(is.pvol(x)) + if (!is.null(vp)) { + vp_df <- as.data.frame(vp) |> dplyr::mutate( + heightBin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), + heightBin = factor(heightBin, levels = heightBin) + ) + + assertthat::assert_that( + is.null(range), + msg = "When specifying a vp the range is taken from there. Thus no range should be provided" + ) + range <- c(vp$attributes$how$minrange, vp$attributes$how$maxrange) * 1000 + if (length(height) == 1) { + height <- max(vp$data$height[vp$data$height <= height]) + c(0, vp$attributes$where$interval) + } + + if (!is.null(height)) { + # if a height profile has been provided we only plot those curves from a vp + vp_df <- vp_df[(vp_df$height + vp$attributes$where$interval) > min(height) & vp_df$height < max(height), ] + } + } + assertthat::assert_that( + is.null(range) || + (is.numeric(range) && length(2)) + ) + assertthat::assert_that( + is.null(height) || + (is.numeric(height) && length(2)) || + (!is.null(vp) && is.numeric(height)) + ) + if (is.null(range)) { + range <- c(-Inf, Inf) + } + if (is.null(height)) { + height <- c(-Inf, Inf) + } + data <- + mapply(SIMPLIFY = F, + cbind, + lapply( lapply(x$scans, scan_to_spatial), + as.data.frame + ), + split(attribute_table(x) |> + dplyr::mutate(scanNr=1:dplyr::n())|> + dplyr::select(-param), 1:length(x$scans)), MoreArgs = list(row.names = NULL) + ) |> + dplyr::bind_rows() |> + dplyr::filter( + !is.na(azim), !is.na(VRADH), + .data$range > min(!!range), .data$range < max(!!range), + .data$height < max(!!height), .data$height > min(!!height), + !!rlang::enexpr(range_gate_filter) + ) + if (!is.null(vp)) { + s <- !(is.na(vp_df$ff) | is.na(vp_df$dd)) + vp_geom <- purrr::pmap( + list(vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$heightBin[s]), + ~ ggplot2::geom_function( + data = dplyr::bind_cols(data, data.frame( + minBinHeight = ..3, + maxBinHeight = ..3 + vp$attributes$where$interval, + heightBin = ..4 + )), + fun = function(x, v, a) cos((x - a) / 180 * pi) * v, + args = list(a = ..2, v = ..1), + color = "red" + ) + ) + if(length(vp_geom)>1){ + vp_geom<-c(vp_geom, ggplot2::facet_wrap(~heightBin)) + } + int <- findInterval(data$height, c(vp_df$height, max(vp_df$height) + vp$attributes$where$interval)) + int[int == 0] <- NA + data$heightBin <- vp_df$heightBin[int] + } else { + vp_geom <- list() + } + if(!is.null(annotate) & is.vp(vp)){ + df<-data.frame(label=as.character(glue::glue_data(annotate, .x=vp_df)),x=Inf,y=Inf, heightBin=vp_df$heightBin) + annotate_geom<- + list( ggplot2::geom_label( + data=df,# parse=T, + ggplot2::aes(x=x,y=y, label = label), vjust = "inward", hjust = "inward", color="red",fill=NA, label.size = 0 + )) + + }else{ + annotate_geom<-list() + } + plt <- ggplot2::ggplot(data, ggplot2::aes(x = azim, y = VRADH)) + + do.call(point_geom, point_geom_args) + + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + + ggplot2::ylab("Radial velocity [m/s]") + + ggplot2::xlab("Azimuth [\u00B0]")+ + vp_geom+annotate_geom + + return(plt) +} diff --git a/man/vad.Rd b/man/vad.Rd new file mode 100644 index 000000000..68519edb8 --- /dev/null +++ b/man/vad.Rd @@ -0,0 +1,65 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/vad.R +\name{vad} +\alias{vad} +\alias{vad.pvol} +\title{Create a velocity azimuth display (VAD) plot} +\usage{ +vad(x, ...) + +\method{vad}{pvol}( + x, + vp = NULL, + ..., + range = NULL, + height = NULL, + range_gate_filter = DBZH < 20 & RHOHV < 0.95, + point_geom = ggplot2::geom_point, + point_geom_args = list(), + annotate = "{round(ff,1)} m/s, {round(dd)}°" +) +} +\arguments{ +\item{x}{A polar volume from which range gates are extracted.} + +\item{...}{Currently not used.} + +\item{vp}{A vertical profile to annotate the VAD plot with the fit in the vertical profile} + +\item{range}{The distance range in to filter the range gates with. +If a \code{vp} is provided the range is taken from there and the argument \code{range} should not be provided.} + +\item{height}{The height range to filter the range gates by. +If only one value is provided next to a \code{vp} then the height bin intersection with this elevation is plotted.} + +\item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a \code{DBZH} less then 20 and a \code{RHOHV} less then 0.95. +Alternative filters could be used to highlight specific effects.} + +\item{point_geom}{The geom to visualize the range gates, the default is \code{\link[=geom_points]{geom_points()}}, in some cases this suffers from over plotting. +Alternatives that avoid over plotting could be \code{\link[=geom_bin2d]{geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} + +\item{point_geom_args}{Additional arguments to the \code{point_geom} function. For example controling the point size or alpha.} + +\item{annotate}{A \code{\link[=glue]{glue()}} string that is used to annotate the plot with additional properties of the height bin of the vp. Use \code{NULL} if no annotation is desired.} +} +\description{ +Create a velocity azimuth display (VAD) plot +} +\examples{ +pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") +example_pvol <- read_pvolfile(pvolfile) +vp <- calculate_vp(pvolfile) +vad(example_pvol, + range = c(5000, 30000), height = c(200, 400) +) +vad(example_pvol, + vp = vp, + height = 400, + point_geom_args=list(ggplot2::aes(color=ZDR)) +) +ggplot2::scale_color_gradient2() +vad(example_pvol, + vp = vp, height=c(400,1200), + point_geom=ggplot2::geom_bin2d, + annotate="sdvvp: {round(sd_vvp,2)} [m/s]", +) + ggplot2::scale_fill_viridis_c() +} From 530733b5981ac7c907d921ec9aa159e9882a4ab5 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 2 Sep 2025 14:53:03 +0200 Subject: [PATCH 02/26] Fix documentation and enable some control for visual elements --- NEWS.md | 2 ++ R/vad.R | 35 ++++++++++++++++++++++++++--------- _pkgdown.yml | 2 ++ man/vad.Rd | 29 +++++++++++++++++++++++------ 4 files changed, 53 insertions(+), 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index 95e66f1ef..f8e6eee27 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # bioRad 0.11.0.9000 (development version) +* Add the `vad()` function to create a velocity azimuth display plots. + # bioRad 0.11.0 ## New features diff --git a/R/vad.R b/R/vad.R index 332a9c39f..8ebe0133c 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,5 +1,9 @@ #' Create a velocity azimuth display (VAD) plot #' +#' A velocity azimuth display plot visualized the radial velocity (`VRAD`) as a function of the azimuth from the radar. +#' Among others it can be used to asses the fit of the movement speeds in a vertical profile. +#' For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as deviations from the sine function. +#' #' @param x A polar volume from which range gates are extracted. #' @param ... Currently not used. #' @param vp A vertical profile to annotate the VAD plot with the fit in the vertical profile @@ -11,22 +15,32 @@ #' Alternative filters could be used to highlight specific effects. #' @param point_geom The geom to visualize the range gates, the default is [geom_points()], in some cases this suffers from over plotting. #' Alternatives that avoid over plotting could be [geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. -#' @param point_geom_args Additional arguments to the `point_geom` function. For example controling the point size or alpha. -#' @param annotate A [glue()] string that is used to annotate the plot with additional properties of the height bin of the vp. Use `NULL` if no annotation is desired. +#' @param point_geom_args Additional arguments to the `point_geom` function. For example, controlling the point size or alpha. +#' @param annotate A [glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. +#' The string is evaluated using the columns from `as.data.frame(vp)`. +#' Use `NULL` if no annotation is desired. +#' @param vp_color The color used for the `vp` annotations and line. +#' @param annotation_size The text size used for the annotation. +#' +#' @returns A [ggplot] object. Additional elements can be added using regular function in the `ggplot2` package. #' #' @export #' @examples #' pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") #' example_pvol <- read_pvolfile(pvolfile) -#' vp <- calculate_vp(pvolfile) +#' # VAD plots can be created for polar volumes alone #' vad(example_pvol, #' range = c(5000, 30000), height = c(200, 400) #' ) +#' # It is also possible to plot one height or more height bins from a `vp`. +#' # Many visual aspects can be controlled through the function arguments +#' # or through adding `ggplot` +#' vp <- calculate_vp(pvolfile) #' vad(example_pvol, #' vp = vp, #' height = 400, #' point_geom_args=list(ggplot2::aes(color=ZDR)) -#' ) +ggplot2::scale_color_gradient2() +#' ) + ggplot2::scale_color_gradient2() #' vad(example_pvol, #' vp = vp, height=c(400,1200), #' point_geom=ggplot2::geom_bin2d, @@ -39,10 +53,11 @@ vad <- function(x, ...) { #' @export vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, range_gate_filter = DBZH < 20 & RHOHV < .95, - #point_mapping = aes(), point_geom=ggplot2::geom_point, point_geom_args=list(), - annotate="{round(ff,1)} m/s, {round(dd)}\u00B0"){ + annotate="{round(ff,1)} m/s, {round(dd)}\u00B0", + annotation_size=4, + vp_color="red"){ assertthat::assert_that(is.pvol(x)) if (!is.null(vp)) { vp_df <- as.data.frame(vp) |> dplyr::mutate( @@ -108,7 +123,7 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, )), fun = function(x, v, a) cos((x - a) / 180 * pi) * v, args = list(a = ..2, v = ..1), - color = "red" + color = vp_color ) ) if(length(vp_geom)>1){ @@ -124,8 +139,10 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, df<-data.frame(label=as.character(glue::glue_data(annotate, .x=vp_df)),x=Inf,y=Inf, heightBin=vp_df$heightBin) annotate_geom<- list( ggplot2::geom_label( - data=df,# parse=T, - ggplot2::aes(x=x,y=y, label = label), vjust = "inward", hjust = "inward", color="red",fill=NA, label.size = 0 + data=df, + ggplot2::aes(x=x,y=y, label = label), + vjust = "inward", hjust = "inward", color=vp_color, + fill=NA, label.size = 0, size=annotation_size )) }else{ diff --git a/_pkgdown.yml b/_pkgdown.yml index 0c75eb60e..2b0a33464 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -56,6 +56,7 @@ reference: - download_basemap - composite_ppi - "`[.ppi`" + - vad - title: "Creating vertical profiles of biological targets" desc: "Functions to process weather radar data (pvol) into vertical profiles (vp) of biological targets." contents: @@ -74,6 +75,7 @@ reference: - plot.vp - as.data.frame.vp - list_vpts_aloft + - vad - title: "Manipulating vertical profile data" desc: "Functions to combine vertical profiles (vp) into time series (vpts) and to post-process, read, inspect and plot these." contents: diff --git a/man/vad.Rd b/man/vad.Rd index 68519edb8..a3ced23ff 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -16,7 +16,9 @@ vad(x, ...) range_gate_filter = DBZH < 20 & RHOHV < 0.95, point_geom = ggplot2::geom_point, point_geom_args = list(), - annotate = "{round(ff,1)} m/s, {round(dd)}°" + annotate = "{round(ff,1)} m/s, {round(dd)}°", + annotation_size = 4, + vp_color = "red" ) } \arguments{ @@ -38,25 +40,40 @@ Alternative filters could be used to highlight specific effects.} \item{point_geom}{The geom to visualize the range gates, the default is \code{\link[=geom_points]{geom_points()}}, in some cases this suffers from over plotting. Alternatives that avoid over plotting could be \code{\link[=geom_bin2d]{geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} -\item{point_geom_args}{Additional arguments to the \code{point_geom} function. For example controling the point size or alpha.} +\item{point_geom_args}{Additional arguments to the \code{point_geom} function. For example, controlling the point size or alpha.} -\item{annotate}{A \code{\link[=glue]{glue()}} string that is used to annotate the plot with additional properties of the height bin of the vp. Use \code{NULL} if no annotation is desired.} +\item{annotate}{A \code{\link[=glue]{glue()}} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. +The string is evaluated using the columns from \code{as.data.frame(vp)}. +Use \code{NULL} if no annotation is desired.} + +\item{annotation_size}{The text size used for the annotation.} + +\item{vp_color}{The color used for the \code{vp} annotations and line.} +} +\value{ +A \link{ggplot} object. Additional elements can be added using regular function in the \code{ggplot2} package. } \description{ -Create a velocity azimuth display (VAD) plot +A velocity azimuth display plot visualized the radial velocity (\code{VRAD}) as a function of the azimuth from the radar. +Among others it can be used to asses the fit of the movement speeds in a vertical profile. +For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as deviations from the sine function. } \examples{ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") example_pvol <- read_pvolfile(pvolfile) -vp <- calculate_vp(pvolfile) +# VAD plots can be created for polar volumes alone vad(example_pvol, range = c(5000, 30000), height = c(200, 400) ) +# It is also possible to plot one height or more height bins from a `vp`. +# Many visual aspects can be controlled through the function arguments +# or through adding `ggplot` +vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, height = 400, point_geom_args=list(ggplot2::aes(color=ZDR)) -) +ggplot2::scale_color_gradient2() +) + ggplot2::scale_color_gradient2() vad(example_pvol, vp = vp, height=c(400,1200), point_geom=ggplot2::geom_bin2d, From 8101251abf04ac79d1c767fa23efb86de9d984f4 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 2 Sep 2025 22:03:01 +0200 Subject: [PATCH 03/26] fix cran checks --- R/vad.R | 62 +++++++++++++++++++++++++++++++++++------------------- man/vad.Rd | 8 +++---- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/R/vad.R b/R/vad.R index 8ebe0133c..6823f1ca9 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,3 +1,6 @@ +globalVariables(c("DBZH","VRADH","RHOHV")) +NULL + #' Create a velocity azimuth display (VAD) plot #' #' A velocity azimuth display plot visualized the radial velocity (`VRAD`) as a function of the azimuth from the radar. @@ -13,16 +16,16 @@ #' If only one value is provided next to a `vp` then the height bin intersection with this elevation is plotted. #' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a `DBZH` less then 20 and a `RHOHV` less then 0.95. #' Alternative filters could be used to highlight specific effects. -#' @param point_geom The geom to visualize the range gates, the default is [geom_points()], in some cases this suffers from over plotting. -#' Alternatives that avoid over plotting could be [geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. +#' @param point_geom The geom to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. +#' Alternatives that avoid over plotting could be [ggplot2::geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. #' @param point_geom_args Additional arguments to the `point_geom` function. For example, controlling the point size or alpha. -#' @param annotate A [glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. +#' @param annotate A [glue::glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. #' The string is evaluated using the columns from `as.data.frame(vp)`. #' Use `NULL` if no annotation is desired. #' @param vp_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. #' -#' @returns A [ggplot] object. Additional elements can be added using regular function in the `ggplot2` package. +#' @returns A [ggplot2::ggplot] object. Additional elements can be added using regular function in the `ggplot2` package. #' #' @export #' @examples @@ -61,8 +64,8 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, assertthat::assert_that(is.pvol(x)) if (!is.null(vp)) { vp_df <- as.data.frame(vp) |> dplyr::mutate( - heightBin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), - heightBin = factor(heightBin, levels = heightBin) + height_bin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), + height_bin = factor(.data$height_bin, levels = .data$height_bin) ) assertthat::assert_that( @@ -101,46 +104,61 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, as.data.frame ), split(attribute_table(x) |> - dplyr::mutate(scanNr=1:dplyr::n())|> - dplyr::select(-param), 1:length(x$scans)), MoreArgs = list(row.names = NULL) + dplyr::mutate(scan_nr=1:dplyr::n())|> + dplyr::select(-"param"), 1:length(x$scans)), MoreArgs = list(row.names = NULL) ) |> dplyr::bind_rows() |> dplyr::filter( - !is.na(azim), !is.na(VRADH), + !is.na(.data$azim), !is.na(VRADH), .data$range > min(!!range), .data$range < max(!!range), .data$height < max(!!height), .data$height > min(!!height), !!rlang::enexpr(range_gate_filter) ) if (!is.null(vp)) { s <- !(is.na(vp_df$ff) | is.na(vp_df$dd)) - vp_geom <- purrr::pmap( - list(vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$heightBin[s]), - ~ ggplot2::geom_function( + # vp_geom <- purrr::pmap( + # list(vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s]), + # ~ ggplot2::geom_function( + # data = dplyr::bind_cols(data, data.frame( + # min_bin_height = ..3, + # max_bin_height = ..3 + vp$attributes$where$interval, + # height_bin = ..4 + # )), + # fun = function(x, v, a) cos((x - a) / 180 * pi) * v, + # args = list(a = ..2, v = ..1), + # color = vp_color + # ) + # ) + vp_geom <- mapply(SIMPLIFY = F, + function(spd, dir,hgt, bin) + {ggplot2::geom_function( data = dplyr::bind_cols(data, data.frame( - minBinHeight = ..3, - maxBinHeight = ..3 + vp$attributes$where$interval, - heightBin = ..4 + min_bin_height = hgt, + max_bin_height = hgt + vp$attributes$where$interval, + height_bin = bin )), fun = function(x, v, a) cos((x - a) / 180 * pi) * v, - args = list(a = ..2, v = ..1), + args = list(a = dir, v = spd), color = vp_color - ) + )}, + + vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s] ) if(length(vp_geom)>1){ - vp_geom<-c(vp_geom, ggplot2::facet_wrap(~heightBin)) + vp_geom<-c(vp_geom, ggplot2::facet_wrap(~.data$height_bin)) } int <- findInterval(data$height, c(vp_df$height, max(vp_df$height) + vp$attributes$where$interval)) int[int == 0] <- NA - data$heightBin <- vp_df$heightBin[int] + data$height_bin <- vp_df$height_bin[int] } else { vp_geom <- list() } if(!is.null(annotate) & is.vp(vp)){ - df<-data.frame(label=as.character(glue::glue_data(annotate, .x=vp_df)),x=Inf,y=Inf, heightBin=vp_df$heightBin) + df<-data.frame(label=as.character(glue::glue_data(annotate, .x=vp_df)),x=Inf,y=Inf, height_bin=vp_df$height_bin) annotate_geom<- list( ggplot2::geom_label( data=df, - ggplot2::aes(x=x,y=y, label = label), + ggplot2::aes(x=x,y=y, label = .data$label), vjust = "inward", hjust = "inward", color=vp_color, fill=NA, label.size = 0, size=annotation_size )) @@ -148,7 +166,7 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, }else{ annotate_geom<-list() } - plt <- ggplot2::ggplot(data, ggplot2::aes(x = azim, y = VRADH)) + + plt <- ggplot2::ggplot(data, ggplot2::aes(x = .data$azim, y = VRADH)) + do.call(point_geom, point_geom_args) + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + ggplot2::ylab("Radial velocity [m/s]") + diff --git a/man/vad.Rd b/man/vad.Rd index a3ced23ff..de0ec699c 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -37,12 +37,12 @@ If only one value is provided next to a \code{vp} then the height bin intersecti \item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a \code{DBZH} less then 20 and a \code{RHOHV} less then 0.95. Alternative filters could be used to highlight specific effects.} -\item{point_geom}{The geom to visualize the range gates, the default is \code{\link[=geom_points]{geom_points()}}, in some cases this suffers from over plotting. -Alternatives that avoid over plotting could be \code{\link[=geom_bin2d]{geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} +\item{point_geom}{The geom to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}, in some cases this suffers from over plotting. +Alternatives that avoid over plotting could be \code{\link[ggplot2:geom_bin_2d]{ggplot2::geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} \item{point_geom_args}{Additional arguments to the \code{point_geom} function. For example, controlling the point size or alpha.} -\item{annotate}{A \code{\link[=glue]{glue()}} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. +\item{annotate}{A \code{\link[glue:glue]{glue::glue()}} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. The string is evaluated using the columns from \code{as.data.frame(vp)}. Use \code{NULL} if no annotation is desired.} @@ -51,7 +51,7 @@ Use \code{NULL} if no annotation is desired.} \item{vp_color}{The color used for the \code{vp} annotations and line.} } \value{ -A \link{ggplot} object. Additional elements can be added using regular function in the \code{ggplot2} package. +A \link[ggplot2:ggplot]{ggplot2::ggplot} object. Additional elements can be added using regular function in the \code{ggplot2} package. } \description{ A velocity azimuth display plot visualized the radial velocity (\code{VRAD}) as a function of the azimuth from the radar. From 2c9ac329fa41cdd428e473361506f6f8eb6c20ac Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 15 Sep 2025 14:27:57 +0200 Subject: [PATCH 04/26] include suggestions to documentation and rename argument to `plotting_geom` --- R/vad.R | 28 +++++++++++++++++----------- man/vad.Rd | 27 +++++++++++++++++---------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/R/vad.R b/R/vad.R index 6823f1ca9..babd00112 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,11 +1,12 @@ globalVariables(c("DBZH","VRADH","RHOHV")) NULL -#' Create a velocity azimuth display (VAD) plot +#' Create a Velocity Azimuth Display (VAD) plot #' #' A velocity azimuth display plot visualized the radial velocity (`VRAD`) as a function of the azimuth from the radar. #' Among others it can be used to asses the fit of the movement speeds in a vertical profile. -#' For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as deviations from the sine function. +#' For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as +#' deviations from the sine function. #' #' @param x A polar volume from which range gates are extracted. #' @param ... Currently not used. @@ -16,16 +17,21 @@ NULL #' If only one value is provided next to a `vp` then the height bin intersection with this elevation is plotted. #' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a `DBZH` less then 20 and a `RHOHV` less then 0.95. #' Alternative filters could be used to highlight specific effects. -#' @param point_geom The geom to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. +#' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. #' Alternatives that avoid over plotting could be [ggplot2::geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. -#' @param point_geom_args Additional arguments to the `point_geom` function. For example, controlling the point size or alpha. -#' @param annotate A [glue::glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. +#' @param plotting_geom_args A list with additional arguments to the `plotting_geom` function. For example, controlling the point size or alpha. +#' @param annotate A [glue][glue::glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. #' The string is evaluated using the columns from `as.data.frame(vp)`. #' Use `NULL` if no annotation is desired. #' @param vp_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. #' -#' @returns A [ggplot2::ggplot] object. Additional elements can be added using regular function in the `ggplot2` package. +#' @returns A [ggplot2::ggplot] object. +#' +#' @details As a [ggplot2::ggplot] object us returned additional elements can be added using regular function in +#' the `ggplot2` package. Labels could, for example, be modified using [ggplot2::labs()] or [ggplot2::ggtitle()]. +#' Using [ggplot2::theme()] the visual appearance can easily be modified. To do this the regular [+][ggplot2::+.gg] +#' syntax can be used. In the examples this is demonstrated using scale. #' #' @export #' @examples @@ -42,11 +48,11 @@ NULL #' vad(example_pvol, #' vp = vp, #' height = 400, -#' point_geom_args=list(ggplot2::aes(color=ZDR)) +#' plotting_geom_args=list(ggplot2::aes(color=ZDR)) #' ) + ggplot2::scale_color_gradient2() #' vad(example_pvol, #' vp = vp, height=c(400,1200), -#' point_geom=ggplot2::geom_bin2d, +#' plotting_geom=ggplot2::geom_bin2d, #' annotate="sdvvp: {round(sd_vvp,2)} [m/s]", #' ) + ggplot2::scale_fill_viridis_c() vad <- function(x, ...) { @@ -56,8 +62,8 @@ vad <- function(x, ...) { #' @export vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, range_gate_filter = DBZH < 20 & RHOHV < .95, - point_geom=ggplot2::geom_point, - point_geom_args=list(), + plotting_geom=ggplot2::geom_point, + plotting_geom_args=list(), annotate="{round(ff,1)} m/s, {round(dd)}\u00B0", annotation_size=4, vp_color="red"){ @@ -167,7 +173,7 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, annotate_geom<-list() } plt <- ggplot2::ggplot(data, ggplot2::aes(x = .data$azim, y = VRADH)) + - do.call(point_geom, point_geom_args) + + do.call(plotting_geom, plotting_geom_args) + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + ggplot2::ylab("Radial velocity [m/s]") + ggplot2::xlab("Azimuth [\u00B0]")+ diff --git a/man/vad.Rd b/man/vad.Rd index de0ec699c..41eecee30 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -3,7 +3,7 @@ \name{vad} \alias{vad} \alias{vad.pvol} -\title{Create a velocity azimuth display (VAD) plot} +\title{Create a Velocity Azimuth Display (VAD) plot} \usage{ vad(x, ...) @@ -14,8 +14,8 @@ vad(x, ...) range = NULL, height = NULL, range_gate_filter = DBZH < 20 & RHOHV < 0.95, - point_geom = ggplot2::geom_point, - point_geom_args = list(), + plotting_geom = ggplot2::geom_point, + plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}°", annotation_size = 4, vp_color = "red" @@ -37,12 +37,12 @@ If only one value is provided next to a \code{vp} then the height bin intersecti \item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a \code{DBZH} less then 20 and a \code{RHOHV} less then 0.95. Alternative filters could be used to highlight specific effects.} -\item{point_geom}{The geom to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}, in some cases this suffers from over plotting. +\item{plotting_geom}{The geom function to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}, in some cases this suffers from over plotting. Alternatives that avoid over plotting could be \code{\link[ggplot2:geom_bin_2d]{ggplot2::geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} -\item{point_geom_args}{Additional arguments to the \code{point_geom} function. For example, controlling the point size or alpha.} +\item{plotting_geom_args}{A list with additional arguments to the \code{plotting_geom} function. For example, controlling the point size or alpha.} -\item{annotate}{A \code{\link[glue:glue]{glue::glue()}} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. +\item{annotate}{A \link[glue:glue]{glue} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. The string is evaluated using the columns from \code{as.data.frame(vp)}. Use \code{NULL} if no annotation is desired.} @@ -51,12 +51,19 @@ Use \code{NULL} if no annotation is desired.} \item{vp_color}{The color used for the \code{vp} annotations and line.} } \value{ -A \link[ggplot2:ggplot]{ggplot2::ggplot} object. Additional elements can be added using regular function in the \code{ggplot2} package. +A \link[ggplot2:ggplot]{ggplot2::ggplot} object. } \description{ A velocity azimuth display plot visualized the radial velocity (\code{VRAD}) as a function of the azimuth from the radar. Among others it can be used to asses the fit of the movement speeds in a vertical profile. -For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as deviations from the sine function. +For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as +deviations from the sine function. +} +\details{ +As a \link[ggplot2:ggplot]{ggplot2::ggplot} object us returned additional elements can be added using regular function in +the \code{ggplot2} package. Labels could, for example, be modified using \code{\link[ggplot2:labs]{ggplot2::labs()}} or \code{\link[ggplot2:labs]{ggplot2::ggtitle()}}. +Using \code{\link[ggplot2:theme]{ggplot2::theme()}} the visual appearance can easily be modified. To do this the regular \link[ggplot2:gg-add]{+} +syntax can be used. In the examples this is demonstrated using scale. } \examples{ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") @@ -72,11 +79,11 @@ vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, height = 400, - point_geom_args=list(ggplot2::aes(color=ZDR)) + plotting_geom_args=list(ggplot2::aes(color=ZDR)) ) + ggplot2::scale_color_gradient2() vad(example_pvol, vp = vp, height=c(400,1200), - point_geom=ggplot2::geom_bin2d, + plotting_geom=ggplot2::geom_bin2d, annotate="sdvvp: {round(sd_vvp,2)} [m/s]", ) + ggplot2::scale_fill_viridis_c() } From 62206b1e348cf1546d39c1024360bb849bfd9b63 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 08:41:31 +0200 Subject: [PATCH 05/26] remove commented code --- R/vad.R | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/R/vad.R b/R/vad.R index babd00112..d34704061 100644 --- a/R/vad.R +++ b/R/vad.R @@ -122,19 +122,6 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, ) if (!is.null(vp)) { s <- !(is.na(vp_df$ff) | is.na(vp_df$dd)) - # vp_geom <- purrr::pmap( - # list(vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s]), - # ~ ggplot2::geom_function( - # data = dplyr::bind_cols(data, data.frame( - # min_bin_height = ..3, - # max_bin_height = ..3 + vp$attributes$where$interval, - # height_bin = ..4 - # )), - # fun = function(x, v, a) cos((x - a) / 180 * pi) * v, - # args = list(a = ..2, v = ..1), - # color = vp_color - # ) - # ) vp_geom <- mapply(SIMPLIFY = F, function(spd, dir,hgt, bin) {ggplot2::geom_function( From bd06c4b99a447d1e292331ae7b54b5d181c6d5e6 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 10:58:39 +0200 Subject: [PATCH 06/26] add comments to orient in function --- R/vad.R | 123 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/R/vad.R b/R/vad.R index d34704061..21e3e65c8 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,4 +1,4 @@ -globalVariables(c("DBZH","VRADH","RHOHV")) +globalVariables(c("DBZH", "VRADH", "RHOHV")) NULL #' Create a Velocity Azimuth Display (VAD) plot @@ -48,46 +48,50 @@ NULL #' vad(example_pvol, #' vp = vp, #' height = 400, -#' plotting_geom_args=list(ggplot2::aes(color=ZDR)) +#' plotting_geom_args = list(ggplot2::aes(color = ZDR)) #' ) + ggplot2::scale_color_gradient2() #' vad(example_pvol, -#' vp = vp, height=c(400,1200), -#' plotting_geom=ggplot2::geom_bin2d, -#' annotate="sdvvp: {round(sd_vvp,2)} [m/s]", +#' vp = vp, height = c(400, 1200), +#' plotting_geom = ggplot2::geom_bin2d, +#' annotate = "sdvvp: {round(sd_vvp,2)} [m/s]", #' ) + ggplot2::scale_fill_viridis_c() vad <- function(x, ...) { UseMethod("vad", x) } #' @rdname vad #' @export -vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, +vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, range_gate_filter = DBZH < 20 & RHOHV < .95, - plotting_geom=ggplot2::geom_point, - plotting_geom_args=list(), - annotate="{round(ff,1)} m/s, {round(dd)}\u00B0", - annotation_size=4, - vp_color="red"){ + plotting_geom = ggplot2::geom_point, + plotting_geom_args = list(), + annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", + annotation_size = 4, + vp_color = "red") { assertthat::assert_that(is.pvol(x)) + # Some of the input variables are checked and modified speficially in the VP context if (!is.null(vp)) { - vp_df <- as.data.frame(vp) |> dplyr::mutate( - height_bin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), - height_bin = factor(.data$height_bin, levels = .data$height_bin) - ) - + assertthat::assert_that(is.vp(x)) + # For vp's we take range from the vp and not the arguments assertthat::assert_that( is.null(range), - msg = "When specifying a vp the range is taken from there. Thus no range should be provided" + msg = "When specifying a vp the range is taken from there. Thus no range should be provided." ) range <- c(vp$attributes$how$minrange, vp$attributes$how$maxrange) * 1000 + # convert the vp to df with height as we will need it later + vp_df <- as.data.frame(vp) |> dplyr::mutate( + height_bin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), + height_bin = factor(.data$height_bin, levels = .data$height_bin) + ) + # For one height we take the interval that intersects if (length(height) == 1) { height <- max(vp$data$height[vp$data$height <= height]) + c(0, vp$attributes$where$interval) } - + # if a height profile has been provided we only plot those curves from a vp and thus subset `vp_df` if (!is.null(height)) { - # if a height profile has been provided we only plot those curves from a vp vp_df <- vp_df[(vp_df$height + vp$attributes$where$interval) > min(height) & vp_df$height < max(height), ] } } + # Checking of input variables assertthat::assert_that( is.null(range) || (is.numeric(range) && length(2)) @@ -103,68 +107,79 @@ vad.pvol <- function(x, vp = NULL,..., range = NULL, height = NULL, if (is.null(height)) { height <- c(-Inf, Inf) } + # Convert the polar volume to plotting data by converting the scans to locations data <- - mapply(SIMPLIFY = F, + mapply( + SIMPLIFY = F, cbind, - lapply( lapply(x$scans, scan_to_spatial), - as.data.frame + lapply( + lapply(x$scans, scan_to_spatial), + as.data.frame ), + # We add extra attributes from the scan attributes that can be useful for filtering or highlighting + # certain attributes split(attribute_table(x) |> - dplyr::mutate(scan_nr=1:dplyr::n())|> + dplyr::mutate(scan_nr = 1:dplyr::n()) |> dplyr::select(-"param"), 1:length(x$scans)), MoreArgs = list(row.names = NULL) ) |> dplyr::bind_rows() |> + # Filter the plotting data with the height and range, furthermore we omit NA's + # ann apply the range_gate_filter's dplyr::filter( !is.na(.data$azim), !is.na(VRADH), .data$range > min(!!range), .data$range < max(!!range), .data$height < max(!!height), .data$height > min(!!height), !!rlang::enexpr(range_gate_filter) ) + # Generate a geom that contains the sine function from the vp + vp_geom <- list() if (!is.null(vp)) { s <- !(is.na(vp_df$ff) | is.na(vp_df$dd)) - vp_geom <- mapply(SIMPLIFY = F, - function(spd, dir,hgt, bin) - {ggplot2::geom_function( - data = dplyr::bind_cols(data, data.frame( - min_bin_height = hgt, - max_bin_height = hgt + vp$attributes$where$interval, - height_bin = bin - )), - fun = function(x, v, a) cos((x - a) / 180 * pi) * v, - args = list(a = dir, v = spd), - color = vp_color - )}, - + vp_geom <- mapply( + SIMPLIFY = F, + function(spd, dir, hgt, bin) { + ggplot2::geom_function( + data = dplyr::bind_cols(data, data.frame( + min_bin_height = hgt, + max_bin_height = hgt + vp$attributes$where$interval, + height_bin = bin + )), + fun = function(x, v, a) cos((x - a) / 180 * pi) * v, + args = list(a = dir, v = spd), + color = vp_color + ) + }, vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s] ) - if(length(vp_geom)>1){ - vp_geom<-c(vp_geom, ggplot2::facet_wrap(~.data$height_bin)) + if (length(vp_geom) > 1) { + vp_geom <- c(vp_geom, ggplot2::facet_wrap(~ .data$height_bin)) } int <- findInterval(data$height, c(vp_df$height, max(vp_df$height) + vp$attributes$where$interval)) int[int == 0] <- NA data$height_bin <- vp_df$height_bin[int] - } else { - vp_geom <- list() } - if(!is.null(annotate) & is.vp(vp)){ - df<-data.frame(label=as.character(glue::glue_data(annotate, .x=vp_df)),x=Inf,y=Inf, height_bin=vp_df$height_bin) - annotate_geom<- - list( ggplot2::geom_label( - data=df, - ggplot2::aes(x=x,y=y, label = .data$label), - vjust = "inward", hjust = "inward", color=vp_color, - fill=NA, label.size = 0, size=annotation_size - )) - - }else{ - annotate_geom<-list() + # Create the `annotate_geom` for textual annotations of each height interval + # The glue string is annotated in the `vp_df` so that all vp attributes are available + annotate_geom <- list() + if (!is.null(annotate) & is.vp(vp)) { + df <- data.frame(label = as.character(glue::glue_data(annotate, .x = vp_df)), + x = Inf, y = Inf, height_bin = vp_df$height_bin) + annotate_geom <- + list(ggplot2::geom_label( + data = df, + ggplot2::aes(x = x, y = y, label = .data$label), + vjust = "inward", hjust = "inward", color = vp_color, + fill = NA, label.size = 0, size = annotation_size + )) } + # Combine everything in one plot plt <- ggplot2::ggplot(data, ggplot2::aes(x = .data$azim, y = VRADH)) + do.call(plotting_geom, plotting_geom_args) + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + ggplot2::ylab("Radial velocity [m/s]") + - ggplot2::xlab("Azimuth [\u00B0]")+ - vp_geom+annotate_geom + ggplot2::xlab("Azimuth [\u00B0]") + + vp_geom + + annotate_geom return(plt) } From 52d1dba5033f6d9d6e410a06c9a69d59db84ca00 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 11:19:13 +0200 Subject: [PATCH 07/26] error in checking --- R/vad.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/vad.R b/R/vad.R index 21e3e65c8..f02eef9d4 100644 --- a/R/vad.R +++ b/R/vad.R @@ -70,7 +70,7 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, assertthat::assert_that(is.pvol(x)) # Some of the input variables are checked and modified speficially in the VP context if (!is.null(vp)) { - assertthat::assert_that(is.vp(x)) + assertthat::assert_that(is.vp(vp)) # For vp's we take range from the vp and not the arguments assertthat::assert_that( is.null(range), From de356a6a06443494a8c61a580dd14510872b63fc Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 11:21:18 +0200 Subject: [PATCH 08/26] rename to annotation_color --- R/vad.R | 8 ++++---- man/vad.Rd | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/R/vad.R b/R/vad.R index f02eef9d4..7ae170347 100644 --- a/R/vad.R +++ b/R/vad.R @@ -23,7 +23,7 @@ NULL #' @param annotate A [glue][glue::glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. #' The string is evaluated using the columns from `as.data.frame(vp)`. #' Use `NULL` if no annotation is desired. -#' @param vp_color The color used for the `vp` annotations and line. +#' @param annotation_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. #' #' @returns A [ggplot2::ggplot] object. @@ -66,7 +66,7 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", annotation_size = 4, - vp_color = "red") { + annotation_color = "red") { assertthat::assert_that(is.pvol(x)) # Some of the input variables are checked and modified speficially in the VP context if (!is.null(vp)) { @@ -146,7 +146,7 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, )), fun = function(x, v, a) cos((x - a) / 180 * pi) * v, args = list(a = dir, v = spd), - color = vp_color + color = annotation_color ) }, vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s] @@ -168,7 +168,7 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, list(ggplot2::geom_label( data = df, ggplot2::aes(x = x, y = y, label = .data$label), - vjust = "inward", hjust = "inward", color = vp_color, + vjust = "inward", hjust = "inward", color = annotation_color, fill = NA, label.size = 0, size = annotation_size )) } diff --git a/man/vad.Rd b/man/vad.Rd index 41eecee30..8690b2c8f 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -18,7 +18,7 @@ vad(x, ...) plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}°", annotation_size = 4, - vp_color = "red" + annotation_color = "red" ) } \arguments{ @@ -48,7 +48,7 @@ Use \code{NULL} if no annotation is desired.} \item{annotation_size}{The text size used for the annotation.} -\item{vp_color}{The color used for the \code{vp} annotations and line.} +\item{annotation_color}{The color used for the \code{vp} annotations and line.} } \value{ A \link[ggplot2:ggplot]{ggplot2::ggplot} object. @@ -79,11 +79,11 @@ vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, height = 400, - plotting_geom_args=list(ggplot2::aes(color=ZDR)) + plotting_geom_args = list(ggplot2::aes(color = ZDR)) ) + ggplot2::scale_color_gradient2() vad(example_pvol, - vp = vp, height=c(400,1200), - plotting_geom=ggplot2::geom_bin2d, - annotate="sdvvp: {round(sd_vvp,2)} [m/s]", + vp = vp, height = c(400, 1200), + plotting_geom = ggplot2::geom_bin2d, + annotate = "sdvvp: {round(sd_vvp,2)} [m/s]", ) + ggplot2::scale_fill_viridis_c() } From 403bc2a58daa53b30f85dc040df5ec9198aed756 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 17:09:49 +0200 Subject: [PATCH 09/26] towards min max alt and range --- R/vad.R | 74 +++++++++++++++++++++++++++++++----------------------- man/vad.Rd | 21 +++++++++------- 2 files changed, 55 insertions(+), 40 deletions(-) diff --git a/R/vad.R b/R/vad.R index 7ae170347..afe9e5385 100644 --- a/R/vad.R +++ b/R/vad.R @@ -11,10 +11,10 @@ NULL #' @param x A polar volume from which range gates are extracted. #' @param ... Currently not used. #' @param vp A vertical profile to annotate the VAD plot with the fit in the vertical profile -#' @param range The distance range in to filter the range gates with. -#' If a `vp` is provided the range is taken from there and the argument `range` should not be provided. -#' @param height The height range to filter the range gates by. -#' If only one value is provided next to a `vp` then the height bin intersection with this elevation is plotted. +#' @param range_min,range_max The distance range in to filter the range gates with. +#' If a `vp` is provided the range is taken from the `vp` and the argument `range_min` and `range_max` should not be provided. +#' @param alt_min,alt_max The altitude range to filter the range gates by. +#' If only `alt_min` value is provided next to a `vp` then the height bin intersection with this altitude is plotted. #' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a `DBZH` less then 20 and a `RHOHV` less then 0.95. #' Alternative filters could be used to highlight specific effects. #' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. @@ -39,7 +39,8 @@ NULL #' example_pvol <- read_pvolfile(pvolfile) #' # VAD plots can be created for polar volumes alone #' vad(example_pvol, -#' range = c(5000, 30000), height = c(200, 400) +#' range_min = 5000, range_max = 30000, +#' alt_min = 200, alt_max = 400 #' ) #' # It is also possible to plot one height or more height bins from a `vp`. #' # Many visual aspects can be controlled through the function arguments @@ -47,11 +48,11 @@ NULL #' vp <- calculate_vp(pvolfile) #' vad(example_pvol, #' vp = vp, -#' height = 400, +#' alt_min = 400, #' plotting_geom_args = list(ggplot2::aes(color = ZDR)) #' ) + ggplot2::scale_color_gradient2() #' vad(example_pvol, -#' vp = vp, height = c(400, 1200), +#' vp = vp, alt_min = 400, alt_max = 1200, #' plotting_geom = ggplot2::geom_bin2d, #' annotate = "sdvvp: {round(sd_vvp,2)} [m/s]", #' ) + ggplot2::scale_fill_viridis_c() @@ -60,7 +61,9 @@ vad <- function(x, ...) { } #' @rdname vad #' @export -vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, +vad.pvol <- function(x, vp = NULL, ..., + range_min = NULL, range_max = NULL, + alt_min = NULL, alt_max = NULL, range_gate_filter = DBZH < 20 & RHOHV < .95, plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), @@ -68,45 +71,52 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, annotation_size = 4, annotation_color = "red") { assertthat::assert_that(is.pvol(x)) - # Some of the input variables are checked and modified speficially in the VP context + # Some of the input variables are checked and modified specifically in the VP context if (!is.null(vp)) { assertthat::assert_that(is.vp(vp)) # For vp's we take range from the vp and not the arguments assertthat::assert_that( - is.null(range), + is.null(range_min) && is.null(range_max), msg = "When specifying a vp the range is taken from there. Thus no range should be provided." ) - range <- c(vp$attributes$how$minrange, vp$attributes$how$maxrange) * 1000 + range_min <- vp$attributes$how$minrange * 1000 + range_max <- vp$attributes$how$maxrange * 1000 # convert the vp to df with height as we will need it later vp_df <- as.data.frame(vp) |> dplyr::mutate( height_bin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), height_bin = factor(.data$height_bin, levels = .data$height_bin) ) # For one height we take the interval that intersects - if (length(height) == 1) { - height <- max(vp$data$height[vp$data$height <= height]) + c(0, vp$attributes$where$interval) + if (is.numeric(alt_min) && rlang::is_scalar_vector(alt_min) && is.null(alt_max)) { + alt_min <- max(vp$data$height[vp$data$height <= alt_min]) + alt_max <- alt_min + vp$attributes$where$interval } # if a height profile has been provided we only plot those curves from a vp and thus subset `vp_df` - if (!is.null(height)) { - vp_df <- vp_df[(vp_df$height + vp$attributes$where$interval) > min(height) & vp_df$height < max(height), ] + if (!is.null(alt_min)) { + vp_df <- vp_df[(vp_df$height + vp$attributes$where$interval) > alt_min, ] + } + if (!is.null(alt_max)) { + vp_df <- vp_df[vp_df$height < alt_max, ] } } # Checking of input variables + range_min <- max(-Inf, range_min) + range_max <- min(Inf, range_max) assertthat::assert_that( - is.null(range) || - (is.numeric(range) && length(2)) + is.numeric(range_min) && rlang::is_scalar_vector(range_min) ) assertthat::assert_that( - is.null(height) || - (is.numeric(height) && length(2)) || - (!is.null(vp) && is.numeric(height)) + is.numeric(range_max) && rlang::is_scalar_vector(range_max) ) - if (is.null(range)) { - range <- c(-Inf, Inf) - } - if (is.null(height)) { - height <- c(-Inf, Inf) - } + alt_min <- max(-Inf, alt_min) + alt_max <- min(Inf, alt_max) + assertthat::assert_that( + is.numeric(alt_min) && rlang::is_scalar_vector(alt_min) + ) + assertthat::assert_that( + is.numeric(alt_max) && rlang::is_scalar_vector(alt_max) + ) + # Convert the polar volume to plotting data by converting the scans to locations data <- mapply( @@ -124,11 +134,11 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, ) |> dplyr::bind_rows() |> # Filter the plotting data with the height and range, furthermore we omit NA's - # ann apply the range_gate_filter's + # and apply the range_gate_filter's dplyr::filter( !is.na(.data$azim), !is.na(VRADH), - .data$range > min(!!range), .data$range < max(!!range), - .data$height < max(!!height), .data$height > min(!!height), + .data$range > range_min, .data$range < range_max, + .data$height < alt_max, .data$height > alt_min, !!rlang::enexpr(range_gate_filter) ) # Generate a geom that contains the sine function from the vp @@ -162,8 +172,10 @@ vad.pvol <- function(x, vp = NULL, ..., range = NULL, height = NULL, # The glue string is annotated in the `vp_df` so that all vp attributes are available annotate_geom <- list() if (!is.null(annotate) & is.vp(vp)) { - df <- data.frame(label = as.character(glue::glue_data(annotate, .x = vp_df)), - x = Inf, y = Inf, height_bin = vp_df$height_bin) + df <- data.frame( + label = as.character(glue::glue_data(annotate, .x = vp_df)), + x = Inf, y = Inf, height_bin = vp_df$height_bin + ) annotate_geom <- list(ggplot2::geom_label( data = df, diff --git a/man/vad.Rd b/man/vad.Rd index 8690b2c8f..42aff24a2 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -11,8 +11,10 @@ vad(x, ...) x, vp = NULL, ..., - range = NULL, - height = NULL, + range_min = NULL, + range_max = NULL, + alt_min = NULL, + alt_max = NULL, range_gate_filter = DBZH < 20 & RHOHV < 0.95, plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), @@ -28,11 +30,11 @@ vad(x, ...) \item{vp}{A vertical profile to annotate the VAD plot with the fit in the vertical profile} -\item{range}{The distance range in to filter the range gates with. -If a \code{vp} is provided the range is taken from there and the argument \code{range} should not be provided.} +\item{range_min, range_max}{The distance range in to filter the range gates with. +If a \code{vp} is provided the range is taken from the \code{vp} and the argument \code{range_min} and \code{range_max} should not be provided.} -\item{height}{The height range to filter the range gates by. -If only one value is provided next to a \code{vp} then the height bin intersection with this elevation is plotted.} +\item{alt_min, alt_max}{The altitude range to filter the range gates by. +If only \code{alt_min} value is provided next to a \code{vp} then the height bin intersection with this altitude is plotted.} \item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a \code{DBZH} less then 20 and a \code{RHOHV} less then 0.95. Alternative filters could be used to highlight specific effects.} @@ -70,7 +72,8 @@ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") example_pvol <- read_pvolfile(pvolfile) # VAD plots can be created for polar volumes alone vad(example_pvol, - range = c(5000, 30000), height = c(200, 400) + range_min = 5000, range_max = 30000, + alt_min = 200, alt_max = 400 ) # It is also possible to plot one height or more height bins from a `vp`. # Many visual aspects can be controlled through the function arguments @@ -78,11 +81,11 @@ vad(example_pvol, vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, - height = 400, + alt_min = 400, plotting_geom_args = list(ggplot2::aes(color = ZDR)) ) + ggplot2::scale_color_gradient2() vad(example_pvol, - vp = vp, height = c(400, 1200), + vp = vp, alt_min = 400, alt_max = 1200, plotting_geom = ggplot2::geom_bin2d, annotate = "sdvvp: {round(sd_vvp,2)} [m/s]", ) + ggplot2::scale_fill_viridis_c() From 30b606a717c07d69bc79e868a7077353262cf8d3 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 18:16:13 +0200 Subject: [PATCH 10/26] VRAD flexible and use eta to filter --- R/vad.R | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/R/vad.R b/R/vad.R index afe9e5385..befbf0cb8 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,4 +1,4 @@ -globalVariables(c("DBZH", "VRADH", "RHOHV")) +globalVariables(c("DBZH", "RHOHV")) NULL #' Create a Velocity Azimuth Display (VAD) plot @@ -64,7 +64,8 @@ vad <- function(x, ...) { vad.pvol <- function(x, vp = NULL, ..., range_min = NULL, range_max = NULL, alt_min = NULL, alt_max = NULL, - range_gate_filter = DBZH < 20 & RHOHV < .95, + range_gate_filter = dbz_to_eta(DBZH, !!x$attributes$how$wavelength )<36000 & + RHOHV < .95, plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", @@ -132,11 +133,14 @@ vad.pvol <- function(x, vp = NULL, ..., dplyr::mutate(scan_nr = 1:dplyr::n()) |> dplyr::select(-"param"), 1:length(x$scans)), MoreArgs = list(row.names = NULL) ) |> - dplyr::bind_rows() |> + dplyr::bind_rows() + + vrad_quantity<-c("VRAD","VRADH","VRADV") + vrad_quantity<-vrad_quantity[vrad_quantity %in%names(data)][1] # Filter the plotting data with the height and range, furthermore we omit NA's # and apply the range_gate_filter's - dplyr::filter( - !is.na(.data$azim), !is.na(VRADH), + data<- dplyr::filter(data, + !is.na(.data$azim), !is.na(!!sym(vrad_quantity)), .data$range > range_min, .data$range < range_max, .data$height < alt_max, .data$height > alt_min, !!rlang::enexpr(range_gate_filter) @@ -184,8 +188,12 @@ vad.pvol <- function(x, vp = NULL, ..., fill = NA, label.size = 0, size = annotation_size )) } + + # Combine everything in one plot - plt <- ggplot2::ggplot(data, ggplot2::aes(x = .data$azim, y = VRADH)) + + plt <- ggplot2::ggplot(data, + ggplot2::aes(x = !!rlang::sym("azim"), + y = !!rlang::sym(vrad_quantity))) + do.call(plotting_geom, plotting_geom_args) + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + ggplot2::ylab("Radial velocity [m/s]") + From cd3e8d47b112e23667aeb8a05b230ecb2fc71215 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 21:12:10 +0200 Subject: [PATCH 11/26] dbz and rhohv flexible amd document --- R/vad.R | 21 ++++++++++++--------- man/vad.Rd | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/R/vad.R b/R/vad.R index befbf0cb8..9678d0400 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,6 +1,3 @@ -globalVariables(c("DBZH", "RHOHV")) -NULL - #' Create a Velocity Azimuth Display (VAD) plot #' #' A velocity azimuth display plot visualized the radial velocity (`VRAD`) as a function of the azimuth from the radar. @@ -15,13 +12,15 @@ NULL #' If a `vp` is provided the range is taken from the `vp` and the argument `range_min` and `range_max` should not be provided. #' @param alt_min,alt_max The altitude range to filter the range gates by. #' If only `alt_min` value is provided next to a `vp` then the height bin intersection with this altitude is plotted. -#' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a `DBZH` less then 20 and a `RHOHV` less then 0.95. +#' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a eta (reflectivity) +#' value less then 36000 (the vol2bird default) and a `RHOHV` less then 0.95. +#' The function selects the first reflectivity factor quantity from `DBZ`, `DBZH`, `DBZV`, `TH` or `TV` that is present. #' Alternative filters could be used to highlight specific effects. #' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. #' Alternatives that avoid over plotting could be [ggplot2::geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. #' @param plotting_geom_args A list with additional arguments to the `plotting_geom` function. For example, controlling the point size or alpha. -#' @param annotate A [glue][glue::glue()] string that is used to annotate the plot with additional properties of the height bin of the `vp`. -#' The string is evaluated using the columns from `as.data.frame(vp)`. +#' @param annotate A [glue][glue::glue()] formating string that is used to annotate the plot with additional properties of the height bin from the `vp`. +#' The string is evaluated using the columns from `as.data.frame(vp)`, any of these columns can thus be used (e.g. `ff` or `sd_vvp`). #' Use `NULL` if no annotation is desired. #' @param annotation_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. @@ -33,6 +32,8 @@ NULL #' Using [ggplot2::theme()] the visual appearance can easily be modified. To do this the regular [+][ggplot2::+.gg] #' syntax can be used. In the examples this is demonstrated using scale. #' +#' As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. +#' #' @export #' @examples #' pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") @@ -64,8 +65,10 @@ vad <- function(x, ...) { vad.pvol <- function(x, vp = NULL, ..., range_min = NULL, range_max = NULL, alt_min = NULL, alt_max = NULL, - range_gate_filter = dbz_to_eta(DBZH, !!x$attributes$how$wavelength )<36000 & - RHOHV < .95, + range_gate_filter = + dplyr::if_all(utils::head(dplyr::matches(c("^DBZ$","^DBZH$","^DBZV$","^TH$","^TV$")),1), + \(dbz) dbz_to_eta(dbz, !!x$attributes$how$wavelength)<36000)& + dplyr::if_any(dplyr::matches("RHOHV"), \(rhohv) rhohv <.95), plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", @@ -140,7 +143,7 @@ vad.pvol <- function(x, vp = NULL, ..., # Filter the plotting data with the height and range, furthermore we omit NA's # and apply the range_gate_filter's data<- dplyr::filter(data, - !is.na(.data$azim), !is.na(!!sym(vrad_quantity)), + !is.na(.data$azim), !is.na(!!rlang::sym(vrad_quantity)), .data$range > range_min, .data$range < range_max, .data$height < alt_max, .data$height > alt_min, !!rlang::enexpr(range_gate_filter) diff --git a/man/vad.Rd b/man/vad.Rd index 42aff24a2..40fcc6f84 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -15,7 +15,10 @@ vad(x, ...) range_max = NULL, alt_min = NULL, alt_max = NULL, - range_gate_filter = DBZH < 20 & RHOHV < 0.95, + range_gate_filter = dplyr::if_all(utils::head(dplyr::matches(c("^DBZ$", "^DBZH$", + "^DBZV$", "^TH$", "^TV$")), 1), function(dbz) dbz_to_eta(dbz, + !!x$attributes$how$wavelength) < 36000) & dplyr::if_any(dplyr::matches("RHOHV"), + function(rhohv) rhohv < 0.95), plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}°", @@ -36,7 +39,9 @@ If a \code{vp} is provided the range is taken from the \code{vp} and the argumen \item{alt_min, alt_max}{The altitude range to filter the range gates by. If only \code{alt_min} value is provided next to a \code{vp} then the height bin intersection with this altitude is plotted.} -\item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a \code{DBZH} less then 20 and a \code{RHOHV} less then 0.95. +\item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a eta (reflectivity) +value less then 36000 (the vol2bird default) and a \code{RHOHV} less then 0.95. +The function selects the first reflectivity factor quantity from \code{DBZ}, \code{DBZH}, \code{DBZV}, \code{TH} or \code{TV} that is present. Alternative filters could be used to highlight specific effects.} \item{plotting_geom}{The geom function to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}, in some cases this suffers from over plotting. @@ -44,8 +49,8 @@ Alternatives that avoid over plotting could be \code{\link[ggplot2:geom_bin_2d]{ \item{plotting_geom_args}{A list with additional arguments to the \code{plotting_geom} function. For example, controlling the point size or alpha.} -\item{annotate}{A \link[glue:glue]{glue} string that is used to annotate the plot with additional properties of the height bin of the \code{vp}. -The string is evaluated using the columns from \code{as.data.frame(vp)}. +\item{annotate}{A \link[glue:glue]{glue} formating string that is used to annotate the plot with additional properties of the height bin from the \code{vp}. +The string is evaluated using the columns from \code{as.data.frame(vp)}, any of these columns can thus be used (e.g. \code{ff} or \code{sd_vvp}). Use \code{NULL} if no annotation is desired.} \item{annotation_size}{The text size used for the annotation.} @@ -66,6 +71,9 @@ As a \link[ggplot2:ggplot]{ggplot2::ggplot} object us returned additional eleme the \code{ggplot2} package. Labels could, for example, be modified using \code{\link[ggplot2:labs]{ggplot2::labs()}} or \code{\link[ggplot2:labs]{ggplot2::ggtitle()}}. Using \code{\link[ggplot2:theme]{ggplot2::theme()}} the visual appearance can easily be modified. To do this the regular \link[ggplot2:gg-add]{+} syntax can be used. In the examples this is demonstrated using scale. + +\if{html}{\out{
}}\preformatted{ As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. +}\if{html}{\out{
}} } \examples{ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") From 7d17ef26dc9402aace24f01fa58c07e56a9b0535 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 22:03:32 +0200 Subject: [PATCH 12/26] add basic testing --- R/vad.R | 22 ++++++++++++---------- tests/testthat/test-vad.R | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 tests/testthat/test-vad.R diff --git a/R/vad.R b/R/vad.R index 9678d0400..8b12851e8 100644 --- a/R/vad.R +++ b/R/vad.R @@ -104,22 +104,24 @@ vad.pvol <- function(x, vp = NULL, ..., } } # Checking of input variables - range_min <- max(-Inf, range_min) - range_max <- min(Inf, range_max) assertthat::assert_that( - is.numeric(range_min) && rlang::is_scalar_vector(range_min) + is.null(range_min) || rlang::is_scalar_vector(range_min), + is.null(range_max) || rlang::is_scalar_vector(range_max) ) + range_min <- max(-Inf, range_min) + range_max <- min(Inf, range_max) assertthat::assert_that( - is.numeric(range_max) && rlang::is_scalar_vector(range_max) - ) + is.numeric(range_min), + is.numeric(range_max), + is.null(alt_min) || rlang::is_scalar_vector(alt_min), + is.null(alt_max) || rlang::is_scalar_vector(alt_max) + + ) alt_min <- max(-Inf, alt_min) alt_max <- min(Inf, alt_max) assertthat::assert_that( - is.numeric(alt_min) && rlang::is_scalar_vector(alt_min) - ) - assertthat::assert_that( - is.numeric(alt_max) && rlang::is_scalar_vector(alt_max) - ) + is.numeric(alt_min) , + is.numeric(alt_max) ) # Convert the polar volume to plotting data by converting the scans to locations data <- diff --git a/tests/testthat/test-vad.R b/tests/testthat/test-vad.R new file mode 100644 index 000000000..55e6bbb43 --- /dev/null +++ b/tests/testthat/test-vad.R @@ -0,0 +1,34 @@ +pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") +pvol <- read_pvolfile(pvolfile) +vp <- example_vp + +test_that("vad() errors on incorrect parameters",{ + expect_error(vad(vp),"no applicable method for 'vad' applied to an object of class \"vp\"") + expect_error(vad(pvol,1),"is.vp(x = vp) is not TRUE", fixed=T) + expect_error(vad(pvol, range_max = "a"),"range_max is not a numeric or integer vector") + expect_error(vad(pvol, range_max = 1:2),'range_max is not NULL or rlang::is_scalar_vector(x = range_max) is not TRUE', fixed=TRUE) + expect_error(vad(pvol, range_min = "a"),"range_min is not a numeric or integer vector") + expect_error(vad(pvol, range_min = 1:2),'range_min is not NULL or rlang::is_scalar_vector(x = range_min) is not TRUE', fixed=TRUE) + + expect_error(vad(pvol, alt_max = "a"),"alt_max is not a numeric or integer vector") + expect_error(vad(pvol, alt_max = 1:2),'alt_max is not NULL or rlang::is_scalar_vector(x = alt_max) is not TRUE', fixed=TRUE) + expect_error(vad(pvol, alt_min = "a"),"alt_min is not a numeric or integer vector") + expect_error(vad(pvol, alt_min = 1:2),'alt_min is not NULL or rlang::is_scalar_vector(x = alt_min) is not TRUE', fixed=TRUE) + expect_error(vad(pvol, vp, range_min=1),'When specifying a vp the range is taken from there. Thus no range should be provided') + expect_error(vad(pvol, vp, range_max=1),'When specifying a vp the range is taken from there. Thus no range should be provided') +}) +test_that("plot from vad() matches some expectations",{ + expect_s3_class(plt<-vad(pvol, alt_min=400, alt_max=756, range_min = 6953, range_max=65344), "ggplot") + expect_s3_class(plt$facet,"FacetNull") + expect_true(all(plt$data$height<756)) + expect_true(all(plt$data$height>400)) + expect_true(all(plt$data$range<65344)) + expect_true(all(plt$data$range>6953)) + expect_equal(plt$mapping, ggplot2::aes(x=azim, y=VRADH), ignore_attr=TRUE) +}) + +test_that("plot from vad() matches some expectations",{ + expect_s3_class(plt<-vad(calculate_param(pvol, VRAD=VRADH),vp), "ggplot") + expect_s3_class(plt$facet,"FacetWrap") + expect_equal(plt$mapping, ggplot2::aes(x=azim, y=VRAD), ignore_attr=TRUE) +}) From d65eb78e27fd3a96e67d817d1700c74314bd1c59 Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 17 Sep 2025 22:04:50 +0200 Subject: [PATCH 13/26] style test --- tests/testthat/test-vad.R | 81 +++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/tests/testthat/test-vad.R b/tests/testthat/test-vad.R index 55e6bbb43..01fc3610a 100644 --- a/tests/testthat/test-vad.R +++ b/tests/testthat/test-vad.R @@ -2,33 +2,64 @@ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") pvol <- read_pvolfile(pvolfile) vp <- example_vp -test_that("vad() errors on incorrect parameters",{ - expect_error(vad(vp),"no applicable method for 'vad' applied to an object of class \"vp\"") - expect_error(vad(pvol,1),"is.vp(x = vp) is not TRUE", fixed=T) - expect_error(vad(pvol, range_max = "a"),"range_max is not a numeric or integer vector") - expect_error(vad(pvol, range_max = 1:2),'range_max is not NULL or rlang::is_scalar_vector(x = range_max) is not TRUE', fixed=TRUE) - expect_error(vad(pvol, range_min = "a"),"range_min is not a numeric or integer vector") - expect_error(vad(pvol, range_min = 1:2),'range_min is not NULL or rlang::is_scalar_vector(x = range_min) is not TRUE', fixed=TRUE) +test_that("vad() errors on incorrect parameters", { + expect_error(vad(vp), "no applicable method for 'vad' applied to an object of class \"vp\"") + expect_error(vad(pvol, 1), "is.vp(x = vp) is not TRUE", fixed = T) + expect_error( + vad(pvol, range_max = "a"), + "range_max is not a numeric or integer vector" + ) + expect_error(vad(pvol, range_max = 1:2), + "range_max is not NULL or rlang::is_scalar_vector(x = range_max) is not TRUE", + fixed = TRUE + ) + expect_error( + vad(pvol, range_min = "a"), + "range_min is not a numeric or integer vector" + ) + expect_error(vad(pvol, range_min = 1:2), + "range_min is not NULL or rlang::is_scalar_vector(x = range_min) is not TRUE", + fixed = TRUE + ) - expect_error(vad(pvol, alt_max = "a"),"alt_max is not a numeric or integer vector") - expect_error(vad(pvol, alt_max = 1:2),'alt_max is not NULL or rlang::is_scalar_vector(x = alt_max) is not TRUE', fixed=TRUE) - expect_error(vad(pvol, alt_min = "a"),"alt_min is not a numeric or integer vector") - expect_error(vad(pvol, alt_min = 1:2),'alt_min is not NULL or rlang::is_scalar_vector(x = alt_min) is not TRUE', fixed=TRUE) - expect_error(vad(pvol, vp, range_min=1),'When specifying a vp the range is taken from there. Thus no range should be provided') - expect_error(vad(pvol, vp, range_max=1),'When specifying a vp the range is taken from there. Thus no range should be provided') + expect_error( + vad(pvol, alt_max = "a"), "alt_max is not a numeric or integer vector" + ) + expect_error( + vad(pvol, alt_max = 1:2), "alt_max is not NULL or rlang::is_scalar_vector(x = alt_max) is not TRUE", + fixed = TRUE + ) + expect_error( + vad(pvol, alt_min = "a"), "alt_min is not a numeric or integer vector" + ) + expect_error( + vad(pvol, alt_min = 1:2), "alt_min is not NULL or rlang::is_scalar_vector(x = alt_min) is not TRUE", + fixed = TRUE + ) + expect_error( + vad(pvol, vp, range_min = 1), + "When specifying a vp the range is taken from there. Thus no range should be provided" + ) + expect_error( + vad(pvol, vp, range_max = 1), + "When specifying a vp the range is taken from there. Thus no range should be provided" + ) }) -test_that("plot from vad() matches some expectations",{ - expect_s3_class(plt<-vad(pvol, alt_min=400, alt_max=756, range_min = 6953, range_max=65344), "ggplot") - expect_s3_class(plt$facet,"FacetNull") - expect_true(all(plt$data$height<756)) - expect_true(all(plt$data$height>400)) - expect_true(all(plt$data$range<65344)) - expect_true(all(plt$data$range>6953)) - expect_equal(plt$mapping, ggplot2::aes(x=azim, y=VRADH), ignore_attr=TRUE) +test_that("plot from vad() matches some expectations", { + expect_s3_class(plt <- vad(pvol, + alt_min = 400, alt_max = 756, + range_min = 6953, range_max = 65344 + ), "ggplot") + expect_s3_class(plt$facet, "FacetNull") + expect_true(all(plt$data$height < 756)) + expect_true(all(plt$data$height > 400)) + expect_true(all(plt$data$range < 65344)) + expect_true(all(plt$data$range > 6953)) + expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) }) -test_that("plot from vad() matches some expectations",{ - expect_s3_class(plt<-vad(calculate_param(pvol, VRAD=VRADH),vp), "ggplot") - expect_s3_class(plt$facet,"FacetWrap") - expect_equal(plt$mapping, ggplot2::aes(x=azim, y=VRAD), ignore_attr=TRUE) +test_that("plot from vad() matches some expectations", { + expect_s3_class(plt <- vad(calculate_param(pvol, VRAD = VRADH), vp), "ggplot") + expect_s3_class(plt$facet, "FacetWrap") + expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRAD), ignore_attr = TRUE) }) From 9ee1a72d3f5be249b102e4ea12517573bb734bc5 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 18 Sep 2025 12:18:24 +0200 Subject: [PATCH 14/26] include different cosine correction methods --- NAMESPACE | 1 + R/vad.R | 103 ++++++++++++++++++++++++++++---------- man/vad.Rd | 21 +++++++- tests/testthat/test-vad.R | 21 +++++++- 4 files changed, 116 insertions(+), 30 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 69c6803d8..ed54fe4ca 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -87,6 +87,7 @@ S3method(summary,vp) S3method(summary,vpi) S3method(summary,vpts) S3method(vad,pvol) +S3method(vad,scan) export("rcs<-") export("sd_vvp_threshold<-") export(apply_mistnet) diff --git a/R/vad.R b/R/vad.R index 8b12851e8..233e5de97 100644 --- a/R/vad.R +++ b/R/vad.R @@ -24,6 +24,9 @@ #' Use `NULL` if no annotation is desired. #' @param annotation_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. +#' @param cosine_correction A character option to select what approach should be taken to correct for the fact that the horizontal velocities are measured at an angle (the elevation angle). +#' For plotting a single scan with one elevation angle the visualized sine curve is adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +#' See details for more information on the specific options. #' #' @returns A [ggplot2::ggplot] object. #' @@ -32,12 +35,21 @@ #' Using [ggplot2::theme()] the visual appearance can easily be modified. To do this the regular [+][ggplot2::+.gg] #' syntax can be used. In the examples this is demonstrated using scale. #' -#' As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. +#' As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. +#' +#' For these plots no vertical velocity is assumed as aeroecology will predominantly move in the horizontal plane. +#' However as the velocity is measured under an angle (the elevation angle of the radar) the radial velocities are an under estimation of the horizontal velocities. +#' To correct this either visualized sine curve from the horizontal velocities can be adjusted (`cosine_correction="vp"`), +#' however if multiple elevation angles are included no single correction can be applied and a warning is raised. +#' Alternatively the measured radial velocity can be corrected (`cosine_correction=="range_gates"`) the advantage is this can be done on multiple elevation scans in the same plot. +#' But with data that has been nyquist folded this correction can introduce extra errors. Therefore with low nyquist velocities a warning is raised. #' #' @export #' @examples #' pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") #' example_pvol <- read_pvolfile(pvolfile) +#' # VAD plot for a scan +#' vad(example_scan) #' # VAD plots can be created for polar volumes alone #' vad(example_pvol, #' range_min = 5000, range_max = 30000, @@ -66,15 +78,19 @@ vad.pvol <- function(x, vp = NULL, ..., range_min = NULL, range_max = NULL, alt_min = NULL, alt_max = NULL, range_gate_filter = - dplyr::if_all(utils::head(dplyr::matches(c("^DBZ$","^DBZH$","^DBZV$","^TH$","^TV$")),1), - \(dbz) dbz_to_eta(dbz, !!x$attributes$how$wavelength)<36000)& - dplyr::if_any(dplyr::matches("RHOHV"), \(rhohv) rhohv <.95), + dplyr::if_all( + utils::head(dplyr::matches(c("^DBZ$", "^DBZH$", "^DBZV$", "^TH$", "^TV$")), 1), + \(dbz) dbz_to_eta(dbz, !!x$attributes$how$wavelength) < 36000 + ) & + dplyr::if_any(dplyr::matches("RHOHV"), \(rhohv) rhohv < .95), plotting_geom = ggplot2::geom_point, plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", annotation_size = 4, - annotation_color = "red") { + annotation_color = "red", + cosine_correction = c("none", "vp", "range_gates")) { assertthat::assert_that(is.pvol(x)) + cosine_correction <- rlang::arg_match(cosine_correction) # Some of the input variables are checked and modified specifically in the VP context if (!is.null(vp)) { assertthat::assert_that(is.vp(vp)) @@ -115,13 +131,13 @@ vad.pvol <- function(x, vp = NULL, ..., is.numeric(range_max), is.null(alt_min) || rlang::is_scalar_vector(alt_min), is.null(alt_max) || rlang::is_scalar_vector(alt_max) - - ) + ) alt_min <- max(-Inf, alt_min) alt_max <- min(Inf, alt_max) assertthat::assert_that( - is.numeric(alt_min) , - is.numeric(alt_max) ) + is.numeric(alt_min), + is.numeric(alt_max) + ) # Convert the polar volume to plotting data by converting the scans to locations data <- @@ -140,19 +156,30 @@ vad.pvol <- function(x, vp = NULL, ..., ) |> dplyr::bind_rows() - vrad_quantity<-c("VRAD","VRADH","VRADV") - vrad_quantity<-vrad_quantity[vrad_quantity %in%names(data)][1] - # Filter the plotting data with the height and range, furthermore we omit NA's - # and apply the range_gate_filter's - data<- dplyr::filter(data, - !is.na(.data$azim), !is.na(!!rlang::sym(vrad_quantity)), - .data$range > range_min, .data$range < range_max, - .data$height < alt_max, .data$height > alt_min, - !!rlang::enexpr(range_gate_filter) - ) + vrad_quantity <- c("VRAD", "VRADH", "VRADV") + vrad_quantity <- vrad_quantity[vrad_quantity %in% names(data)][1] + # Filter the plotting data with the height and range, furthermore we omit NA's + # and apply the range_gate_filter's + data <- dplyr::filter( + data, + !is.na(.data$azim), !is.na(!!rlang::sym(vrad_quantity)), + .data$range > range_min, .data$range < range_max, + .data$height < alt_max, .data$height > alt_min, + !!rlang::enexpr(range_gate_filter) + ) + # Generate a geom that contains the sine function from the vp vp_geom <- list() if (!is.null(vp)) { + # restrict the vp annotation to only those elevation height bins for which we have data + vp_df <- dplyr::filter(vp_df, !(height + vp$attributes$where$interval < min(data$height) | height > max(data$height))) + + elangles <- unique(data$where.elangle) + if (length(elangles) != 1 && cosine_correction == "vp") { + warning("The data to plot is based on multiple elevation angles. To correct the `vp` data one elevation angle is needed, it is therefore averaged resulting in a inperfect correction.") + } + mean_elangle_cos <- cospi(mean(elangles) / 180) # TODO should this be a weighted mean? + s <- !(is.na(vp_df$ff) | is.na(vp_df$dd)) vp_geom <- mapply( SIMPLIFY = F, @@ -168,7 +195,10 @@ vad.pvol <- function(x, vp = NULL, ..., color = annotation_color ) }, - vp_df$ff[s], vp_df$dd[s], vp_df$height[s], vp_df$height_bin[s] + spd = vp_df$ff[s] * dplyr::if_else(cosine_correction == "vp", mean_elangle_cos, 1), + dir = vp_df$dd[s], + hgt = vp_df$height[s], + bin = vp_df$height_bin[s] ) if (length(vp_geom) > 1) { vp_geom <- c(vp_geom, ggplot2::facet_wrap(~ .data$height_bin)) @@ -193,18 +223,37 @@ vad.pvol <- function(x, vp = NULL, ..., fill = NA, label.size = 0, size = annotation_size )) } - - + ylab_label <- "Radial velocity [m/s]" + if (cosine_correction == "range_gates") { + data[, vrad_quantity] <- data[, vrad_quantity] * (1 / cospi(data$where.elangle / 180)) + ylab_label <- "Corrected radial velocity [m/s]" + if (min(data$how.NI) < 25) { + warning("There are data that have a relatively low nyquist velocity (below 25 m/s) meaning nyquist folding is likely to occur. Applying a cosine correction to range gates that are nyquist folded can result in larger deviations then before.") + } + } + if (cosine_correction == "none" && max(data$where.elangle) > acos(.95) / pi * 180) { + warning("Data with relativiely large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity") + } # Combine everything in one plot - plt <- ggplot2::ggplot(data, - ggplot2::aes(x = !!rlang::sym("azim"), - y = !!rlang::sym(vrad_quantity))) + + plt <- ggplot2::ggplot( + data, + ggplot2::aes( + x = !!rlang::sym("azim"), + y = !!rlang::sym(vrad_quantity) + ) + ) + do.call(plotting_geom, plotting_geom_args) + ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + - ggplot2::ylab("Radial velocity [m/s]") + + ggplot2::ylab(ylab_label) + ggplot2::xlab("Azimuth [\u00B0]") + vp_geom + annotate_geom - return(plt) } +#' @rdname vad +#' @export +vad.scan <- function(x, vp = NULL, ..., cosine_correction = c("vp", "none", "range_gates")) { + # construct a pvol to let `vad.pvol` do the heavy lifting however rely on the "vp" cosine correction at it is good for one elevation angle + pv <- structure(list(scans = list(x), attributes = list(how = list(wavelength = x$attributes$how$wavelength))), class = "pvol") + vad(pv, vp = vp, ..., cosine_correction = cosine_correction) +} diff --git a/man/vad.Rd b/man/vad.Rd index 40fcc6f84..94b92ee3e 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -3,6 +3,7 @@ \name{vad} \alias{vad} \alias{vad.pvol} +\alias{vad.scan} \title{Create a Velocity Azimuth Display (VAD) plot} \usage{ vad(x, ...) @@ -23,8 +24,11 @@ vad(x, ...) plotting_geom_args = list(), annotate = "{round(ff,1)} m/s, {round(dd)}°", annotation_size = 4, - annotation_color = "red" + annotation_color = "red", + cosine_correction = c("none", "vp", "range_gates") ) + +\method{vad}{scan}(x, vp = NULL, ..., cosine_correction = c("vp", "none", "range_gates")) } \arguments{ \item{x}{A polar volume from which range gates are extracted.} @@ -56,6 +60,10 @@ Use \code{NULL} if no annotation is desired.} \item{annotation_size}{The text size used for the annotation.} \item{annotation_color}{The color used for the \code{vp} annotations and line.} + +\item{cosine_correction}{A character option to select what approach should be taken to correct for the fact that the horizontal velocities are measured at an angle (the elevation angle). +For plotting a single scan with one elevation angle the visualized sine curve is adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +See details for more information on the specific options.} } \value{ A \link[ggplot2:ggplot]{ggplot2::ggplot} object. @@ -72,12 +80,21 @@ the \code{ggplot2} package. Labels could, for example, be modified using \code{\ Using \code{\link[ggplot2:theme]{ggplot2::theme()}} the visual appearance can easily be modified. To do this the regular \link[ggplot2:gg-add]{+} syntax can be used. In the examples this is demonstrated using scale. -\if{html}{\out{
}}\preformatted{ As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. +As for the radial velocity to plot the first of \code{VRAD}, \code{VRADH} or \code{VRADV} is used. + +\if{html}{\out{
}}\preformatted{ For these plots no vertical velocity is assumed as aeroecology will predominantly move in the horizontal plane. + However as the velocity is measured under an angle (the elevation angle of the radar) the radial velocities are an under estimation of the horizontal velocities. + To correct this either visualized sine curve from the horizontal velocities can be adjusted (`cosine_correction="vp"`), + however if multiple elevation angles are included no single correction can be applied and a warning is raised. + Alternatively the measured radial velocity can be corrected (`cosine_correction=="range_gates"`) the advantage is this can be done on multiple elevation scans in the same plot. + But with data that has been nyquist folded this correction can introduce extra errors. Therefore with low nyquist velocities a warning is raised. }\if{html}{\out{
}} } \examples{ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") example_pvol <- read_pvolfile(pvolfile) +# VAD plot for a scan +vad(example_scan) # VAD plots can be created for polar volumes alone vad(example_pvol, range_min = 5000, range_max = 30000, diff --git a/tests/testthat/test-vad.R b/tests/testthat/test-vad.R index 01fc3610a..2a9ce8190 100644 --- a/tests/testthat/test-vad.R +++ b/tests/testthat/test-vad.R @@ -1,6 +1,7 @@ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") pvol <- read_pvolfile(pvolfile) vp <- example_vp +scan<-example_scan test_that("vad() errors on incorrect parameters", { expect_error(vad(vp), "no applicable method for 'vad' applied to an object of class \"vp\"") @@ -48,7 +49,8 @@ test_that("vad() errors on incorrect parameters", { test_that("plot from vad() matches some expectations", { expect_s3_class(plt <- vad(pvol, alt_min = 400, alt_max = 756, - range_min = 6953, range_max = 65344 + range_min = 6953, range_max = 65344, + range_gate_filter= where.elangle<2 ), "ggplot") expect_s3_class(plt$facet, "FacetNull") expect_true(all(plt$data$height < 756)) @@ -56,10 +58,27 @@ test_that("plot from vad() matches some expectations", { expect_true(all(plt$data$range < 65344)) expect_true(all(plt$data$range > 6953)) expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) + expect_length(plt$data$scan_nr|> unique(),sum(get_elevation_angles(pvol)<2)) + }) test_that("plot from vad() matches some expectations", { expect_s3_class(plt <- vad(calculate_param(pvol, VRAD = VRADH), vp), "ggplot") expect_s3_class(plt$facet, "FacetWrap") expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRAD), ignore_attr = TRUE) + expect_length(plt$data$scan_nr|> unique(),length(pvol$scans)) + +}) + +test_that("plot from vad() matches some expectations for scans", { + expect_s3_class(plt <- vad(scan, vp), "ggplot") + expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) + expect_length(plt$data$scan_nr|> unique(),1) +}) +test_that("vad raises cosine correction warnings",{ +expect_warning(vad(pvol, cosine_correction="range_gates"),'There are data that have a relatively low nyquist velocity') + expect_warning(vad(pvol,vp, cosine_correction="vp"),'The data to plot is based on multiple elevation angle') + pvol_tmp<-pvol + pvol_tmp$scans[[3]]$attributes$where$elangle<-20 + expect_warning(vad(pvol_tmp),"Data with relativiely large elevation angles are included.") }) From f2453a8262c07169b023acad6146713f08affa6c Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 18 Sep 2025 12:39:25 +0200 Subject: [PATCH 15/26] typo and correction tests --- R/vad.R | 2 +- tests/testthat/test-vad.R | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/R/vad.R b/R/vad.R index 233e5de97..5459e16e6 100644 --- a/R/vad.R +++ b/R/vad.R @@ -232,7 +232,7 @@ vad.pvol <- function(x, vp = NULL, ..., } } if (cosine_correction == "none" && max(data$where.elangle) > acos(.95) / pi * 180) { - warning("Data with relativiely large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity") + warning("Data with relatively large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity") } # Combine everything in one plot plt <- ggplot2::ggplot( diff --git a/tests/testthat/test-vad.R b/tests/testthat/test-vad.R index 2a9ce8190..95e46b4f7 100644 --- a/tests/testthat/test-vad.R +++ b/tests/testthat/test-vad.R @@ -75,10 +75,17 @@ test_that("plot from vad() matches some expectations for scans", { expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) expect_length(plt$data$scan_nr|> unique(),1) }) -test_that("vad raises cosine correction warnings",{ -expect_warning(vad(pvol, cosine_correction="range_gates"),'There are data that have a relatively low nyquist velocity') - expect_warning(vad(pvol,vp, cosine_correction="vp"),'The data to plot is based on multiple elevation angle') +test_that("vad raises cosine correction warnings and is applied",{ + expect_warning(plt_rg<-vad(pvol, cosine_correction="range_gates", range_min=5000, range_max=25000),'There are data that have a relatively low nyquist velocity') + expect_warning(plt_vp<-vad(pvol,vp, cosine_correction="vp"),'The data to plot is based on multiple elevation angle') + expect_equal(plt_rg$data$VRADH, plt_vp$data$VRADH *1/cospi(plt_vp$data$where.elangle/180)) + expect_identical(plt_vp$layers[[2]]$stat_params$args$v,vp$data$ff[2]* cospi(mean(get_elevation_angles(pvol))/180)) + expect_identical(plt_vp$layers[[3]]$stat_params$args$a,vp$data$dd[3]) pvol_tmp<-pvol pvol_tmp$scans[[3]]$attributes$where$elangle<-20 - expect_warning(vad(pvol_tmp),"Data with relativiely large elevation angles are included.") + expect_warning(plt_none<-vad(pvol_tmp, vp),"Data with relatively large elevation angles are included.") + expect_identical(plt_none$data$VRAD, plt_vp$data$VRADH) + expect_s3_class(plt_scn<-vad(scan, vp),'ggplot') + expect_equal(plt_scn$data, plt_none$data |> dplyr::filter(scan_nr==1)) + expect_identical(plt_scn$layers[[2]]$stat_params$args$v,vp$data$ff[2]* cospi((get_elevation_angles(scan))/180)) }) From 8f30c8de4c6ef0ed17975421051e38294a0fb278 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Mon, 22 Sep 2025 12:08:47 -0400 Subject: [PATCH 16/26] fix nexrad bucket in tests --- tests/testthat/test-apply_mistnet.R | 2 +- tests/testthat/test-s3.R | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-apply_mistnet.R b/tests/testthat/test-apply_mistnet.R index 89fe21254..dfb07e21f 100644 --- a/tests/testthat/test-apply_mistnet.R +++ b/tests/testthat/test-apply_mistnet.R @@ -1,7 +1,7 @@ skip_if_offline() temp_dir <- tempdir() download.file( - "https://noaa-nexrad-level2.s3.amazonaws.com/2019/10/01/KBGM/KBGM20191001_000542_V06", + "https://unidata-nexrad-level2.s3.amazonaws.com/2019/10/01/KBGM/KBGM20191001_000542_V06", file.path(temp_dir, "KBGM_example"), quiet = TRUE ) diff --git a/tests/testthat/test-s3.R b/tests/testthat/test-s3.R index 0fc290061..aefb0361a 100644 --- a/tests/testthat/test-s3.R +++ b/tests/testthat/test-s3.R @@ -2,7 +2,7 @@ test_that("S3 bucket & prefix exist", { skip_if_offline() withr::local_options(timeout = 15) - bucket <- "noaa-nexrad-level2" + bucket <- "unidata-nexrad-level2" prefix <- "1998/01/20/KABR/" expect_true(s3_bucket_exists(bucket)) @@ -15,7 +15,7 @@ test_that("s3_get_bucket_df parses an S3 listing", { withr::local_options(timeout = 20) df <- s3_get_bucket_df( - bucket = "noaa-nexrad-level2", + bucket = "unidata-nexrad-level2", prefix = "1998/01/20/KABR/", max_keys = 10 ) @@ -33,19 +33,19 @@ test_that("s3_save_object downloads an object and respects overwrite", { skip_if_offline() withr::local_options(timeout = 30) - listing <- s3_get_bucket_df("noaa-nexrad-level2", "1998/01/20/KABR/", max_keys = 1) + listing <- s3_get_bucket_df("unidata-nexrad-level2", "1998/01/20/KABR/", max_keys = 1) skip_if(nrow(listing) == 0, "No objects returned; historical dataset may have moved.") key <- listing$Key[[1]] tmp <- tempfile(); on.exit(unlink(tmp), add = TRUE) # first download (live) - s3_save_object(object = key, bucket = "noaa-nexrad-level2", file = tmp, overwrite = TRUE) + s3_save_object(object = key, bucket = "unidata-nexrad-level2", file = tmp, overwrite = TRUE) expect_true(file.exists(tmp)) size1 <- file.size(tmp); expect_gt(size1, 0) # second call with overwrite=FALSE should leave file unchanged - s3_save_object(object = key, bucket = "noaa-nexrad-level2", file = tmp, overwrite = FALSE) + s3_save_object(object = key, bucket = "unidata-nexrad-level2", file = tmp, overwrite = FALSE) expect_identical(file.size(tmp), size1) }) From 287cea255dcf5882b625c624194e1842e2675b68 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Mon, 22 Sep 2025 12:38:06 -0400 Subject: [PATCH 17/26] remove `httr2::req_url_query(location = "")` line --- R/s3.R | 1 - 1 file changed, 1 deletion(-) diff --git a/R/s3.R b/R/s3.R index c70284e13..23d3eea68 100644 --- a/R/s3.R +++ b/R/s3.R @@ -34,7 +34,6 @@ #' @noRd s3_bucket_exists <- function(bucket) { httr2::request(.s3_endpoint(bucket)) |> - httr2::req_url_query(location = "") |> httr2::req_error(is_error = function(resp) FALSE) |> httr2::req_perform() |> httr2::resp_status() |> From 0de84cf008dd255bcc5808cadda9044db10942c5 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Mon, 22 Sep 2025 13:04:14 -0400 Subject: [PATCH 18/26] only retrieve head --- R/s3.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/s3.R b/R/s3.R index 23d3eea68..27a0f629e 100644 --- a/R/s3.R +++ b/R/s3.R @@ -34,6 +34,7 @@ #' @noRd s3_bucket_exists <- function(bucket) { httr2::request(.s3_endpoint(bucket)) |> + httr2::req_method("HEAD") |> httr2::req_error(is_error = function(resp) FALSE) |> httr2::req_perform() |> httr2::resp_status() |> From 3250b8d0700551c640a02e217332f17e40eddff2 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Mon, 22 Sep 2025 22:48:40 -0400 Subject: [PATCH 19/26] reviewing manual entry for `vad()` --- R/vad.R | 58 +++++++++++++++++++++++++---------------------- man/vad.Rd | 66 +++++++++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 58 deletions(-) diff --git a/R/vad.R b/R/vad.R index 5459e16e6..d43bb79fe 100644 --- a/R/vad.R +++ b/R/vad.R @@ -1,39 +1,43 @@ -#' Create a Velocity Azimuth Display (VAD) plot +#' Plot a Velocity Azimuth Display (VAD). #' -#' A velocity azimuth display plot visualized the radial velocity (`VRAD`) as a function of the azimuth from the radar. -#' Among others it can be used to asses the fit of the movement speeds in a vertical profile. -#' For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as -#' deviations from the sine function. +#' A Velocity Azimuth Display visualizes the radial velocity (typically `VRADH`) as a function of the beam azimuth. +#' These plots are useful to assess the quality of radial velocity data (including velocity folding), +#' and for visually inspecting the quality of the velocity fit of a vertical profile estimate. #' -#' @param x A polar volume from which range gates are extracted. +#' Poor velocity fits in vertical profiles can arise in cases where the movement is not well described by a unidirectional +#' velocity model, or for data with strong velocity folding (i.e. a low Nyquist velocity). +#' @param x An object of class `pvol` or `scan` +#' @param vp An object of class `pvol`, typically a vertical profile estimated for the input polar volume specified under `x` using `bioRad::calculate_vp()`. #' @param ... Currently not used. -#' @param vp A vertical profile to annotate the VAD plot with the fit in the vertical profile -#' @param range_min,range_max The distance range in to filter the range gates with. -#' If a `vp` is provided the range is taken from the `vp` and the argument `range_min` and `range_max` should not be provided. -#' @param alt_min,alt_max The altitude range to filter the range gates by. -#' If only `alt_min` value is provided next to a `vp` then the height bin intersection with this altitude is plotted. -#' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for a eta (reflectivity) -#' value less then 36000 (the vol2bird default) and a `RHOHV` less then 0.95. -#' The function selects the first reflectivity factor quantity from `DBZ`, `DBZH`, `DBZV`, `TH` or `TV` that is present. -#' Alternative filters could be used to highlight specific effects. -#' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()], in some cases this suffers from over plotting. -#' Alternatives that avoid over plotting could be [ggplot2::geom_bin2d()] or [ggpointdensity::geom_pointdensity()]. -#' @param plotting_geom_args A list with additional arguments to the `plotting_geom` function. For example, controlling the point size or alpha. +#' @param range_min,range_max Numeric. The minimum and maximum range to include, in m. Values are taken from the `vp` +#' object if provided. +#' @param alt_min,alt_max Numeric. The minimum and maximum altitude to include, in m. If only `alt_min` value is provided +#' next to a `vp` then the height bin intersection with this altitude is plotted. +#' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for linear reflectivity +#' value (`eta`) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see `etaMax` in `vol2bird::vol2bird_config()`) +#' and a correlation coefficient (`RHOHV`) < 0.95. The function selects the first reflectivity factor quantity +#' from `DBZ`, `DBZH`, `DBZV`, `TH` or `TV` that is present. Alternative filters could be used to highlight specific effects. +#' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()]. In cases +#' with many data points plots may become cluttered, in which cases alternatives like [ggplot2::geom_bin2d()] or +#' [ggpointdensity::geom_pointdensity()] may be preferred. +#' @param plotting_geom_args A list with additional arguments to the `plotting_geom` function. For example, controlling the point size or transparancy alpha. #' @param annotate A [glue][glue::glue()] formating string that is used to annotate the plot with additional properties of the height bin from the `vp`. -#' The string is evaluated using the columns from `as.data.frame(vp)`, any of these columns can thus be used (e.g. `ff` or `sd_vvp`). -#' Use `NULL` if no annotation is desired. +#' The string is evaluated using the columns from `as.data.frame(vp)`, any of these columns can thus be used (e.g. `ff` or `sd_vvp`). +#' Use `NULL` if no annotation is desired. #' @param annotation_color The color used for the `vp` annotations and line. #' @param annotation_size The text size used for the annotation. -#' @param cosine_correction A character option to select what approach should be taken to correct for the fact that the horizontal velocities are measured at an angle (the elevation angle). -#' For plotting a single scan with one elevation angle the visualized sine curve is adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. -#' See details for more information on the specific options. +#' @param cosine_correction A character option to select what approach should be taken to correct for the fact +#' that the horizontal velocities are measured at an angle (the elevation angle). +#' For plotting a single scan with one elevation angle the visualized sine curve is +#' adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +#' See details for more information on the specific options. #' #' @returns A [ggplot2::ggplot] object. #' -#' @details As a [ggplot2::ggplot] object us returned additional elements can be added using regular function in -#' the `ggplot2` package. Labels could, for example, be modified using [ggplot2::labs()] or [ggplot2::ggtitle()]. -#' Using [ggplot2::theme()] the visual appearance can easily be modified. To do this the regular [+][ggplot2::+.gg] -#' syntax can be used. In the examples this is demonstrated using scale. +#' @details The returned [ggplot2::ggplot] can be styled with additional elements available in the `ggplot2` package. +#' For example, labels and titles can be modified using [ggplot2::labs()] or [ggplot2::ggtitle()]. +#' Using [ggplot2::theme()] the visual appearance can easily be modified. Use the regular [+][ggplot2::+.gg] +#' syntax to make modifications (e.g. see the examples for changing the plotting color gradients). #' #' As for the radial velocity to plot the first of `VRAD`, `VRADH` or `VRADV` is used. #' diff --git a/man/vad.Rd b/man/vad.Rd index 94b92ee3e..0eafe0d54 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -4,7 +4,7 @@ \alias{vad} \alias{vad.pvol} \alias{vad.scan} -\title{Create a Velocity Azimuth Display (VAD) plot} +\title{Plot a Velocity Azimuth Display (VAD).} \usage{ vad(x, ...) @@ -31,27 +31,28 @@ vad(x, ...) \method{vad}{scan}(x, vp = NULL, ..., cosine_correction = c("vp", "none", "range_gates")) } \arguments{ -\item{x}{A polar volume from which range gates are extracted.} +\item{x}{An object of class \code{pvol} or \code{scan}} \item{...}{Currently not used.} -\item{vp}{A vertical profile to annotate the VAD plot with the fit in the vertical profile} +\item{vp}{An object of class \code{pvol}, typically a vertical profile estimated for the input polar volume specified under \code{x} using \code{bioRad::calculate_vp()}.} -\item{range_min, range_max}{The distance range in to filter the range gates with. -If a \code{vp} is provided the range is taken from the \code{vp} and the argument \code{range_min} and \code{range_max} should not be provided.} +\item{range_min, range_max}{Numeric. The minimum and maximum range to include, in m. Values are taken from the \code{vp} +object if provided.} -\item{alt_min, alt_max}{The altitude range to filter the range gates by. -If only \code{alt_min} value is provided next to a \code{vp} then the height bin intersection with this altitude is plotted.} +\item{alt_min, alt_max}{Numeric. The minimum and maximum altitude to include, in m. If only \code{alt_min} value is provided +next to a \code{vp} then the height bin intersection with this altitude is plotted.} -\item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for a eta (reflectivity) -value less then 36000 (the vol2bird default) and a \code{RHOHV} less then 0.95. -The function selects the first reflectivity factor quantity from \code{DBZ}, \code{DBZH}, \code{DBZV}, \code{TH} or \code{TV} that is present. -Alternative filters could be used to highlight specific effects.} +\item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for linear reflectivity +value (\code{eta}) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see \code{etaMax} in \code{vol2bird::vol2bird_config()}) +and a correlation coefficient (\code{RHOHV}) < 0.95. The function selects the first reflectivity factor quantity +from \code{DBZ}, \code{DBZH}, \code{DBZV}, \code{TH} or \code{TV} that is present. Alternative filters could be used to highlight specific effects.} -\item{plotting_geom}{The geom function to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}, in some cases this suffers from over plotting. -Alternatives that avoid over plotting could be \code{\link[ggplot2:geom_bin_2d]{ggplot2::geom_bin2d()}} or \code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}}.} +\item{plotting_geom}{The geom function to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}. In cases +with many data points plots may become cluttered, in which cases alternatives like \code{\link[ggplot2:geom_bin_2d]{ggplot2::geom_bin2d()}} or +\code{\link[ggpointdensity:geom_pointdensity]{ggpointdensity::geom_pointdensity()}} may be preferred.} -\item{plotting_geom_args}{A list with additional arguments to the \code{plotting_geom} function. For example, controlling the point size or alpha.} +\item{plotting_geom_args}{A list with additional arguments to the \code{plotting_geom} function. For example, controlling the point size or transparancy alpha.} \item{annotate}{A \link[glue:glue]{glue} formating string that is used to annotate the plot with additional properties of the height bin from the \code{vp}. The string is evaluated using the columns from \code{as.data.frame(vp)}, any of these columns can thus be used (e.g. \code{ff} or \code{sd_vvp}). @@ -61,34 +62,37 @@ Use \code{NULL} if no annotation is desired.} \item{annotation_color}{The color used for the \code{vp} annotations and line.} -\item{cosine_correction}{A character option to select what approach should be taken to correct for the fact that the horizontal velocities are measured at an angle (the elevation angle). -For plotting a single scan with one elevation angle the visualized sine curve is adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +\item{cosine_correction}{A character option to select what approach should be taken to correct for the fact +that the horizontal velocities are measured at an angle (the elevation angle). +For plotting a single scan with one elevation angle the visualized sine curve is +adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. See details for more information on the specific options.} } \value{ A \link[ggplot2:ggplot]{ggplot2::ggplot} object. } \description{ -A velocity azimuth display plot visualized the radial velocity (\code{VRAD}) as a function of the azimuth from the radar. -Among others it can be used to asses the fit of the movement speeds in a vertical profile. -For example, when the movement is not uniform but rather in multiple directions or rotating this will be visible as -deviations from the sine function. +A Velocity Azimuth Display visualizes the radial velocity (typically \code{VRADH}) as a function of the beam azimuth. +These plots are useful to assess the quality of radial velocity data (including velocity folding), +and for visually inspecting the quality of the velocity fit of a vertical profile estimate. } \details{ -As a \link[ggplot2:ggplot]{ggplot2::ggplot} object us returned additional elements can be added using regular function in -the \code{ggplot2} package. Labels could, for example, be modified using \code{\link[ggplot2:labs]{ggplot2::labs()}} or \code{\link[ggplot2:labs]{ggplot2::ggtitle()}}. -Using \code{\link[ggplot2:theme]{ggplot2::theme()}} the visual appearance can easily be modified. To do this the regular \link[ggplot2:gg-add]{+} -syntax can be used. In the examples this is demonstrated using scale. +Poor velocity fits in vertical profiles can arise in cases where the movement is not well described by a unidirectional +velocity model, or for data with strong velocity folding (i.e. a low Nyquist velocity). + +The returned \link[ggplot2:ggplot]{ggplot2::ggplot} can be styled with additional elements available in the \code{ggplot2} package. +For example, labels and titles can be modified using \code{\link[ggplot2:labs]{ggplot2::labs()}} or \code{\link[ggplot2:labs]{ggplot2::ggtitle()}}. +Using \code{\link[ggplot2:theme]{ggplot2::theme()}} the visual appearance can easily be modified. Use the regular \link[ggplot2:gg-add]{+} +syntax to make modifications (e.g. see the examples for changing the plotting color gradients). As for the radial velocity to plot the first of \code{VRAD}, \code{VRADH} or \code{VRADV} is used. -\if{html}{\out{
}}\preformatted{ For these plots no vertical velocity is assumed as aeroecology will predominantly move in the horizontal plane. - However as the velocity is measured under an angle (the elevation angle of the radar) the radial velocities are an under estimation of the horizontal velocities. - To correct this either visualized sine curve from the horizontal velocities can be adjusted (`cosine_correction="vp"`), - however if multiple elevation angles are included no single correction can be applied and a warning is raised. - Alternatively the measured radial velocity can be corrected (`cosine_correction=="range_gates"`) the advantage is this can be done on multiple elevation scans in the same plot. - But with data that has been nyquist folded this correction can introduce extra errors. Therefore with low nyquist velocities a warning is raised. -}\if{html}{\out{
}} +For these plots no vertical velocity is assumed as aeroecology will predominantly move in the horizontal plane. +However as the velocity is measured under an angle (the elevation angle of the radar) the radial velocities are an under estimation of the horizontal velocities. +To correct this either visualized sine curve from the horizontal velocities can be adjusted (\code{cosine_correction="vp"}), +however if multiple elevation angles are included no single correction can be applied and a warning is raised. +Alternatively the measured radial velocity can be corrected (\code{cosine_correction=="range_gates"}) the advantage is this can be done on multiple elevation scans in the same plot. +But with data that has been nyquist folded this correction can introduce extra errors. Therefore with low nyquist velocities a warning is raised. } \examples{ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") From e75431593e1c03d985f6156b3779adc0c0f354c0 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Tue, 23 Sep 2025 09:59:23 -0400 Subject: [PATCH 20/26] change to coloring by DBZH, more common use case to identify weather outliers --- R/vad.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/vad.R b/R/vad.R index d43bb79fe..fc265894c 100644 --- a/R/vad.R +++ b/R/vad.R @@ -66,8 +66,8 @@ #' vad(example_pvol, #' vp = vp, #' alt_min = 400, -#' plotting_geom_args = list(ggplot2::aes(color = ZDR)) -#' ) + ggplot2::scale_color_gradient2() +#' plotting_geom_args = list(ggplot2::aes(color = DBZH)) +#' ) + viridis::scale_color_virids() #' vad(example_pvol, #' vp = vp, alt_min = 400, alt_max = 1200, #' plotting_geom = ggplot2::geom_bin2d, From 692055c33dbd87781544751ee2599841ba553239 Mon Sep 17 00:00:00 2001 From: Adriaan Dokter Date: Tue, 23 Sep 2025 10:32:33 -0400 Subject: [PATCH 21/26] clarifying description of alt_min argument --- R/vad.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/vad.R b/R/vad.R index fc265894c..c6e2ebbc0 100644 --- a/R/vad.R +++ b/R/vad.R @@ -11,8 +11,9 @@ #' @param ... Currently not used. #' @param range_min,range_max Numeric. The minimum and maximum range to include, in m. Values are taken from the `vp` #' object if provided. -#' @param alt_min,alt_max Numeric. The minimum and maximum altitude to include, in m. If only `alt_min` value is provided -#' next to a `vp` then the height bin intersection with this altitude is plotted. +#' @param alt_min,alt_max Numeric. The minimum and maximum altitude to include, in m. Separate panels will be plot for each +#' altitude layer of the profile `vp`. If only `alt_min` is provided +#' data for height bin containing the provided altitude is plotted. #' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for linear reflectivity #' value (`eta`) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see `etaMax` in `vol2bird::vol2bird_config()`) #' and a correlation coefficient (`RHOHV`) < 0.95. The function selects the first reflectivity factor quantity From 068c721731fca8f11e33a27577b5179d0726a281 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 7 Oct 2025 13:24:41 +0200 Subject: [PATCH 22/26] add velocity_quanity argument --- R/vad.R | 162 +++++++++++++++++++++++++++++++++++++---------------- man/vad.Rd | 14 +++-- 2 files changed, 122 insertions(+), 54 deletions(-) diff --git a/R/vad.R b/R/vad.R index c6e2ebbc0..54b2881d8 100644 --- a/R/vad.R +++ b/R/vad.R @@ -30,8 +30,9 @@ #' @param cosine_correction A character option to select what approach should be taken to correct for the fact #' that the horizontal velocities are measured at an angle (the elevation angle). #' For plotting a single scan with one elevation angle the visualized sine curve is -#' adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +#' adjusted by default for multiple scans multiple scans in a polar volume no correction is applied unless explicitly selected. #' See details for more information on the specific options. +#' @param velocity_quantity The velocity quantity used for plotting. If a vector the first present in the polar volume is used. #' #' @returns A [ggplot2::ggplot] object. #' @@ -79,21 +80,30 @@ vad <- function(x, ...) { } #' @rdname vad #' @export -vad.pvol <- function(x, vp = NULL, ..., - range_min = NULL, range_max = NULL, - alt_min = NULL, alt_max = NULL, - range_gate_filter = - dplyr::if_all( - utils::head(dplyr::matches(c("^DBZ$", "^DBZH$", "^DBZV$", "^TH$", "^TV$")), 1), - \(dbz) dbz_to_eta(dbz, !!x$attributes$how$wavelength) < 36000 - ) & - dplyr::if_any(dplyr::matches("RHOHV"), \(rhohv) rhohv < .95), - plotting_geom = ggplot2::geom_point, - plotting_geom_args = list(), - annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", - annotation_size = 4, - annotation_color = "red", - cosine_correction = c("none", "vp", "range_gates")) { +vad.pvol <- function( + x, + vp = NULL, + ..., + range_min = NULL, + range_max = NULL, + alt_min = NULL, + alt_max = NULL, + range_gate_filter = dplyr::if_all( + utils::head( + dplyr::matches(c("^DBZ$", "^DBZH$", "^DBZV$", "^TH$", "^TV$")), + 1 + ), + \(dbz) dbz_to_eta(dbz, !!x$attributes$how$wavelength) < 36000 + ) & + dplyr::if_any(dplyr::matches("RHOHV"), \(rhohv) rhohv < .95), + plotting_geom = ggplot2::geom_point, + plotting_geom_args = list(), + annotate = "{round(ff,1)} m/s, {round(dd)}\u00B0", + annotation_size = 4, + annotation_color = "red", + velocity_quantity = c("VRAD", "VRADH", "VRADV"), + cosine_correction = c("none", "vp", "range_gates") +) { assertthat::assert_that(is.pvol(x)) cosine_correction <- rlang::arg_match(cosine_correction) # Some of the input variables are checked and modified specifically in the VP context @@ -107,12 +117,19 @@ vad.pvol <- function(x, vp = NULL, ..., range_min <- vp$attributes$how$minrange * 1000 range_max <- vp$attributes$how$maxrange * 1000 # convert the vp to df with height as we will need it later - vp_df <- as.data.frame(vp) |> dplyr::mutate( - height_bin = glue::glue("{height}-{height + vp$attributes$where$interval} [m]"), - height_bin = factor(.data$height_bin, levels = .data$height_bin) - ) + vp_df <- as.data.frame(vp) |> + dplyr::mutate( + height_bin = glue::glue( + "{height}-{height + vp$attributes$where$interval} [m]" + ), + height_bin = factor(.data$height_bin, levels = .data$height_bin) + ) # For one height we take the interval that intersects - if (is.numeric(alt_min) && rlang::is_scalar_vector(alt_min) && is.null(alt_max)) { + if ( + is.numeric(alt_min) && + rlang::is_scalar_vector(alt_min) && + is.null(alt_max) + ) { alt_min <- max(vp$data$height[vp$data$height <= alt_min]) alt_max <- alt_min + vp$attributes$where$interval } @@ -155,21 +172,27 @@ vad.pvol <- function(x, vp = NULL, ..., ), # We add extra attributes from the scan attributes that can be useful for filtering or highlighting # certain attributes - split(attribute_table(x) |> - dplyr::mutate(scan_nr = 1:dplyr::n()) |> - dplyr::select(-"param"), 1:length(x$scans)), MoreArgs = list(row.names = NULL) + split( + attribute_table(x) |> + dplyr::mutate(scan_nr = 1:dplyr::n()) |> + dplyr::select(-"param"), + 1:length(x$scans) + ), + MoreArgs = list(row.names = NULL) ) |> dplyr::bind_rows() - vrad_quantity <- c("VRAD", "VRADH", "VRADV") - vrad_quantity <- vrad_quantity[vrad_quantity %in% names(data)][1] + velocity_quantity <- velocity_quantity[velocity_quantity %in% names(data)][1] # Filter the plotting data with the height and range, furthermore we omit NA's # and apply the range_gate_filter's data <- dplyr::filter( data, - !is.na(.data$azim), !is.na(!!rlang::sym(vrad_quantity)), - .data$range > range_min, .data$range < range_max, - .data$height < alt_max, .data$height > alt_min, + !is.na(.data$azim), + !is.na(!!rlang::sym(velocity_quantity)), + .data$range > range_min, + .data$range < range_max, + .data$height < alt_max, + .data$height > alt_min, !!rlang::enexpr(range_gate_filter) ) @@ -177,11 +200,17 @@ vad.pvol <- function(x, vp = NULL, ..., vp_geom <- list() if (!is.null(vp)) { # restrict the vp annotation to only those elevation height bins for which we have data - vp_df <- dplyr::filter(vp_df, !(height + vp$attributes$where$interval < min(data$height) | height > max(data$height))) + vp_df <- dplyr::filter( + vp_df, + !(height + vp$attributes$where$interval < min(data$height) | + height > max(data$height)) + ) elangles <- unique(data$where.elangle) if (length(elangles) != 1 && cosine_correction == "vp") { - warning("The data to plot is based on multiple elevation angles. To correct the `vp` data one elevation angle is needed, it is therefore averaged resulting in a inperfect correction.") + warning( + "The data to plot is based on multiple elevation angles. To correct the `vp` data one elevation angle is needed, it is therefore averaged resulting in a inperfect correction." + ) } mean_elangle_cos <- cospi(mean(elangles) / 180) # TODO should this be a weighted mean? @@ -190,17 +219,21 @@ vad.pvol <- function(x, vp = NULL, ..., SIMPLIFY = F, function(spd, dir, hgt, bin) { ggplot2::geom_function( - data = dplyr::bind_cols(data, data.frame( - min_bin_height = hgt, - max_bin_height = hgt + vp$attributes$where$interval, - height_bin = bin - )), + data = dplyr::bind_cols( + data, + data.frame( + min_bin_height = hgt, + max_bin_height = hgt + vp$attributes$where$interval, + height_bin = bin + ) + ), fun = function(x, v, a) cos((x - a) / 180 * pi) * v, args = list(a = dir, v = spd), color = annotation_color ) }, - spd = vp_df$ff[s] * dplyr::if_else(cosine_correction == "vp", mean_elangle_cos, 1), + spd = vp_df$ff[s] * + dplyr::if_else(cosine_correction == "vp", mean_elangle_cos, 1), dir = vp_df$dd[s], hgt = vp_df$height[s], bin = vp_df$height_bin[s] @@ -208,7 +241,10 @@ vad.pvol <- function(x, vp = NULL, ..., if (length(vp_geom) > 1) { vp_geom <- c(vp_geom, ggplot2::facet_wrap(~ .data$height_bin)) } - int <- findInterval(data$height, c(vp_df$height, max(vp_df$height) + vp$attributes$where$interval)) + int <- findInterval( + data$height, + c(vp_df$height, max(vp_df$height) + vp$attributes$where$interval) + ) int[int == 0] <- NA data$height_bin <- vp_df$height_bin[int] } @@ -218,37 +254,54 @@ vad.pvol <- function(x, vp = NULL, ..., if (!is.null(annotate) & is.vp(vp)) { df <- data.frame( label = as.character(glue::glue_data(annotate, .x = vp_df)), - x = Inf, y = Inf, height_bin = vp_df$height_bin + x = Inf, + y = Inf, + height_bin = vp_df$height_bin ) annotate_geom <- list(ggplot2::geom_label( data = df, ggplot2::aes(x = x, y = y, label = .data$label), - vjust = "inward", hjust = "inward", color = annotation_color, - fill = NA, label.size = 0, size = annotation_size + vjust = "inward", + hjust = "inward", + color = annotation_color, + fill = NA, + label.size = 0, + size = annotation_size )) } ylab_label <- "Radial velocity [m/s]" if (cosine_correction == "range_gates") { - data[, vrad_quantity] <- data[, vrad_quantity] * (1 / cospi(data$where.elangle / 180)) + data[, velocity_quantity] <- data[, velocity_quantity] * + (1 / cospi(data$where.elangle / 180)) ylab_label <- "Corrected radial velocity [m/s]" if (min(data$how.NI) < 25) { - warning("There are data that have a relatively low nyquist velocity (below 25 m/s) meaning nyquist folding is likely to occur. Applying a cosine correction to range gates that are nyquist folded can result in larger deviations then before.") + warning( + "There are data that have a relatively low nyquist velocity (below 25 m/s) meaning nyquist folding is likely to occur. Applying a cosine correction to range gates that are nyquist folded can result in larger deviations then before." + ) } } - if (cosine_correction == "none" && max(data$where.elangle) > acos(.95) / pi * 180) { - warning("Data with relatively large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity") + if ( + cosine_correction == "none" && + max(data$where.elangle) > acos(.95) / pi * 180 + ) { + warning( + "Data with relatively large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity" + ) } # Combine everything in one plot plt <- ggplot2::ggplot( data, ggplot2::aes( x = !!rlang::sym("azim"), - y = !!rlang::sym(vrad_quantity) + y = !!rlang::sym(velocity_quantity) ) ) + do.call(plotting_geom, plotting_geom_args) + - ggplot2::scale_x_continuous(breaks = (0:4) * 90, minor_breaks = (0:12) * 30) + + ggplot2::scale_x_continuous( + breaks = (0:4) * 90, + minor_breaks = (0:12) * 30 + ) + ggplot2::ylab(ylab_label) + ggplot2::xlab("Azimuth [\u00B0]") + vp_geom + @@ -257,8 +310,19 @@ vad.pvol <- function(x, vp = NULL, ..., } #' @rdname vad #' @export -vad.scan <- function(x, vp = NULL, ..., cosine_correction = c("vp", "none", "range_gates")) { +vad.scan <- function( + x, + vp = NULL, + ..., + cosine_correction = c("vp", "none", "range_gates") +) { # construct a pvol to let `vad.pvol` do the heavy lifting however rely on the "vp" cosine correction at it is good for one elevation angle - pv <- structure(list(scans = list(x), attributes = list(how = list(wavelength = x$attributes$how$wavelength))), class = "pvol") + pv <- structure( + list( + scans = list(x), + attributes = list(how = list(wavelength = x$attributes$how$wavelength)) + ), + class = "pvol" + ) vad(pv, vp = vp, ..., cosine_correction = cosine_correction) } diff --git a/man/vad.Rd b/man/vad.Rd index 0eafe0d54..d34aaf615 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -25,6 +25,7 @@ vad(x, ...) annotate = "{round(ff,1)} m/s, {round(dd)}°", annotation_size = 4, annotation_color = "red", + velocity_quantity = c("VRAD", "VRADH", "VRADV"), cosine_correction = c("none", "vp", "range_gates") ) @@ -40,8 +41,9 @@ vad(x, ...) \item{range_min, range_max}{Numeric. The minimum and maximum range to include, in m. Values are taken from the \code{vp} object if provided.} -\item{alt_min, alt_max}{Numeric. The minimum and maximum altitude to include, in m. If only \code{alt_min} value is provided -next to a \code{vp} then the height bin intersection with this altitude is plotted.} +\item{alt_min, alt_max}{Numeric. The minimum and maximum altitude to include, in m. Separate panels will be plot for each +altitude layer of the profile \code{vp}. If only \code{alt_min} is provided +data for height bin containing the provided altitude is plotted.} \item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for linear reflectivity value (\code{eta}) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see \code{etaMax} in \code{vol2bird::vol2bird_config()}) @@ -62,10 +64,12 @@ Use \code{NULL} if no annotation is desired.} \item{annotation_color}{The color used for the \code{vp} annotations and line.} +\item{velocity_quantity}{The velocity quantity used for plotting. If a vector the first present in the polar volume is used.} + \item{cosine_correction}{A character option to select what approach should be taken to correct for the fact that the horizontal velocities are measured at an angle (the elevation angle). For plotting a single scan with one elevation angle the visualized sine curve is -adjusted by default for multiple scans multiple scans in a polar volume no correction is aplied unless explicitly selected. +adjusted by default for multiple scans multiple scans in a polar volume no correction is applied unless explicitly selected. See details for more information on the specific options.} } \value{ @@ -111,8 +115,8 @@ vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, alt_min = 400, - plotting_geom_args = list(ggplot2::aes(color = ZDR)) -) + ggplot2::scale_color_gradient2() + plotting_geom_args = list(ggplot2::aes(color = DBZH)) +) + viridis::scale_color_virids() vad(example_pvol, vp = vp, alt_min = 400, alt_max = 1200, plotting_geom = ggplot2::geom_bin2d, From 6b65b66868a15b5a4f02d93447209bd26661b430 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 7 Oct 2025 13:39:58 +0200 Subject: [PATCH 23/26] improve documentation --- R/vad.R | 6 ++++-- man/vad.Rd | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/R/vad.R b/R/vad.R index 54b2881d8..57e7358f1 100644 --- a/R/vad.R +++ b/R/vad.R @@ -16,7 +16,7 @@ #' data for height bin containing the provided altitude is plotted. #' @param range_gate_filter Optional filtering of the range gates. By default range gates are filtered for linear reflectivity #' value (`eta`) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see `etaMax` in `vol2bird::vol2bird_config()`) -#' and a correlation coefficient (`RHOHV`) < 0.95. The function selects the first reflectivity factor quantity +#' and a correlation coefficient (`RHOHV`) < 0.95. By default function selects the first reflectivity factor quantity #' from `DBZ`, `DBZH`, `DBZV`, `TH` or `TV` that is present. Alternative filters could be used to highlight specific effects. #' @param plotting_geom The geom function to visualize the range gates, the default is [ggplot2::geom_point()]. In cases #' with many data points plots may become cluttered, in which cases alternatives like [ggplot2::geom_bin2d()] or @@ -281,9 +281,11 @@ vad.pvol <- function( ) } } + # Calculate if large (bigger then 5%) under estimations of speed do occur and if so warn about it + # (acos(.95) / pi * 180) calculates the elevation angle at which these occur if ( cosine_correction == "none" && - max(data$where.elangle) > acos(.95) / pi * 180 + max(data$where.elangle) > (acos(.95) / pi * 180) ) { warning( "Data with relatively large elevation angles are included. This means larger deviation (above 5%) between the horizontal velocity and radial velocity measured occur. Therefore it is important to consider cosine corrections of the radial velocity" diff --git a/man/vad.Rd b/man/vad.Rd index d34aaf615..f381d0fa6 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -47,7 +47,7 @@ data for height bin containing the provided altitude is plotted.} \item{range_gate_filter}{Optional filtering of the range gates. By default range gates are filtered for linear reflectivity value (\code{eta}) less then 36000 cm^2/km^3 (the default in the profiling algorithm, see \code{etaMax} in \code{vol2bird::vol2bird_config()}) -and a correlation coefficient (\code{RHOHV}) < 0.95. The function selects the first reflectivity factor quantity +and a correlation coefficient (\code{RHOHV}) < 0.95. By default function selects the first reflectivity factor quantity from \code{DBZ}, \code{DBZH}, \code{DBZV}, \code{TH} or \code{TV} that is present. Alternative filters could be used to highlight specific effects.} \item{plotting_geom}{The geom function to visualize the range gates, the default is \code{\link[ggplot2:geom_point]{ggplot2::geom_point()}}. In cases From 1e4a4b654436f3ea14fa1bf707197bc4e2eca914 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 7 Oct 2025 14:16:42 +0200 Subject: [PATCH 24/26] check velocity quantity --- R/vad.R | 5 ++ tests/testthat/test-vad.R | 126 ++++++++++++++++++++++++++++---------- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/R/vad.R b/R/vad.R index 57e7358f1..7b3adbe1f 100644 --- a/R/vad.R +++ b/R/vad.R @@ -105,6 +105,7 @@ vad.pvol <- function( cosine_correction = c("none", "vp", "range_gates") ) { assertthat::assert_that(is.pvol(x)) + assertthat::assert_that(is.character(velocity_quantity)) cosine_correction <- rlang::arg_match(cosine_correction) # Some of the input variables are checked and modified specifically in the VP context if (!is.null(vp)) { @@ -183,6 +184,10 @@ vad.pvol <- function( dplyr::bind_rows() velocity_quantity <- velocity_quantity[velocity_quantity %in% names(data)][1] + assertthat::assert_that( + !is.na(velocity_quantity), + msg = "None of the specified velocity quantities could be found in the polar volume data." + ) # Filter the plotting data with the height and range, furthermore we omit NA's # and apply the range_gate_filter's data <- dplyr::filter( diff --git a/tests/testthat/test-vad.R b/tests/testthat/test-vad.R index 95e46b4f7..797f489da 100644 --- a/tests/testthat/test-vad.R +++ b/tests/testthat/test-vad.R @@ -1,16 +1,29 @@ pvolfile <- system.file("extdata", "volume.h5", package = "bioRad") pvol <- read_pvolfile(pvolfile) vp <- example_vp -scan<-example_scan +scan <- example_scan test_that("vad() errors on incorrect parameters", { - expect_error(vad(vp), "no applicable method for 'vad' applied to an object of class \"vp\"") + expect_error( + vad(pvol, velocity_quantity = 1:2), + "velocity_quantity is not a character vector" + ) + expect_error( + vad(pvol, velocity_quantity = c("VRAD", "VRADV")), + "None of the specified velocity quantities could be found in the polar volume data" + ) + + expect_error( + vad(vp), + "no applicable method for 'vad' applied to an object of class \"vp\"" + ) expect_error(vad(pvol, 1), "is.vp(x = vp) is not TRUE", fixed = T) expect_error( vad(pvol, range_max = "a"), "range_max is not a numeric or integer vector" ) - expect_error(vad(pvol, range_max = 1:2), + expect_error( + vad(pvol, range_max = 1:2), "range_max is not NULL or rlang::is_scalar_vector(x = range_max) is not TRUE", fixed = TRUE ) @@ -18,23 +31,28 @@ test_that("vad() errors on incorrect parameters", { vad(pvol, range_min = "a"), "range_min is not a numeric or integer vector" ) - expect_error(vad(pvol, range_min = 1:2), + expect_error( + vad(pvol, range_min = 1:2), "range_min is not NULL or rlang::is_scalar_vector(x = range_min) is not TRUE", fixed = TRUE ) expect_error( - vad(pvol, alt_max = "a"), "alt_max is not a numeric or integer vector" + vad(pvol, alt_max = "a"), + "alt_max is not a numeric or integer vector" ) expect_error( - vad(pvol, alt_max = 1:2), "alt_max is not NULL or rlang::is_scalar_vector(x = alt_max) is not TRUE", + vad(pvol, alt_max = 1:2), + "alt_max is not NULL or rlang::is_scalar_vector(x = alt_max) is not TRUE", fixed = TRUE ) expect_error( - vad(pvol, alt_min = "a"), "alt_min is not a numeric or integer vector" + vad(pvol, alt_min = "a"), + "alt_min is not a numeric or integer vector" ) expect_error( - vad(pvol, alt_min = 1:2), "alt_min is not NULL or rlang::is_scalar_vector(x = alt_min) is not TRUE", + vad(pvol, alt_min = 1:2), + "alt_min is not NULL or rlang::is_scalar_vector(x = alt_min) is not TRUE", fixed = TRUE ) expect_error( @@ -47,45 +65,87 @@ test_that("vad() errors on incorrect parameters", { ) }) test_that("plot from vad() matches some expectations", { - expect_s3_class(plt <- vad(pvol, - alt_min = 400, alt_max = 756, - range_min = 6953, range_max = 65344, - range_gate_filter= where.elangle<2 - ), "ggplot") + expect_s3_class( + plt <- vad( + pvol, + alt_min = 400, + alt_max = 756, + range_min = 6953, + range_max = 65344, + range_gate_filter = where.elangle < 2 + ), + "ggplot" + ) expect_s3_class(plt$facet, "FacetNull") expect_true(all(plt$data$height < 756)) expect_true(all(plt$data$height > 400)) expect_true(all(plt$data$range < 65344)) expect_true(all(plt$data$range > 6953)) - expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) - expect_length(plt$data$scan_nr|> unique(),sum(get_elevation_angles(pvol)<2)) - + expect_equal( + plt$mapping, + ggplot2::aes(x = azim, y = VRADH), + ignore_attr = TRUE + ) + expect_length( + plt$data$scan_nr |> unique(), + sum(get_elevation_angles(pvol) < 2) + ) }) test_that("plot from vad() matches some expectations", { expect_s3_class(plt <- vad(calculate_param(pvol, VRAD = VRADH), vp), "ggplot") expect_s3_class(plt$facet, "FacetWrap") - expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRAD), ignore_attr = TRUE) - expect_length(plt$data$scan_nr|> unique(),length(pvol$scans)) - + expect_equal( + plt$mapping, + ggplot2::aes(x = azim, y = VRAD), + ignore_attr = TRUE + ) + expect_length(plt$data$scan_nr |> unique(), length(pvol$scans)) }) test_that("plot from vad() matches some expectations for scans", { expect_s3_class(plt <- vad(scan, vp), "ggplot") - expect_equal(plt$mapping, ggplot2::aes(x = azim, y = VRADH), ignore_attr = TRUE) - expect_length(plt$data$scan_nr|> unique(),1) + expect_equal( + plt$mapping, + ggplot2::aes(x = azim, y = VRADH), + ignore_attr = TRUE + ) + expect_length(plt$data$scan_nr |> unique(), 1) }) -test_that("vad raises cosine correction warnings and is applied",{ - expect_warning(plt_rg<-vad(pvol, cosine_correction="range_gates", range_min=5000, range_max=25000),'There are data that have a relatively low nyquist velocity') - expect_warning(plt_vp<-vad(pvol,vp, cosine_correction="vp"),'The data to plot is based on multiple elevation angle') - expect_equal(plt_rg$data$VRADH, plt_vp$data$VRADH *1/cospi(plt_vp$data$where.elangle/180)) - expect_identical(plt_vp$layers[[2]]$stat_params$args$v,vp$data$ff[2]* cospi(mean(get_elevation_angles(pvol))/180)) - expect_identical(plt_vp$layers[[3]]$stat_params$args$a,vp$data$dd[3]) - pvol_tmp<-pvol - pvol_tmp$scans[[3]]$attributes$where$elangle<-20 - expect_warning(plt_none<-vad(pvol_tmp, vp),"Data with relatively large elevation angles are included.") +test_that("vad raises cosine correction warnings and is applied", { + expect_warning( + plt_rg <- vad( + pvol, + cosine_correction = "range_gates", + range_min = 5000, + range_max = 25000 + ), + 'There are data that have a relatively low nyquist velocity' + ) + expect_warning( + plt_vp <- vad(pvol, vp, cosine_correction = "vp"), + 'The data to plot is based on multiple elevation angle' + ) + expect_equal( + plt_rg$data$VRADH, + plt_vp$data$VRADH * 1 / cospi(plt_vp$data$where.elangle / 180) + ) + expect_identical( + plt_vp$layers[[2]]$stat_params$args$v, + vp$data$ff[2] * cospi(mean(get_elevation_angles(pvol)) / 180) + ) + expect_identical(plt_vp$layers[[3]]$stat_params$args$a, vp$data$dd[3]) + pvol_tmp <- pvol + pvol_tmp$scans[[3]]$attributes$where$elangle <- 20 + expect_warning( + plt_none <- vad(pvol_tmp, vp), + "Data with relatively large elevation angles are included." + ) expect_identical(plt_none$data$VRAD, plt_vp$data$VRADH) - expect_s3_class(plt_scn<-vad(scan, vp),'ggplot') - expect_equal(plt_scn$data, plt_none$data |> dplyr::filter(scan_nr==1)) - expect_identical(plt_scn$layers[[2]]$stat_params$args$v,vp$data$ff[2]* cospi((get_elevation_angles(scan))/180)) + expect_s3_class(plt_scn <- vad(scan, vp), 'ggplot') + expect_equal(plt_scn$data, plt_none$data |> dplyr::filter(scan_nr == 1)) + expect_identical( + plt_scn$layers[[2]]$stat_params$args$v, + vp$data$ff[2] * cospi((get_elevation_angles(scan)) / 180) + ) }) From 3d0b0529ee8fd362c4a32de93232c58d9ac6db39 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 7 Oct 2025 14:45:36 +0200 Subject: [PATCH 25/26] fix check error --- R/vad.R | 2 +- man/vad.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/vad.R b/R/vad.R index 7b3adbe1f..decce926c 100644 --- a/R/vad.R +++ b/R/vad.R @@ -69,7 +69,7 @@ #' vp = vp, #' alt_min = 400, #' plotting_geom_args = list(ggplot2::aes(color = DBZH)) -#' ) + viridis::scale_color_virids() +#' ) + ggplot2::scale_color_virids_c() #' vad(example_pvol, #' vp = vp, alt_min = 400, alt_max = 1200, #' plotting_geom = ggplot2::geom_bin2d, diff --git a/man/vad.Rd b/man/vad.Rd index f381d0fa6..8157be101 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -116,7 +116,7 @@ vad(example_pvol, vp = vp, alt_min = 400, plotting_geom_args = list(ggplot2::aes(color = DBZH)) -) + viridis::scale_color_virids() +) + ggplot2::scale_color_virids_c() vad(example_pvol, vp = vp, alt_min = 400, alt_max = 1200, plotting_geom = ggplot2::geom_bin2d, From bcaa1cb2864c36c50ed2b5d3c6aaa92c2e30f475 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 7 Oct 2025 14:59:42 +0200 Subject: [PATCH 26/26] typo --- R/vad.R | 4 ++-- man/vad.Rd | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/vad.R b/R/vad.R index decce926c..d772554d7 100644 --- a/R/vad.R +++ b/R/vad.R @@ -63,13 +63,13 @@ #' ) #' # It is also possible to plot one height or more height bins from a `vp`. #' # Many visual aspects can be controlled through the function arguments -#' # or through adding `ggplot` +#' # or through adding `ggplot2` functions #' vp <- calculate_vp(pvolfile) #' vad(example_pvol, #' vp = vp, #' alt_min = 400, #' plotting_geom_args = list(ggplot2::aes(color = DBZH)) -#' ) + ggplot2::scale_color_virids_c() +#' ) + ggplot2::scale_color_viridis_c() #' vad(example_pvol, #' vp = vp, alt_min = 400, alt_max = 1200, #' plotting_geom = ggplot2::geom_bin2d, diff --git a/man/vad.Rd b/man/vad.Rd index 8157be101..589c599a9 100644 --- a/man/vad.Rd +++ b/man/vad.Rd @@ -110,13 +110,13 @@ vad(example_pvol, ) # It is also possible to plot one height or more height bins from a `vp`. # Many visual aspects can be controlled through the function arguments -# or through adding `ggplot` +# or through adding `ggplot2` functions vp <- calculate_vp(pvolfile) vad(example_pvol, vp = vp, alt_min = 400, plotting_geom_args = list(ggplot2::aes(color = DBZH)) -) + ggplot2::scale_color_virids_c() +) + ggplot2::scale_color_viridis_c() vad(example_pvol, vp = vp, alt_min = 400, alt_max = 1200, plotting_geom = ggplot2::geom_bin2d,