diff --git a/CITATION.cff b/CITATION.cff index 5625ca1b..c901d9d2 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -8,7 +8,7 @@ message: 'To cite package "getRad" in publications use:' type: software license: MIT title: 'getRad: Download Radar Data for Biological Research' -version: 0.2.4 +version: 0.2.4.9000 doi: 10.32614/CRAN.package.getRad abstract: Load polar volume and vertical profile data for aeroecological research directly into R. With 'getRad' you can access data from several sources in Europe @@ -45,4 +45,477 @@ keywords: - r-package - radar - rstats +references: +- type: software + title: 'R: A Language and Environment for Statistical Computing' + notes: Depends + url: https://www.R-project.org/ + authors: + - name: R Core Team + institution: + name: R Foundation for Statistical Computing + address: Vienna, Austria + year: '2026' + version: '>= 4.1.0' +- type: software + title: bioRad + abstract: 'bioRad: Biological Analysis and Visualization of Weather Radar Data' + notes: Imports + url: https://github.com/adokter/bioRad/ + repository: https://CRAN.R-project.org/package=bioRad + authors: + - family-names: Dokter + given-names: Adriaan M. + email: biorad@cornell.edu + orcid: https://orcid.org/0000-0001-6573-066X + - family-names: Desmet + given-names: Peter + email: peter.desmet@inbo.be + orcid: https://orcid.org/0000-0002-8442-8025 + - family-names: Kranstauber + given-names: Bart + email: b.kranstauber@uva.nl + orcid: https://orcid.org/0000-0001-8303-780X + - family-names: Nilsson + given-names: Cecilia + email: cecilia.nilsson709@gmail.com + orcid: https://orcid.org/0000-0001-8957-4411 + - family-names: Tedeschi + given-names: Alexander + email: alexander.tedeschi@cornell.edu + orcid: https://orcid.org/0000-0003-0772-6931 + - family-names: Van Hoey + given-names: Stijn + email: stijnvanhoey@gmail.com + orcid: https://orcid.org/0000-0001-6413-3185 + year: '2026' + doi: 10.32614/CRAN.package.bioRad +- type: software + title: cachem + abstract: 'cachem: Cache R Objects with Automatic Pruning' + notes: Imports + url: https://cachem.r-lib.org/ + repository: https://CRAN.R-project.org/package=cachem + authors: + - family-names: Chang + given-names: Winston + email: winston@posit.co + year: '2026' + doi: 10.32614/CRAN.package.cachem +- type: software + title: cli + abstract: 'cli: Helpers for Developing Command Line Interfaces' + notes: Imports + url: https://cli.r-lib.org + repository: https://CRAN.R-project.org/package=cli + authors: + - family-names: Csárdi + given-names: Gábor + email: gabor@posit.co + year: '2026' + doi: 10.32614/CRAN.package.cli +- type: software + title: dplyr + abstract: 'dplyr: A Grammar of Data Manipulation' + notes: Imports + url: https://dplyr.tidyverse.org + repository: https://CRAN.R-project.org/package=dplyr + authors: + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + orcid: https://orcid.org/0000-0003-4757-117X + - family-names: François + given-names: Romain + orcid: https://orcid.org/0000-0002-2444-4226 + - family-names: Henry + given-names: Lionel + - family-names: Müller + given-names: Kirill + orcid: https://orcid.org/0000-0002-1416-3412 + - family-names: Vaughan + given-names: Davis + email: davis@posit.co + orcid: https://orcid.org/0000-0003-4777-038X + year: '2026' + doi: 10.32614/CRAN.package.dplyr + version: '>= 1.1.0' +- type: software + title: glue + abstract: 'glue: Interpreted String Literals' + notes: Imports + url: https://glue.tidyverse.org/ + repository: https://CRAN.R-project.org/package=glue + authors: + - family-names: Hester + given-names: Jim + orcid: https://orcid.org/0000-0002-2739-7082 + - family-names: Bryan + given-names: Jennifer + email: jenny@posit.co + orcid: https://orcid.org/0000-0002-6983-2759 + year: '2026' + doi: 10.32614/CRAN.package.glue +- type: software + title: httr2 + abstract: 'httr2: Perform HTTP Requests and Process the Responses' + notes: Imports + url: https://httr2.r-lib.org + repository: https://CRAN.R-project.org/package=httr2 + authors: + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + year: '2026' + doi: 10.32614/CRAN.package.httr2 + version: '>= 1.1.1' +- type: software + title: lubridate + abstract: 'lubridate: Make Dealing with Dates a Little Easier' + notes: Imports + url: https://lubridate.tidyverse.org + repository: https://CRAN.R-project.org/package=lubridate + authors: + - family-names: Spinu + given-names: Vitalie + email: spinuvit@gmail.com + - family-names: Grolemund + given-names: Garrett + - family-names: Wickham + given-names: Hadley + year: '2026' + doi: 10.32614/CRAN.package.lubridate +- type: software + title: purrr + abstract: 'purrr: Functional Programming Tools' + notes: Imports + url: https://purrr.tidyverse.org/ + repository: https://CRAN.R-project.org/package=purrr + authors: + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + orcid: https://orcid.org/0000-0003-4757-117X + - family-names: Henry + given-names: Lionel + email: lionel@posit.co + year: '2026' + doi: 10.32614/CRAN.package.purrr + version: '>= 1.0.0' +- type: software + title: rlang + abstract: 'rlang: Functions for Base Types and Core R and ''Tidyverse'' Features' + notes: Imports + url: https://rlang.r-lib.org + repository: https://CRAN.R-project.org/package=rlang + authors: + - family-names: Henry + given-names: Lionel + email: lionel@posit.co + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + year: '2026' + doi: 10.32614/CRAN.package.rlang +- type: software + title: tibble + abstract: 'tibble: Simple Data Frames' + notes: Imports + url: https://tibble.tidyverse.org/ + repository: https://CRAN.R-project.org/package=tibble + authors: + - family-names: Müller + given-names: Kirill + email: kirill@cynkra.com + orcid: https://orcid.org/0000-0002-1416-3412 + - family-names: Wickham + given-names: Hadley + email: hadley@rstudio.com + year: '2026' + doi: 10.32614/CRAN.package.tibble +- type: software + title: tools + abstract: 'R: A Language and Environment for Statistical Computing' + notes: Imports + authors: + - name: R Core Team + institution: + name: R Foundation for Statistical Computing + address: Vienna, Austria + year: '2026' +- type: software + title: utils + abstract: 'R: A Language and Environment for Statistical Computing' + notes: Imports + authors: + - name: R Core Team + institution: + name: R Foundation for Statistical Computing + address: Vienna, Austria + year: '2026' +- type: software + title: vroom + abstract: 'vroom: Read and Write Rectangular Text Data Quickly' + notes: Imports + url: https://vroom.tidyverse.org + repository: https://CRAN.R-project.org/package=vroom + authors: + - family-names: Hester + given-names: Jim + orcid: https://orcid.org/0000-0002-2739-7082 + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + orcid: https://orcid.org/0000-0003-4757-117X + - family-names: Bryan + given-names: Jennifer + email: jenny@posit.co + orcid: https://orcid.org/0000-0002-6983-2759 + year: '2026' + doi: 10.32614/CRAN.package.vroom +- type: software + title: withr + abstract: 'withr: Run Code ''With'' Temporarily Modified Global State' + notes: Imports + url: https://withr.r-lib.org + repository: https://CRAN.R-project.org/package=withr + authors: + - family-names: Hester + given-names: Jim + - family-names: Henry + given-names: Lionel + email: lionel@posit.co + - family-names: Müller + given-names: Kirill + email: krlmlr+r@mailbox.org + - family-names: Ushey + given-names: Kevin + email: kevinushey@gmail.com + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + - family-names: Chang + given-names: Winston + year: '2026' + doi: 10.32614/CRAN.package.withr +- type: software + title: xml2 + abstract: 'xml2: Parse XML' + notes: Imports + url: https://xml2.r-lib.org + repository: https://CRAN.R-project.org/package=xml2 + authors: + - family-names: Wickham + given-names: Hadley + - family-names: Hester + given-names: Jim + - family-names: Ooms + given-names: Jeroen + email: jeroenooms@gmail.com + year: '2026' + doi: 10.32614/CRAN.package.xml2 +- type: software + title: askpass + abstract: 'askpass: Password Entry Utilities for R, Git, and SSH' + notes: Suggests + url: https://r-lib.r-universe.dev/askpass + repository: https://CRAN.R-project.org/package=askpass + authors: + - family-names: Ooms + given-names: Jeroen + email: jeroenooms@gmail.com + orcid: https://orcid.org/0000-0002-4035-0289 + year: '2026' + doi: 10.32614/CRAN.package.askpass +- type: software + title: htmltools + abstract: 'htmltools: Tools for HTML' + notes: Suggests + url: https://rstudio.github.io/htmltools/ + repository: https://CRAN.R-project.org/package=htmltools + authors: + - family-names: Cheng + given-names: Joe + email: joe@posit.co + - family-names: Sievert + given-names: Carson + email: carson@posit.co + orcid: https://orcid.org/0000-0002-4958-2844 + - family-names: Schloerke + given-names: Barret + email: barret@posit.co + orcid: https://orcid.org/0000-0001-9986-114X + - family-names: Chang + given-names: Winston + email: winston@posit.co + orcid: https://orcid.org/0000-0002-1576-2126 + - family-names: Xie + given-names: Yihui + email: yihui@posit.co + - family-names: Allen + given-names: Jeff + year: '2026' + doi: 10.32614/CRAN.package.htmltools +- type: software + title: keyring + abstract: 'keyring: Access the System Credential Store from R' + notes: Suggests + url: https://keyring.r-lib.org/ + repository: https://CRAN.R-project.org/package=keyring + authors: + - family-names: Csárdi + given-names: Gábor + email: csardi.gabor@gmail.com + year: '2026' + doi: 10.32614/CRAN.package.keyring +- type: software + title: knitr + abstract: 'knitr: A General-Purpose Package for Dynamic Report Generation in R' + notes: Suggests + url: https://yihui.org/knitr/ + repository: https://CRAN.R-project.org/package=knitr + authors: + - family-names: Xie + given-names: Yihui + email: xie@yihui.name + orcid: https://orcid.org/0000-0003-0645-5666 + year: '2026' + doi: 10.32614/CRAN.package.knitr +- type: software + title: leaflet + abstract: 'leaflet: Create Interactive Web Maps with the JavaScript ''Leaflet'' + Library' + notes: Suggests + url: https://rstudio.github.io/leaflet/ + repository: https://CRAN.R-project.org/package=leaflet + authors: + - family-names: Cheng + given-names: Joe + email: joe@posit.co + - family-names: Schloerke + given-names: Barret + email: barret@posit.co + orcid: https://orcid.org/0000-0001-9986-114X + - family-names: Karambelkar + given-names: Bhaskar + - family-names: Xie + given-names: Yihui + - family-names: Aden-Buie + given-names: Garrick + email: garrick@posit.co + orcid: https://orcid.org/0000-0002-7111-0077 + year: '2026' + doi: 10.32614/CRAN.package.leaflet +- type: software + title: rhdf5 + abstract: 'rhdf5: R Interface to HDF5' + notes: Suggests + url: https://github.com/Huber-group-EMBL/rhdf5 + repository: https://bioconductor.org/ + authors: + - family-names: Fischer + given-names: Bernd + - family-names: Smith + given-names: Mike + email: mike.smith@embl.de + orcid: https://orcid.org/0000-0002-7800-3848 + - family-names: Pau + given-names: Gregoire + year: '2026' + doi: 10.18129/B9.bioc.rhdf5 +- type: software + title: rnaturalearth + abstract: 'rnaturalearth: World Map Data from Natural Earth' + notes: Suggests + url: https://docs.ropensci.org/rnaturalearth/ + repository: https://CRAN.R-project.org/package=rnaturalearth + authors: + - family-names: Massicotte + given-names: Philippe + email: pmassicotte@hotmail.com + orcid: https://orcid.org/0000-0002-5919-4116 + - family-names: South + given-names: Andy + email: southandy@gmail.com + year: '2026' + doi: 10.32614/CRAN.package.rnaturalearth +- type: software + title: rnaturalearthdata + abstract: 'rnaturalearthdata: World Vector Map Data from Natural Earth Used in ''rnaturalearth''' + notes: Suggests + url: https://docs.ropensci.org/rnaturalearthdata/ + repository: https://CRAN.R-project.org/package=rnaturalearthdata + authors: + - family-names: South + given-names: Andy + email: southandy@gmail.com + orcid: https://orcid.org/0000-0003-4051-6135 + - family-names: Michael + given-names: Schramm + email: mpschramm@gmail.com + - family-names: Massicotte + given-names: Philippe + email: pmassicotte@hotmail.com + orcid: https://orcid.org/0000-0002-5919-4116 + year: '2026' + doi: 10.32614/CRAN.package.rnaturalearthdata +- type: software + title: sf + abstract: 'sf: Simple Features for R' + notes: Suggests + url: https://r-spatial.github.io/sf/ + repository: https://CRAN.R-project.org/package=sf + authors: + - family-names: Pebesma + given-names: Edzer + email: edzer.pebesma@uni-muenster.de + orcid: https://orcid.org/0000-0001-8049-7069 + year: '2026' + doi: 10.32614/CRAN.package.sf +- type: software + title: testthat + abstract: 'testthat: Unit Testing for R' + notes: Suggests + url: https://testthat.r-lib.org + repository: https://CRAN.R-project.org/package=testthat + authors: + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + year: '2026' + doi: 10.32614/CRAN.package.testthat + version: '>= 3.0.0' +- type: software + title: tidyr + abstract: 'tidyr: Tidy Messy Data' + notes: Suggests + url: https://tidyr.tidyverse.org + repository: https://CRAN.R-project.org/package=tidyr + authors: + - family-names: Wickham + given-names: Hadley + email: hadley@posit.co + - family-names: Vaughan + given-names: Davis + email: davis@posit.co + - family-names: Girlich + given-names: Maximilian + year: '2026' + doi: 10.32614/CRAN.package.tidyr +- type: software + title: vol2birdR + abstract: 'vol2birdR: Vertical Profiles of Biological Signals in Weather Radar Data' + notes: Suggests + url: https://github.com/adokter/vol2birdR/ + repository: https://CRAN.R-project.org/package=vol2birdR + authors: + - family-names: Henja + given-names: Anders + email: anders.henja@gmail.com + - family-names: Dokter + given-names: Adriaan M. + email: vol2birdr@cornell.edu + orcid: https://orcid.org/0000-0001-6573-066X + year: '2026' + doi: 10.32614/CRAN.package.vol2birdR diff --git a/DESCRIPTION b/DESCRIPTION index c025fd2d..23b3d96f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -65,8 +65,8 @@ VignetteBuilder: Config/testthat/edition: 3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.2 Config/Needs/website: rmarkdown, leafpop, htmltools +Config/roxygen2/version: 8.0.0 diff --git a/NAMESPACE b/NAMESPACE index 365c657c..46be60d9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,3 +8,4 @@ export(get_weather_radars) export(set_secret) importFrom(dplyr,.data) importFrom(lubridate,"%within%") +importFrom(rlang,.env) diff --git a/NEWS.md b/NEWS.md index 4e051dda..628027f4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,7 +1,12 @@ # getRad (development version) +* Implement download of `vpts` data from birdcast by Alexander Tedeschi. * Implement reading `vpts` data from a local directory (#135). * Clarify HTTP 429 error for the Netherlands (#165). +* Clarify error for Estonia and propagate call (#173). +* Include the hochficht radar in Austria (#169). +* For Romania match deviating file format (thanks to Sorin Burcea, #167). +* Fix download for Czechia as multiple scans were found (#183). # getRad 0.2.4 diff --git a/R/getRad-package.R b/R/getRad-package.R index 0c6cd517..7036fb97 100644 --- a/R/getRad-package.R +++ b/R/getRad-package.R @@ -3,6 +3,7 @@ ## usethis namespace: start #' @importFrom dplyr .data +#' @importFrom rlang .env #' @importFrom lubridate %within% ## usethis namespace: end NULL diff --git a/R/get_pvol.R b/R/get_pvol.R index 95091f0a..132a64f1 100644 --- a/R/get_pvol.R +++ b/R/get_pvol.R @@ -38,7 +38,9 @@ #' as.POSIXct(Sys.Date()) #' ) get_pvol <- function(radar = NULL, datetime = NULL, ...) { - check_odim_nexrad(radar) + if (!identical(radar, "hochficht")) { + check_odim_nexrad(setdiff(radar, 'hochficht')) + } if (anyDuplicated(radar)) { cli::cli_abort( "{.arg radar} contains duplications that must be removed.", @@ -129,9 +131,13 @@ get_pvol <- function(radar = NULL, datetime = NULL, ...) { # Helper function to find the function for a specific radar # This function is only helpful in get_pvol and therefor not in a utils file select_get_pvol_function <- function(radar, ..., call = rlang::caller_env()) { + if (radar == "hochficht") { + return("get_pvol_hochficht") + } if (is_nexrad(radar)) { return("get_pvol_us") } + cntry_code <- substr(radar, 1, 2) # nolint fun <- (dplyr::case_when( cntry_code == "nl" ~ "get_pvol_nl", diff --git a/R/get_pvol_at.R b/R/get_pvol_at.R new file mode 100644 index 00000000..287371d0 --- /dev/null +++ b/R/get_pvol_at.R @@ -0,0 +1,29 @@ +get_pvol_hochficht <- function(radar, time, ..., call = rlang::caller_env()) { + url <- glue::glue( + getOption( + "getRad.at_hochficht_url", + default = "https://public.hub.geosphere.at/datahub/resources/radar_volumen_hochficht-v1-5min/filelisting/WXRHOF_{strftime(time,'%Y%m%d%H%M', tz='UTC')}.hdf" + ) + ) + pvol <- withr::with_tempfile("file", fileext = ".h5", { + tryCatch( + req <- + httr2::request(url) |> + req_user_agent_getrad() |> + httr2::req_perform(path = file, error_call = call), + httr2_http_403 = function(cnd) { + cli::cli_abort( + c( + "There is data found for download.", + "i" = "Data for the Hochficht radar is only available for the last three days." + ), + cnd = cnd, + class = "getRad_error_get_pvol_at_no_data", + call = call + ) + } + ) + bioRad::read_pvolfile(file, ...) + }) + return(pvol) +} diff --git a/R/get_pvol_cz.R b/R/get_pvol_cz.R index f050fafe..ab696631 100644 --- a/R/get_pvol_cz.R +++ b/R/get_pvol_cz.R @@ -1,7 +1,7 @@ # http://opendata.chmi.cz/meteorology/weather/radar/sites/ska/vol_z/hdf5/ get_pvol_cz <- function(radar, time, ..., call = rlang::caller_env()) { - time_chr <- time_pos <- base <- resp <- NULL + time_chr <- time_pos <- base <- NULL # All parameters are retrieved from separate files # Here all urls are generated params <- c("z", "u", "v", "w", "zdr", "rhohv", "phidp") @@ -37,10 +37,55 @@ get_pvol_cz <- function(radar, time, ..., call = rlang::caller_env()) { time, time + lubridate::minutes(5) ) - )) - pvol <- read_pvol_from_url_per_param(paste0( - files_to_get$base, - files_to_get$file + )) |> + dplyr::mutate(url = paste0(base, file)) + # There are sometimes multiple pvols, generally one with 12 scans + # additionally there are (sometimes) scans in separate files for + # the 0.3 and 1.5 elevation angle. These are not always both present. + # It all seems a bit irregular + pvols <- lapply( + split(files_to_get$url, files_to_get$time_chr), + read_pvol_from_url_per_param + ) + if (length(pvols) == 1) { + return(pvols[[1]]) + } + attr <- purrr::map(pvols, purrr::pluck, "attributes") |> + purrr::map(purrr::assign_in, where = c("what", "time"), purrr::zap()) |> + purrr::map(purrr::assign_in, where = c("what", "date"), purrr::zap()) |> + purrr::map(purrr::assign_in, where = c("how", "scan_count"), purrr::zap()) + if (!all(unlist(lapply(attr[-1], identical, attr[[1]])))) { + cli::cli_abort( + c( + "The attributes of the polar volumes about to be merged differ" + ), + class = "getRad_error_czechia_attributes_differ", + call = call + ) + } + pvol <- purrr::pluck(pvols, 1) + pvol$scans <- purrr::list_flatten(purrr::map(pvols, "scans")) + pvol$attributes$how$scan_count <- length(pvol$scans) + pvol$datetime <- max(purrr::map_vec(pvols, "datetime")) + pvol$attributes$what$time <- max(purrr::map_vec( + pvols, + c("attributes", "what", "time") + )) + pvol$attributes$what$date <- max(purrr::map_vec( + pvols, + c("attributes", "what", "date") )) - pvol + if (anyDuplicated(get_elevation_angles(pvol))) { + # Note that if scanning pattern changes this might flag false positive, but + # currently the check is there to prevent falsely merging scans from two iterations + # of the scanning pattern + cli::cli_abort( + c( + "There are duplicated elevation angles, likely as a result of merging the wrong scans." + ), + class = "getRad_error_czechia_duplicated_elevation_angles", + call = call + ) + } + return(pvol) } diff --git a/R/get_pvol_ee.R b/R/get_pvol_ee.R index 5414404c..a62a1664 100644 --- a/R/get_pvol_ee.R +++ b/R/get_pvol_ee.R @@ -71,8 +71,9 @@ get_pvol_ee <- function(radar, time, ..., call = rlang::caller_env()) { httr2::resp_body_json() if (files$numFound == 0 || length(files$documents) != 1) { cli::cli_abort( - "The expected number of files is not found.", - class = "getRad_error_get_pvol_ee_differing_n_files" + "The expected number of files is not found for this Estonian Radar. This can occur when no data is available.", + class = "getRad_error_get_pvol_ee_differing_n_files", + call = call ) } pvol <- withr::with_tempfile("file", fileext = ".h5", { diff --git a/R/get_pvol_ro.R b/R/get_pvol_ro.R index bc52ff81..6a7d0e63 100644 --- a/R/get_pvol_ro.R +++ b/R/get_pvol_ro.R @@ -3,5 +3,19 @@ get_pvol_ro <- function(radar, time, ..., call = rlang::caller_env()) { urls <- glue::glue( "https://opendata.meteoromania.ro/radar/{toupper(substr(radar,3,5))}/{toupper(substr(radar,3,5))}_{strftime(time,'%Y%m%d%H%M', tz='UTC')}0200{params}.hdf" ) - read_pvol_from_url_per_param(urls, param = "all", call = call) + tryCatch( + read_pvol_from_url_per_param(urls, param = "all", call = call), + error = function(cnd) { + urls_updated <- glue::glue( + "https://opendata.meteoromania.ro/radar/{toupper(substr(radar,3,5))}/{toupper(substr(radar,3,5))}_{strftime(time,'%Y%m%d%H%M', tz='UTC')}0300{params}.hdf" + ) + if ( + rlang::has_name(cnd, "parent") && inherits(cnd$parent, "httr2_http_404") + ) { + read_pvol_from_url_per_param(urls_updated, param = "all", call = call) + } else { + return(cnd) + } + } + ) } diff --git a/R/get_vpts.R b/R/get_vpts.R index 9b3826c0..6fc3707d 100644 --- a/R/get_vpts.R +++ b/R/get_vpts.R @@ -34,8 +34,8 @@ #' - A vector of datetimes or dates, between which all data files are #' downloaded. #' - A [lubridate::interval()], between which all data files are downloaded. -#' @param source Source of the data. One of `"baltrad"`, `"uva"`, `"ecog-04003"` -#' or `"rmi"`. Only one source can be queried at a time. If not provided, +#' @param source Source of the data. One of `"baltrad"`, `"uva"`, `"ecog-04003"`, +#' `"rmi"`, or `"birdcast"`. Only one source can be queried at a time. If not provided, #' `"baltrad"` is used. Alternatively a local directory can be specified, #' see details for an explanation of the file format. #' @param return_type Type of object that should be returned. Either: @@ -77,10 +77,12 @@ #' source = "baltrad", #' return_type = "tibble" #' ) +#' #' Get VPTS data from the public BirdCast NEXRAD archive +#' get_vpts(radar = "KABR", datetime = "2023-01-01", source = "birdcast") get_vpts <- function( radar, datetime, - source = c("baltrad", "uva", "ecog-04003", "rmi"), + source = c("baltrad", "uva", "ecog-04003", "rmi", "birdcast"), return_type = c("vpts", "tibble") ) { # Input checks ---- @@ -205,14 +207,19 @@ get_vpts <- function( # Query the selected radars ---- # Directing to the correct get_vpts_* helper based on source. cl <- rlang::caller_env(0) + + aloft_sources <- eval(formals("get_vpts_aloft")$source) + + source_type <- dplyr::case_when( + source == "rmi" ~ "rmi", + source == "birdcast" ~ "birdcast", + source %in% aloft_sources ~ "aloft", + dir.exists(source) ~ "local" + ) + fetched_vpts <- switch( - dplyr::case_when( - source == "rmi" ~ "rmi", - source %in% eval(formals("get_vpts_aloft")$source) ~ "aloft", - # this is the last option to avoid using a local source if an online exists - dir.exists(source) ~ "local" - ), + source_type, rmi = purrr::map( radar, ~ get_vpts_rmi(.x, rounded_interval), @@ -227,6 +234,14 @@ get_vpts <- function( ), .purrr_error_call = cl ), + birdcast = purrr::map( + radar, + ~ get_vpts_birdcast( + .x, + rounded_interval = rounded_interval + ), + .purrr_error_call = cl + ), local = get_vpts_local(radar, rounded_interval, directory = source) ) |> radar_to_name() diff --git a/R/get_vpts_aloft.R b/R/get_vpts_aloft.R index 29a51b5a..b41fc839 100644 --- a/R/get_vpts_aloft.R +++ b/R/get_vpts_aloft.R @@ -25,12 +25,14 @@ #' @param coverage A data frame containing the coverage of the Aloft bucket. #' If not provided, it will be fetched from via the internet. #' @return A tibble with VPTS data. -#' @keywords internal +#' @noRd get_vpts_aloft <- function( radar_odim_code, rounded_interval, source = c("baltrad", "uva", "ecog-04003"), - coverage = get_vpts_coverage_aloft() + coverage = get_vpts_coverage_aloft(), + ..., + call = rlang::caller_env() ) { # rename source argument for readability selected_source <- source @@ -45,7 +47,8 @@ get_vpts_aloft <- function( "Can't find radar {.val {missing_radar}} in the coverage file (see {.fun get_vpts_coverage}).", missing_radar = missing_radar, - class = "getRad_error_aloft_radar_not_found" + class = "getRad_error_aloft_radar_not_found", + call = call ) } @@ -61,7 +64,8 @@ get_vpts_aloft <- function( if (!at_least_one_radar_date_combination_exists) { cli::cli_abort( "Can't find any data for the requested radar(s) and date(s).", - class = "getRad_error_date_not_found" + class = "getRad_error_date_not_found", + call = call ) } @@ -79,7 +83,8 @@ get_vpts_aloft <- function( cli::cli_abort( "Can't find radar{?s} {.val {missing_radars}} in the coverage file (see {.fun get_vpts_coverage}).", - class = "getRad_error_radar_not_found" + class = "getRad_error_radar_not_found", + call = call ) } diff --git a/R/get_vpts_birdcast.R b/R/get_vpts_birdcast.R new file mode 100644 index 00000000..4cf428dd --- /dev/null +++ b/R/get_vpts_birdcast.R @@ -0,0 +1,95 @@ +#' Get VPTS data from the public BirdCast NEXRAD archive +#' +#' Gets VPTS data from the public BirdCast NEXRAD archive. +#' +#' @details +#' By default, data are retrieved from the public BirdCast S3 archive at +#' `https://birdcastdata.s3.amazonaws.com/nexrad/daily`. +#' +#' The expected path format is: +#' `"{radar}/{year}/{radar}_vpts_{year}{month}{day}.csv"`. +#' +#' @section Inner working: +#' - Checks that the requested radar is present in the NEXRAD coverage file. +#' - Checks that data exist for the requested radar/date combination. +#' - Constructs the S3 paths for the daily VPTS files from the coverage file. +#' - Performs parallel HTTP requests to fetch the VPTS CSV data. +#' - Parses the response bodies with the shared VPTS column classes. +#' - Uses uppercase NEXRAD radar codes for archive paths. +#' - Adds a column with the radar source. +#' +#' @param radar NEXRAD radar code. +#' @param rounded_interval Interval to fetch data for, rounded to nearest day. +#' @param coverage A data frame containing the coverage of the BirdCast NEXRAD +#' archive. If not provided, it will be fetched via the internet. +#' @param ... Used to prevent accidentally using the `call` argument +#' @param call A call used for error messaging. +#' @return A tibble with VPTS data. +#' @noRd +get_vpts_birdcast <- function( + radar, + rounded_interval, + coverage = get_vpts_coverage_birdcast(), + ..., + call = rlang::caller_env() +) { + radar <- toupper(radar) + + # Check that only one radar is provided. + check_odim_nexrad_scalar(radar) + + # Check if the requested radar is present in the coverage. + if (!all(radar %in% coverage$radar)) { + missing_radar <- radar[!radar %in% coverage$radar] + + cli::cli_abort( + "Can't find radar {.val {missing_radar}} in the birdcast coverage file + (see {.fun get_vpts_coverage}).", + missing_radar = missing_radar, + class = "getRad_error_birdcast_radar_not_found", + call = call + ) + } + + # Check if the requested radar/date combination is present in the coverage. + filtered_coverage <- dplyr::filter( + coverage, + .data$radar %in% .env$radar, + .data$date %within% rounded_interval + ) + + if (nrow(filtered_coverage) == 0) { + cli::cli_abort( + "Can't find any data for the requested radar(s) and date(s).", + class = "getRad_error_date_not_found", + call = call + ) + } + + # Convert the selected coverage rows into paths on the BirdCast NEXRAD archive. + s3_paths <- filtered_coverage |> + dplyr::mutate( + path = glue::glue( + "{radar}/{year}/{radar}_vpts_{year}{month}{day}.csv", + radar = .data$radar, + year = lubridate::year(.data$date), + month = sprintf("%02d", lubridate::month(.data$date)), + day = sprintf("%02d", lubridate::day(.data$date)) + ) + ) |> + dplyr::pull(.data$path) + + # Read the VPTS CSV files. + birdcast_data_url <- getOption("getRad.birdcast_vpts_data_url") + radar_out <- tolower(radar) + + out <- paste(birdcast_data_url, "nexrad", "daily", s3_paths, sep = "/") |> + read_vpts_from_url() |> + purrr::keep(.p = ~ as.logical(nrow(.x))) |> + purrr::list_rbind() + + out$radar <- radar_out + out$source <- "birdcast" + + out +} diff --git a/R/get_vpts_coverage.R b/R/get_vpts_coverage.R index 303aa660..e9f94851 100644 --- a/R/get_vpts_coverage.R +++ b/R/get_vpts_coverage.R @@ -3,7 +3,7 @@ #' Gets the VPTS file coverage from supported sources per radar and date. #' #' @param source Source of the data. One or more of `"baltrad"`, `"uva"`, -#' `"ecog-04003"` or `"rmi"`. If not provided, `"baltrad"` is used. +#' `"ecog-04003"` or `"rmi"` or `"birdcast"`. If not provided, `"baltrad"` is used. #' Alternatively `"all"` can be used if data from all sources should be #' returned. #' @param ... Arguments passed on to internal functions. @@ -13,7 +13,7 @@ #' @examplesIf interactive() #' get_vpts_coverage() get_vpts_coverage <- function( - source = c("baltrad", "uva", "ecog-04003", "rmi"), + source = c("baltrad", "uva", "ecog-04003", "rmi", "birdcast"), ... ) { # argument all returns all possible sources @@ -40,7 +40,8 @@ get_vpts_coverage <- function( rmi = get_vpts_coverage_rmi, baltrad = get_vpts_coverage_aloft, uva = get_vpts_coverage_aloft, - "ecog-04003" = get_vpts_coverage_aloft + "ecog-04003" = get_vpts_coverage_aloft, + birdcast = get_vpts_coverage_birdcast ) cl <- rlang::caller_env(0) # Run the helpers, but every helper only once. diff --git a/R/get_vpts_coverage_aloft.R b/R/get_vpts_coverage_aloft.R index 1ad43f86..6c29b96d 100644 --- a/R/get_vpts_coverage_aloft.R +++ b/R/get_vpts_coverage_aloft.R @@ -25,7 +25,6 @@ get_vpts_coverage_aloft <- function( req_user_agent_getrad() |> req_retry_getrad() |> req_cache_getrad(use_cache = use_cache) |> - httr2::req_progress(type = "down") |> httr2::req_perform(error_call = call) |> httr2::resp_body_raw() diff --git a/R/get_vpts_coverage_birdcast.R b/R/get_vpts_coverage_birdcast.R new file mode 100644 index 00000000..208cd476 --- /dev/null +++ b/R/get_vpts_coverage_birdcast.R @@ -0,0 +1,49 @@ +#' Get VPTS file coverage from the public BirdCast NEXRAD archive +#' +#' Gets the VPTS file coverage from the public BirdCast NEXRAD archive. This is +#' derived from a coverage file at +#' <`r file.path(getOption("getRad.birdcast_vpts_data_url"), "coverage.csv")`>. By +#' default this file is cached for 6 hours. +#' +#' @param ... Used to prevent accidentally using the `call` argument +#' @param call A call used for error messaging. +#' @inheritParams req_cache_getrad +#' @return A data frame of the coverage file in the birdcast VPTS archive. +#' @noRd +#' @examplesIf interactive() +#' get_vpts_coverage_birdcast() +get_vpts_coverage_birdcast <- function( + use_cache = TRUE, + ..., + call = rlang::caller_env() +) { + birdcast_vpts_data_url <- getOption("getRad.birdcast_vpts_data_url") + + coverage_raw <- + httr2::request(birdcast_vpts_data_url) |> + httr2::req_url_path_append("coverage.csv") |> + req_user_agent_getrad() |> + req_retry_getrad() |> + req_cache_getrad(use_cache = use_cache) |> + httr2::req_perform(error_call = call) |> + httr2::resp_body_raw() + + coverage <- + vroom::vroom( + coverage_raw, + progress = FALSE, + show_col_types = FALSE + ) |> + dplyr::mutate( + source = "birdcast", + radar = string_extract(.data$directory, "(?<=daily\\/)[A-Z0-9]{4}"), + date = as.Date( + string_extract( + .data$directory, + "[0-9]{4}\\/[0-9]{2}\\/[0-9]{2}$" + ) + ) + ) + + return(coverage) +} diff --git a/R/get_vpts_rmi.R b/R/get_vpts_rmi.R index 6dc2a0bb..f6a053e8 100644 --- a/R/get_vpts_rmi.R +++ b/R/get_vpts_rmi.R @@ -5,7 +5,7 @@ #' #' @inheritParams get_vpts_aloft #' @return A tibble with VPTS data. -#' @keywords internal +#' @noRd get_vpts_rmi <- function(radar_odim_code, rounded_interval) { # Check the coverage for data availability diff --git a/R/get_weather_radars.R b/R/get_weather_radars.R index 14051aef..1b7ed82f 100644 --- a/R/get_weather_radars.R +++ b/R/get_weather_radars.R @@ -15,9 +15,11 @@ #' from. #' - For `nexrad`: [nexrad-stations.txt](https://www.ncei.noaa.gov/access/homr/file/nexrad-stations.txt). #' -#' @inheritParams req_cache_getrad #' @param source Source of the metadata. `"opera"`, `"nexrad"` or `"all"`. #' If not provided, `"opera"` is used. +#' @param use_cache Logical indicating whether to use the cache. Default is +#' `TRUE`. If `FALSE` the cache is ignored and the file is fetched anew. +#' This can also be useful if you want to force a refresh of the cache. #' @param ... Additional arguments passed on to reading functions per source, #' currently not used. #' @return A sf or tibble with weather radar metadata. In all cases the column `source` is diff --git a/R/utils.R b/R/utils.R index c765c5b2..e2c729c8 100644 --- a/R/utils.R +++ b/R/utils.R @@ -183,7 +183,6 @@ radar_to_name <- function(vpts_df_list) { #' #' @param x Character vector. #' @return An integer vector. -#' @seealso [as_numeric_shh()] [as_logical_shh()] #' @noRd #' @examples #' as_integer_shh(c("1", "2", "3")) @@ -278,11 +277,9 @@ req_retry_getrad <- function( #' #' @inheritParams httr2::req_cache #' @param req `httr2` request. -#' @param use_cache Logical indicating whether to use the cache. Default is -#' `TRUE`. If `FALSE` the cache is ignored and the file is fetched anew. -#' This can also be useful if you want to force a refresh of the cache. +#' @inheritParams get_weather_radars use_cache #' @param ... Additional arguments passed to `httr2::req_cache()`. -#' @keywords internal +#' @noRd req_cache_getrad <- function( req, use_cache = TRUE, @@ -524,3 +521,11 @@ get_element_regex <- function(html, regex) { string_extract(regex) |> (\(vec) vec[!is.na(vec)])() } + +#' Wrapper of bioRad::get_elevation_angles +#' This function is wrapped so it can be mocked in +#' `testhat::with_mocked_bindings()` and thus allows for testing an error +#' in`get_pvol_cz()`. +#' +#' @noRd +get_elevation_angles <- bioRad::get_elevation_angles diff --git a/R/utils_pvol.R b/R/utils_pvol.R index dc7ff835..c238180f 100644 --- a/R/utils_pvol.R +++ b/R/utils_pvol.R @@ -5,7 +5,7 @@ #' #' @param urls A character vector with urls to h5 files to read #' @param ... arguments to bioRad::read_pvolfile -#' @param call +#' @inheritParams radar_recode call #' #' @returns a pvol #' @noRd @@ -42,9 +42,12 @@ read_pvol_from_url_per_param <- function( bioRad::attribute_table ) all_params_same_attributes <- all(unlist(lapply( - lapply(list_of_attribute_tables[-1], dplyr::select, -"param"), - all.equal, - dplyr::select(list_of_attribute_tables[[1]], -"param") + lapply( + lapply(list_of_attribute_tables[-1], dplyr::select, -"param"), + all.equal, + dplyr::select(list_of_attribute_tables[[1]], -"param") + ), + isTRUE ))) if (!all_params_same_attributes) { cli::cli_abort( diff --git a/R/zzz.R b/R/zzz.R index c5bfef36..bd016b2f 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -21,6 +21,7 @@ ), getRad.aloft_data_url = "https://aloftdata.s3-eu-west-1.amazonaws.com", getRad.nexrad_data_url = "https://unidata-nexrad-level2.s3.amazonaws.com", + getRad.birdcast_vpts_data_url = "https://birdcastdata.s3.amazonaws.com", getRad.cache = cachem::cache_mem( max_size = 128 * 1024^2, max_age = 60^2 * 24 diff --git a/README.Rmd b/README.Rmd index c5c38587..91a4f816 100644 --- a/README.Rmd +++ b/README.Rmd @@ -29,17 +29,17 @@ getRad is an R package that provides a unified interface to download radar data ## Installation -Install the latest released version from CRAN: +Install the released version of getRad from CRAN: -```{r, eval = FALSE} +``` r install.packages("getRad") ``` -Or the development version from [GitHub](https://github.com/aloftdata/getRad): +Or install the development version from [GitHub](https://github.com/) with: -```{r, eval = FALSE} -# install.packages("devtools") -devtools::install_github("aloftdata/getRad") +``` r +# install.packages("pak") +pak::pak("aloftdata/getRad") ``` ## Usage @@ -49,6 +49,7 @@ Download a polar volume, and then plot it using `bioRad`: ```{r example} library(getRad) library(bioRad) + # Plot daytime insect movements in Finland (Mäkinen et al. 2022) pvol <- get_pvol("fianj", as.POSIXct("2012-05-17 14:00", tz = "UTC")) plot(project_as_ppi(get_scan(pvol, 0), range_max = 75000)) @@ -80,7 +81,7 @@ for (i in names(vpts_list)) { ## Meta -- We welcome [contributions](https://aloftdata.github.io/getRad/CONTRIBUTING.html) including bug reports. +- We welcome [contributions](.github/CONTRIBUTING.md) including bug reports. - License: MIT -- Get [citation information](https://aloftdata.github.io/getRad/authors.html#citation) for getRad in R doing `citation("getRad")`. -- Please note that this project is released with a [Contributor Code of Conduct](https://aloftdata.github.io/getRad/CODE_OF_CONDUCT.html). By participating in this project you agree to abide by its terms. +- Get citation information for getRad in R with `citation("getRad")`. +- Please note that this project is released with a [Contributor Code of Conduct](.github/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. diff --git a/README.md b/README.md index 3036594b..80ee2117 100644 --- a/README.md +++ b/README.md @@ -25,18 +25,18 @@ exploration of the data by other tools such as ## Installation -Install the latest released version from CRAN: +Install the released version of getRad from CRAN: ``` r install.packages("getRad") ``` -Or the development version from -[GitHub](https://github.com/aloftdata/getRad): +Or install the development version from [GitHub](https://github.com/) +with: ``` r -# install.packages("devtools") -devtools::install_github("aloftdata/getRad") +# install.packages("pak") +pak::pak("aloftdata/getRad") ``` ## Usage @@ -46,18 +46,19 @@ Download a polar volume, and then plot it using `bioRad`: ``` r library(getRad) library(bioRad) + # Plot daytime insect movements in Finland (Mäkinen et al. 2022) pvol <- get_pvol("fianj", as.POSIXct("2012-05-17 14:00", tz = "UTC")) plot(project_as_ppi(get_scan(pvol, 0), range_max = 75000)) ``` - + ``` r plot(calculate_vp(pvol, h_layer = 50, n_layer = 40, warning = FALSE)) ``` - + ``` r @@ -66,13 +67,13 @@ pvol <- get_pvol("fianj", as.POSIXct("2012-05-11 23:00", tz = "UTC")) plot(project_as_ppi(get_scan(pvol, 0), range_max = 75000)) ``` - + ``` r plot(calculate_vp(pvol, h_layer = 50, n_layer = 40, warning = FALSE)) ``` - + Download a vertical profile time series from the [Aloft bucket](https://aloftdata.eu/browse/): @@ -93,17 +94,14 @@ for (i in names(vpts_list)) { } ``` - + ## Meta -- We welcome - [contributions](https://aloftdata.github.io/getRad/CONTRIBUTING.html) - including bug reports. +- We welcome [contributions](.github/CONTRIBUTING.md) including bug + reports. - License: MIT -- Get [citation - information](https://aloftdata.github.io/getRad/authors.html#citation) - for getRad in R doing `citation("getRad")`. +- Get citation information for getRad in R with `citation("getRad")`. - Please note that this project is released with a [Contributor Code of - Conduct](https://aloftdata.github.io/getRad/CODE_OF_CONDUCT.html). By - participating in this project you agree to abide by its terms. + Conduct](.github/CODE_OF_CONDUCT.md). By participating in this project + you agree to abide by its terms. diff --git a/man/getRad-package.Rd b/man/getRad-package.Rd index 0c0c6c52..786d872e 100644 --- a/man/getRad-package.Rd +++ b/man/getRad-package.Rd @@ -18,21 +18,22 @@ Useful links: } \author{ -\strong{Maintainer}: Bart Kranstauber \email{b.kranstauber@uva.nl} (\href{https://orcid.org/0000-0001-8303-780X}{ORCID}) (University of Amsterdam) +\strong{Maintainer}: Bart Kranstauber \email{b.kranstauber@uva.nl} (\href{https://orcid.org/0000-0001-8303-780X}{ORCID}) (affiliation: University of Amsterdam) Authors: \itemize{ - \item Pieter Huybrechts \email{pieter.huybrechts@inbo.be} (\href{https://orcid.org/0000-0002-6658-6062}{ORCID}) (Research Institute for Nature and Forest (INBO)) - \item Peter Desmet \email{peter.desmet@inbo.be} (\href{https://orcid.org/0000-0002-8442-8025}{ORCID}) (Research Institute for Nature and Forest (INBO)) + \item Bart Kranstauber \email{b.kranstauber@uva.nl} (\href{https://orcid.org/0000-0001-8303-780X}{ORCID}) (affiliation: University of Amsterdam) + \item Pieter Huybrechts \email{pieter.huybrechts@inbo.be} (\href{https://orcid.org/0000-0002-6658-6062}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) + \item Peter Desmet \email{peter.desmet@inbo.be} (\href{https://orcid.org/0000-0002-8442-8025}{ORCID}) (affiliation: Research Institute for Nature and Forest (INBO)) } Other contributors: \itemize{ - \item Cecilia Nilsson \email{cecilia.nilsson@biol.lu.se} (\href{https://orcid.org/0000-0001-8957-4411}{ORCID}) (Lund University) [contributor] - \item Alexander Tedeschi \email{at744@cornell.edu} (\href{https://orcid.org/0000-0003-0772-6931}{ORCID}) (Cornell Lab of Ornithology) [contributor] - \item Hidde Leijnse (\href{https://orcid.org/0000-0001-7835-4480}{ORCID}) (Royal Netherlands Meteorological Institute) [contributor] - \item Bart Hoekstra (\href{https://orcid.org/0000-0002-7085-3805}{ORCID}) (University of Amsterdam) [contributor] - \item University of Amsterdam (04dkp9463) [copyright holder] + \item Cecilia Nilsson \email{cecilia.nilsson@biol.lu.se} (\href{https://orcid.org/0000-0001-8957-4411}{ORCID}) (affiliation: Lund University) [contributor] + \item Alexander Tedeschi \email{at744@cornell.edu} (\href{https://orcid.org/0000-0003-0772-6931}{ORCID}) (affiliation: Cornell Lab of Ornithology) [contributor] + \item Hidde Leijnse (\href{https://orcid.org/0000-0001-7835-4480}{ORCID}) (affiliation: Royal Netherlands Meteorological Institute) [contributor] + \item Bart Hoekstra (\href{https://orcid.org/0000-0002-7085-3805}{ORCID}) (affiliation: University of Amsterdam) [contributor] + \item University of Amsterdam (\href{https://ror.org/04dkp9463}{ROR}) [copyright holder] \item Biodiversa+ (https://hirad.science/) [funder] } diff --git a/man/get_pvol.Rd b/man/get_pvol.Rd index 5e54b674..1960ec25 100644 --- a/man/get_pvol.Rd +++ b/man/get_pvol.Rd @@ -42,7 +42,7 @@ with the Terminal Doppler Weather Radar (TDWR) program can not be read. These ca be identified using the \code{stntype} column in \code{get_weather_radars("nexrad")}. } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (interactive()) withAutoprint(\{ # examplesIf} # Get PVOL data for a single radar and datetime get_pvol("deess", as.POSIXct(Sys.Date())) diff --git a/man/get_vpts.Rd b/man/get_vpts.Rd index dd6512a7..23b74da8 100644 --- a/man/get_vpts.Rd +++ b/man/get_vpts.Rd @@ -7,7 +7,7 @@ get_vpts( radar, datetime, - source = c("baltrad", "uva", "ecog-04003", "rmi"), + source = c("baltrad", "uva", "ecog-04003", "rmi", "birdcast"), return_type = c("vpts", "tibble") ) } @@ -26,15 +26,15 @@ downloaded. \item A \code{\link[lubridate:interval]{lubridate::interval()}}, between which all data files are downloaded. }} -\item{source}{Source of the data. One of \code{"baltrad"}, \code{"uva"}, \code{"ecog-04003"} -or \code{"rmi"}. Only one source can be queried at a time. If not provided, +\item{source}{Source of the data. One of \code{"baltrad"}, \code{"uva"}, \code{"ecog-04003"}, +\code{"rmi"}, or \code{"birdcast"}. Only one source can be queried at a time. If not provided, \code{"baltrad"} is used. Alternatively a local directory can be specified, see details for an explanation of the file format.} \item{return_type}{Type of object that should be returned. Either: \itemize{ \item \code{"vpts"}: vpts object(s) (default). -\item \code{"tibble"}: a \code{\link[dplyr:reexports]{dplyr::tibble()}}. +\item \code{"tibble"}: a \code{\link[dplyr:tibble]{dplyr::tibble()}}. }} } \value{ @@ -44,7 +44,7 @@ Either a vpts object, a list of vpts objects or a tibble. See \description{ Gets vertical profile time series data from supported sources and returns it as a (list of) of \link[bioRad:summary.vpts]{vpts objects} or a -\code{\link[dplyr:reexports]{dplyr::tibble()}}. +\code{\link[dplyr:tibble]{dplyr::tibble()}}. } \details{ For more details on supported sources, see \code{vignette("supported_sources")}. @@ -68,7 +68,7 @@ data is stored in aloft data Besides the examples above there is a \code{date} object available for formatting. } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (interactive()) withAutoprint(\{ # examplesIf} # Get VPTS data for a single radar and date get_vpts(radar = "bejab", datetime = "2023-01-01", source = "baltrad") get_vpts(radar = "bejab", datetime = "2020-01-19", source = "rmi") @@ -101,5 +101,7 @@ get_vpts( source = "baltrad", return_type = "tibble" ) +#' Get VPTS data from the public BirdCast NEXRAD archive +get_vpts(radar = "KABR", datetime = "2023-01-01", source = "birdcast") \dontshow{\}) # examplesIf} } diff --git a/man/get_vpts_aloft.Rd b/man/get_vpts_aloft.Rd deleted file mode 100644 index 32451589..00000000 --- a/man/get_vpts_aloft.Rd +++ /dev/null @@ -1,47 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_vpts_aloft.R -\name{get_vpts_aloft} -\alias{get_vpts_aloft} -\title{Get VPTS data from the Aloft bucket} -\usage{ -get_vpts_aloft( - radar_odim_code, - rounded_interval, - source = c("baltrad", "uva", "ecog-04003"), - coverage = get_vpts_coverage_aloft() -) -} -\arguments{ -\item{radar_odim_code}{Radar ODIM code.} - -\item{rounded_interval}{Interval to fetch data for, rounded to nearest day.} - -\item{source}{Source of the data. One of \code{baltrad}, \code{uva} or \code{ecog-04003}.} - -\item{coverage}{A data frame containing the coverage of the Aloft bucket. -If not provided, it will be fetched from via the internet.} -} -\value{ -A tibble with VPTS data. -} -\description{ -Gets VPTS data from the Aloft bucket. -} -\details{ -By default, data from the \href{https://aloftdata.eu/browse/}{Aloft bucket} are -retrieved from \url{https://aloftdata.s3-eu-west-1.amazonaws.com}. This can be changed by -setting \code{options(getRad.aloft_data_url)} to any desired url. -} -\section{Inner working}{ - -\itemize{ -\item Constructs the S3 paths for the VPTS files based on the input. -\item Performs parallel HTTP requests to fetch the VPTS CSV data. -\item Parses the response bodies with some assumptions about the column classes. -\item Adds a column with the radar source. -\item Overwrites the radar column with the radar_odim_code, all other values for -this column are considered in error. -} -} - -\keyword{internal} diff --git a/man/get_vpts_coverage.Rd b/man/get_vpts_coverage.Rd index af8087e1..ac0d0034 100644 --- a/man/get_vpts_coverage.Rd +++ b/man/get_vpts_coverage.Rd @@ -4,11 +4,14 @@ \alias{get_vpts_coverage} \title{Get VPTS file coverage from supported sources} \usage{ -get_vpts_coverage(source = c("baltrad", "uva", "ecog-04003", "rmi"), ...) +get_vpts_coverage( + source = c("baltrad", "uva", "ecog-04003", "rmi", "birdcast"), + ... +) } \arguments{ \item{source}{Source of the data. One or more of \code{"baltrad"}, \code{"uva"}, -\code{"ecog-04003"} or \code{"rmi"}. If not provided, \code{"baltrad"} is used. +\code{"ecog-04003"} or \code{"rmi"} or \code{"birdcast"}. If not provided, \code{"baltrad"} is used. Alternatively \code{"all"} can be used if data from all sources should be returned.} @@ -22,7 +25,7 @@ A \code{data.frame} or \code{tibble} with at least three columns, \code{source}, Gets the VPTS file coverage from supported sources per radar and date. } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (interactive()) withAutoprint(\{ # examplesIf} get_vpts_coverage() \dontshow{\}) # examplesIf} } diff --git a/man/get_vpts_rmi.Rd b/man/get_vpts_rmi.Rd deleted file mode 100644 index 6854ac62..00000000 --- a/man/get_vpts_rmi.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_vpts_rmi.R -\name{get_vpts_rmi} -\alias{get_vpts_rmi} -\title{Get VPTS data from RMI} -\usage{ -get_vpts_rmi(radar_odim_code, rounded_interval) -} -\arguments{ -\item{radar_odim_code}{Radar ODIM code.} - -\item{rounded_interval}{Interval to fetch data for, rounded to nearest day.} -} -\value{ -A tibble with VPTS data. -} -\description{ -Get VPTS data from \href{https://opendata.meteo.be/geonetwork/srv/eng/catalog.search#/metadata/RMI_DATASET_CROW}{RMI_DATASET_CROW}. -} -\keyword{internal} diff --git a/man/get_weather_radars.Rd b/man/get_weather_radars.Rd index ae12c983..81d352d7 100644 --- a/man/get_weather_radars.Rd +++ b/man/get_weather_radars.Rd @@ -37,7 +37,7 @@ from. } } \examples{ -\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +\dontshow{if (interactive()) withAutoprint(\{ # examplesIf} # Get radar metadata from OPERA get_weather_radars(source = "opera") diff --git a/man/req_cache_getrad.Rd b/man/req_cache_getrad.Rd deleted file mode 100644 index 90a4b5a5..00000000 --- a/man/req_cache_getrad.Rd +++ /dev/null @@ -1,40 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R -\name{req_cache_getrad} -\alias{req_cache_getrad} -\title{Function to set the cache for a getRad specific httr2 request} -\usage{ -req_cache_getrad( - req, - use_cache = TRUE, - max_age = getOption("getRad.max_cache_age_seconds", default = 6 * 60 * 60), - max_n = getOption("getRad.max_cache_n", default = Inf), - max_size = getOption("getRad.max_cache_size_bytes", default = 1024 * 1024 * 1024), - ... -) -} -\arguments{ -\item{req}{\code{httr2} request.} - -\item{use_cache}{Logical indicating whether to use the cache. Default is -\code{TRUE}. If \code{FALSE} the cache is ignored and the file is fetched anew. -This can also be useful if you want to force a refresh of the cache.} - -\item{max_n, max_age, max_size}{Automatically prune the cache by specifying -one or more of: -\itemize{ -\item \code{max_age}: to delete files older than this number of seconds. -\item \code{max_n}: to delete files (from oldest to newest) to preserve at -most this many files. -\item \code{max_size}: to delete files (from oldest to newest) to preserve at -most this many bytes. -} - -The cache pruning is performed at most once per minute.} - -\item{...}{Additional arguments passed to \code{httr2::req_cache()}.} -} -\description{ -Function to set the cache for a getRad specific httr2 request -} -\keyword{internal} diff --git a/tests/testthat/test-get_pvol_at.R b/tests/testthat/test-get_pvol_at.R new file mode 100644 index 00000000..089a2234 --- /dev/null +++ b/tests/testthat/test-get_pvol_at.R @@ -0,0 +1,33 @@ +test_that("Pvol for hochficht in austria can be downloaded", { + skip_if_offline("public.hub.geosphere.at") + time <- as.POSIXct(Sys.time() - 10000, tz = "Europe/Helsinki") + pvol <- expect_s3_class(get_pvol("hochficht", time, param = "all"), "pvol") + expect_true(bioRad::is.pvol(pvol)) + expect_identical( + pvol$datetime, + lubridate::floor_date(lubridate::with_tz(time, "UTC"), "5 min") + ) +}) + +test_that("Pvol for hochficht in austria can be downloaded", { + skip_if_offline("public.hub.geosphere.at") + time <- as.POSIXct( + Sys.time() - 10000 - 24 * 3 * 60 * 60, + tz = "Europe/Helsinki" + ) + expect_error( + get_pvol("hochficht", time, param = "all"), + class = "getRad_error_get_pvol_at_no_data" + ) +}) +test_that("hochficht can be combined with other radars", { + suppressMessages(expect_type( + pvol_lst <- get_pvol( + c("depro", "hochficht", "KABX"), + as.POSIXct(Sys.Date()) + ), + "list" + )) + expect_all_true(purrr::map_lgl(pvol_lst, inherits, "pvol")) + expect_length(pvol_lst, 3L) +}) diff --git a/tests/testthat/test-get_pvol_cz.R b/tests/testthat/test-get_pvol_cz.R index 138032e4..d9e5eb4e 100644 --- a/tests/testthat/test-get_pvol_cz.R +++ b/tests/testthat/test-get_pvol_cz.R @@ -1,5 +1,5 @@ test_that("Check if the available attributes changed", { - skip_if_offline() + skip_if_offline("opendata.chmi.cz") expect_identical( httr2::request( "http://opendata.chmi.cz/meteorology/weather/radar/sites/ska" @@ -21,7 +21,7 @@ test_that("Check if the available attributes changed", { ) }) test_that("Pvol for Czechia can be downloaded", { - skip_if_offline() + skip_if_offline("opendata.chmi.cz") time <- lubridate::floor_date( as.POSIXct(Sys.time(), tz = "Europe/Helsinki") - lubridate::hours(10), "5 mins" @@ -33,3 +33,20 @@ test_that("Pvol for Czechia can be downloaded", { lubridate::with_tz(time, "UTC") ) }) + +test_that("get_pvol_cz() returns error on duplicate elevation angles", { + skip_if_offline("opendata.chmi.cz") + time <- lubridate::floor_date( + as.POSIXct(Sys.time(), tz = "Europe/Helsinki") - lubridate::hours(10), + "5 mins" + ) + with_mocked_bindings( + code = { + expect_error( + get_pvol("czska", time, param = "all"), + class = "getRad_error_czechia_duplicated_elevation_angles" + ) + }, + get_elevation_angles = function(...) c("dup_angle", "dup_angle") + ) +}) diff --git a/tests/testthat/test-get_pvol_ee.R b/tests/testthat/test-get_pvol_ee.R index 8a353d26..a8722f7a 100644 --- a/tests/testthat/test-get_pvol_ee.R +++ b/tests/testthat/test-get_pvol_ee.R @@ -1,20 +1,38 @@ test_that("Pvol for estonia can be downloaded", { skip_if_offline() withr::local_options(list(httr2_progress = FALSE)) - # The api frequently sends a 429 response therefore test is allowed to fail + + time <- as.POSIXct("2024-4-4 21:00:00", tz = "Europe/Helsinki") + + # The API frequently sends 429/500 responses, therefore this test is allowed + # to skip when the download is unsuccessful. show_failure(expect_no_error( pvol <- get_pvol( "eesur", - time <- as.POSIXct("2024-4-4 21:00:00", tz = "Europe/Helsinki"), + time, param = "all" ) )) - ## If get_pvol() returns an error, the other tests are skipped. + skip_if_not( inherits(pvol, "pvol"), - message = "PVOL download for estonia was unsuccesful, succes is variable in testing environments" + message = paste( + "PVOL download for Estonia was unsuccessful;", + "success is variable in testing environments" + ) ) + expect_s3_class(pvol, "pvol") expect_true(bioRad::is.pvol(pvol)) expect_identical(pvol$datetime, lubridate::with_tz(time, "UTC")) }) + +test_that("Pvol for estonia fails on missing data", { + expect_error( + get_pvol( + radar = "eesur", + structure(1776221700, class = c("POSIXct", "POSIXt")) + ), + class = "getRad_error_get_pvol_ee_differing_n_files" + ) +}) diff --git a/tests/testthat/test-get_pvol_se.R b/tests/testthat/test-get_pvol_se.R index 963e67e8..0a1fd728 100644 --- a/tests/testthat/test-get_pvol_se.R +++ b/tests/testthat/test-get_pvol_se.R @@ -34,8 +34,8 @@ test_that("Pvol for Sweden can be downloaded", { test_that("Pvol for Sweden fails out of time range", { skip_if_offline("opendata-download-radar.smhi.se") + skip_if_se_not_updated("hudiksvall", Sys.time() - lubridate::hours(4)) time <- Sys.time() - lubridate::hours(40) - skip_if_se_not_updated("hudiksvall", time) expect_error( get_pvol("sehuv", time), diff --git a/tests/testthat/test-get_vpts_birdcast.R b/tests/testthat/test-get_vpts_birdcast.R new file mode 100644 index 00000000..d65fc448 --- /dev/null +++ b/tests/testthat/test-get_vpts_birdcast.R @@ -0,0 +1,132 @@ +birdcast_coverage <- tibble::tibble( + radar = "KABR", + date = as.Date(c("2013-09-01", "2013-09-02")) +) + +test_that("get_vpts_birdcast() returns error on invalid radar code", { + expect_error( + getRad:::get_vpts_birdcast( + radar = "KAB", + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ), + class = "getRad_error_radar_not_single_odim_nexrad" + ) + + expect_error( + getRad:::get_vpts_birdcast( + radar = 12345, + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ), + class = "getRad_error_radar_not_single_odim_nexrad" + ) +}) + +test_that("get_vpts_birdcast() returns error when multiple radars are queried", { + expect_error( + getRad:::get_vpts_birdcast( + radar = c("KABR", "KABX"), + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ), + class = "getRad_error_radar_not_single_odim_nexrad" + ) +}) + +test_that("get_vpts_birdcast() returns error when radar is not found in coverage", { + expect_error( + getRad:::get_vpts_birdcast( + radar = "ZZZZ", + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ), + class = "getRad_error_birdcast_radar_not_found" + ) + + expect_identical( + rlang::catch_cnd( + getRad:::get_vpts_birdcast( + radar = "ZZZZ", + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ), + classes = "getRad_error_birdcast_radar_not_found" + )$missing_radar, + "ZZZZ" + ) +}) + +test_that("get_vpts_birdcast() returns error when date is requested not in coverage", { + expect_error( + getRad:::get_vpts_birdcast( + radar = "KABR", + rounded_interval = lubridate::interval("1900-01-01", "1900-01-02"), + coverage = birdcast_coverage + ), + class = "getRad_error_date_not_found" + ) +}) + +test_that("get_vpts_birdcast() can fetch daily VPTS data from BirdCast archive", { + skip_if_offline() + + birdcast_vpts_tbl <- getRad:::get_vpts_birdcast( + radar = "KABR", + rounded_interval = lubridate::interval("2013-09-01", "2013-09-02"), + coverage = birdcast_coverage + ) + + expect_type(birdcast_vpts_tbl, "list") + expect_s3_class(birdcast_vpts_tbl, "tbl_df") + + expect_named( + birdcast_vpts_tbl, + c( + "radar", + "datetime", + "height", + "height_reference", + "u", + "v", + "w", + "ff", + "dd", + "sd_vvp", + "gap", + "eta", + "dens", + "dbz", + "dbz_all", + "n", + "n_dbz", + "n_all", + "n_dbz_all", + "rcs", + "sd_vvp_threshold", + "vcp", + "radar_latitude", + "radar_longitude", + "radar_height", + "radar_wavelength", + "source_file", + "source" + ) + ) + + expect_true(nrow(birdcast_vpts_tbl) > 0) + expect_true(all(birdcast_vpts_tbl$radar == "kabr")) + expect_true(all(birdcast_vpts_tbl$source == "birdcast")) +}) +test_that("get_vpts() can fetch daily VPTS data from BirdCast archive", { + skip_if_offline() + date <- as.Date("2026-4-1") + vpts <- getRad:::get_vpts( + radar = "KABX", + date, + source = "birdcast" + ) + expect_s3_class(vpts, "vpts") + expect_all_true(as.Date(vpts$datetime) == date) + expect_false(vpts$regular) +}) diff --git a/tests/testthat/test-get_vpts_coverage.R b/tests/testthat/test-get_vpts_coverage.R index 2c69aa9f..6e93262d 100644 --- a/tests/testthat/test-get_vpts_coverage.R +++ b/tests/testthat/test-get_vpts_coverage.R @@ -10,7 +10,7 @@ test_that("Source argument as expected", { ) }) -test_that("format as expect for aloft", { +test_that("format as expected for aloft", { skip_if_offline() data <- get_vpts_coverage("uva") @@ -19,7 +19,7 @@ test_that("format as expect for aloft", { expect_true(all(is_odim(data$radar))) }) -test_that("format as expect for rmi", { +test_that("format as expected for rmi", { skip_if_offline("opendata.meteo.be") data <- get_vpts_coverage("rmi") @@ -28,6 +28,16 @@ test_that("format as expect for rmi", { expect_true(all(is_odim(data$radar))) }) +test_that("format as expected for birdcast", { + skip_if_offline() + + data <- get_vpts_coverage("birdcast") + expect_true(all(c("source", "radar", "date") %in% names(data))) + expect_s3_class(data$date, "Date") + expect_true(all(grepl("^[A-Z0-9]{4}$", data$radar))) + expect_true(all(data$source == "birdcast")) +}) + test_that("combined retrieval works", { skip_if_offline("opendata.meteo.be") @@ -48,8 +58,9 @@ test_that("get_vpts_coverage() returns 'baltrad' as a default source", { test_that("The argument source='all' returns all data", { + all_coverage <- get_vpts_coverage(source = "all") expect_equal( - get_vpts_coverage(source = "all") |> + all_coverage |> dplyr::pull(source) |> table(), get_vpts_coverage( @@ -58,4 +69,9 @@ test_that("The argument source='all' returns all data", { dplyr::pull(source) |> table() ) + + expect_identical( + sort(unique(all_coverage$source)), + sort(eval(rlang::fn_fmls(get_vpts_coverage)$source)) + ) }) diff --git a/tests/testthat/test-get_vpts_coverage_birdcast.R b/tests/testthat/test-get_vpts_coverage_birdcast.R new file mode 100644 index 00000000..cba1559d --- /dev/null +++ b/tests/testthat/test-get_vpts_coverage_birdcast.R @@ -0,0 +1,30 @@ +test_that("get_vpts_coverage_birdcast() returns a tibble", { + skip_if_offline() + expect_s3_class( + get_vpts_coverage_birdcast(), + "tbl_df" + ) +}) + +test_that("get_vpts_coverage_birdcast() returns the expected columns", { + skip_if_offline() + + expect_named( + get_vpts_coverage_birdcast(), + c("directory", "file_count", "source", "radar", "date") + ) +}) + +test_that("get_vpts_coverage_birdcast() returns expected NEXRAD values", { + skip_if_offline() + + coverage <- get_vpts_coverage_birdcast() + + expect_all_true(coverage$source == "birdcast") + expect_s3_class(coverage$date, "Date") + expect_true(all(grepl("^[A-Z0-9]{4}$", coverage$radar))) + expect_true(all(grepl( + "^nexrad/daily/[A-Z0-9]{4}/[0-9]{4}/[0-9]{2}/[0-9]{2}$", + coverage$directory + ))) +}) diff --git a/vignettes/articles/vpts_coverage.Rmd b/vignettes/articles/vpts_coverage.Rmd index 400aa569..0c215d90 100644 --- a/vignettes/articles/vpts_coverage.Rmd +++ b/vignettes/articles/vpts_coverage.Rmd @@ -23,12 +23,91 @@ library(htmltools) ``` ```{r data} -cvr <- get_vpts_coverage(source = "all") -wr <- get_weather_radars("opera") |> +cvr <- get_vpts_coverage("all") +wr <- get_weather_radars("all") |> group_by(radar) |> arrange(status) |> slice_tail(n = 1) ``` +```{r} +# there is a with issue in the svg, here is a temporary solution: https://github.com/r-spatial/leafpop/issues/25 +assignInNamespace("popupSVGraph",function(graphs, #dsn = tempdir(), + width = 300, height = 300, ...) { + lapply(1:length(graphs), function(i) { + #nm = paste0("tmp_", i, ".svg") + #fls = file.path(dsn, nm) + + inch_wdth = width / 96 + inch_hght = height / 96 + + #svg(filename = fls, width = inch_wdth, height = inch_hght, ...) + #print(graphs[[i]]) + #dev.off() + lns <- svglite::svgstring( + width = inch_wdth, + height = inch_hght, + standalone = FALSE + ) + print(graphs[[i]]) + dev.off() + + svg_str <- lns() + + # this is a temporary solution to work around svglite + # non-specific CSS styles + # perhaps we should separate out into its own function/utility + # also adds uuid dependency + svg_id <- paste0("x",uuid::UUIDgenerate()) + svg_str <- gsub( + x = svg_str, + pattern = " + # %s + # + # " , + # width, + # height, + # svg_str + # ) + # ) + pop = sprintf( + "
%s
", + width, + height, + svg_str + ) + + popTemplate = system.file("templates/popup-graph.brew", package = "leafpop") + myCon = textConnection("outputObj", open = "w") + brew::brew(popTemplate, output = myCon) + outputObj = outputObj + close(myCon) + + return(paste(outputObj, collapse = ' ')) + + }) +} +,"leafpop") +``` ```{r} @@ -48,20 +127,21 @@ for (i in unique(cvr$source)) { years <- seq(min(cvrsub$year), max(cvrsub$year), 1) cvrsub |> group_by(radar) |> - summarize(grph = list(ggplot(data = pick(everything())) + - geom_tile(aes(x = month, y = year, fill = (n / n_max) * 100)) + - scale_fill_viridis_c("Coverage", limits = c(0, 100), breaks = (0:5) * 20, labels = paste0((0:5) * 20, " [%]"), direction = -1) + + summarize(grph = list(ggplot(data = bind_rows(expand.grid(year=years, month=month.name, n=NA, n_max=NA), pick(everything())))+ + scale_x_discrete("Month", breaks = month.name, labels = month.name, limits = month.name) + + geom_raster(aes(x = month, y = year, fill = (n / n_max) * 100)) + + scale_fill_viridis_c("Coverage", limits = c(0, 100), breaks = (0:5) * 20, labels = paste0((0:5) * 20, " [%]"), direction = -1, na.value = "#FFF0") + theme_minimal() + - scale_y_continuous("Year", breaks = years, limits = range(years) + c(-.51, .51)) + + scale_y_continuous("Year", breaks=if(length(years)<6){ years}else{pretty(years)} , limits = range(years) + c(-.51, .51)) + theme( axis.text.x = element_text(angle = -90, vjust = 0.5, hjust = 0), plot.title = element_text(hjust = 0.5), - panel.grid = element_blank() + panel.grid = element_blank(), + legend.ticks = element_blank() ) + - ggtitle(radar) + - scale_x_discrete("Month", breaks = month.name, labels = month.name, limits = month.name))) -> res - res$grph[[1]] - suppressWarnings(res |> left_join(wr, by = join_by(radar))) -> res + ggtitle(radar) )) -> res +# res$grph[[1]] + suppressWarnings(res |> left_join(wr |> select(radar, longitude, latitude), by = join_by(radar))) -> res ll <- NULL if (any(is.na(res$longitude))) { ll <- p(glue::glue("The following radars were omitted from the map because they lack location information in OPERA: {glue::glue_collapse(res$radar[is.na(res$longitude)], ', ', last = ' and ' )}.")) @@ -74,7 +154,7 @@ for (i in unique(cvr$source)) { lat = res$latitude, label = res$radar, clusterOptions = markerClusterOptions(maxClusterRadius = 30), - popup = popupGraph(res$grph) + popup = popupGraph(res$grph, type = "svg") ) html <- c(html, list(h3(i), lft, ll)) } diff --git a/vignettes/supported_sources.Rmd b/vignettes/supported_sources.Rmd index 4c16718c..cba63100 100644 --- a/vignettes/supported_sources.Rmd +++ b/vignettes/supported_sources.Rmd @@ -25,6 +25,7 @@ included_countries <- dplyr::tribble( ~code, ~description, ~time, ~supported, ~license, ~links, "sk", "Data is publicly available", "Data seems to be available for about one month","Readable","CC BY 4.0 1 2 3 4 ",c("Data source"="https://opendata.shmu.sk/meteorology/weather/radar/volume/"), "ro", "Data is publicly available", "Data seems to be available for about the last 4 days","Readable","CC BY 4.01",c("Data source"="https://opendata.meteoromania.ro/radar/"), + "at", "Data is publicly available. There is only data for one radar that is not part of the odim network. The data can be downloaded using \"hochficht\" as radar code to `get_pvol`", "Data is available for about three days" ,"Readable","CC BY 4.01",c("Data source"="https://public.hub.geosphere.at/public/datahub.html?id=radar_volumen_hochficht-v1-5min/filelisting"), "se", "Data is publicly available", "Data is only available for 24 hours","Readable","CC BY 4.0 SE1",c("API documentation"="https://opendata.smhi.se/radar/api","Documentation of radar products"="https://www.smhi.se/data/sok-oppna-data-i-utforskaren/meteorologiska-observationer-radar-sverigekomposit-och-enskilda-volymer"), "us", "Data is available in an aws S3 bucket", NA, "Readable", "NOAA data disseminated through NODD are open to the public and can be used as desired 1", c("Bucket" = "https://noaa-nexrad-level2.s3.amazonaws.com/index.html", "More info" = "https://www.ncei.noaa.gov/products/radar/next-generation-weather-radar"), "be", "Data is suggested to be opened into the future, it is however not yet accessible.", NA, "Information", NA, c("More info" = "https://opendata.meteo.be/geonetwork/srv/eng/catalog.search#/metadata/RMI_DATASET_JABBEKE_VOLUME"),