diff --git a/NAMESPACE b/NAMESPACE index 9addf16c..e2123183 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(aemet_alert_zones) export(aemet_alerts) export(aemet_api_key) export(aemet_beaches) @@ -55,5 +56,5 @@ importFrom(rlang,.data) importFrom(stats,aggregate) importFrom(stats,approx) importFrom(utils,head) -importFrom(utils,packageVersion) importFrom(utils,tail) +importFrom(utils,unzip) diff --git a/NEWS.md b/NEWS.md index 739bdbc3..3c31f92a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,9 @@ # climaemet (development version) -- New function: `aemet_alerts()`. +- New functions: + - `aemet_alerts()` to get current meteorological alerts issued by AEMET. + - Helper function `aemet_alert_zones()` to obtain the zoning defined by + AEMET for the alerts. - Increase timeout limit with `httr2::req_timeout()`. - Better management of non valid/duplicated/empty API keys. diff --git a/R/aemet_alert_zones.R b/R/aemet_alert_zones.R new file mode 100644 index 00000000..291cc5f8 --- /dev/null +++ b/R/aemet_alert_zones.R @@ -0,0 +1,114 @@ +#' AEMET alert zones +#' +#' Get AEMET alert zones. +#' +#' @family aemet_api_data +#' +#' +#' @inheritParams aemet_beaches +#' +#' +#' @return A [`tibble`][tibble::tibble()] or a \CRANpkg{sf} object. +#' +#' +#' @seealso [aemet_alerts()] +#' +#' @details +#' The first result of the call on each session is (temporarily) cached in +#' the assigned [tempdir()] for avoiding unneeded API calls. +#' +#' @source +#' +#' . See also +#' Annex 2 and Annex 3 docs, linked in this page. +#' +#' +#' @examplesIf aemet_detect_api_key() +#' library(tibble) +#' alert_zones <- aemet_alert_zones() +#' alert_zones +#' +#' # Cached during this R session +#' alert_zones2 <- aemet_alert_zones(verbose = TRUE) +#' +#' identical(alert_zones, alert_zones2) +#' +#' # Select an map beaches +#' library(dplyr) +#' library(ggplot2) +#' +#' +#' # Galicia +#' alert_zones_sf <- aemet_alert_zones(return_sf = TRUE) %>% +#' filter(COD_CCAA == "71") +#' +#' # Coast zones are identified by a "C" in COD_Z +#' alert_zones_sf$type <- ifelse(grepl("C$", alert_zones_sf$COD_Z), +#' "Coast", "Mainland" +#' ) +#' +#' +#' ggplot(alert_zones_sf) + +#' geom_sf(aes(fill = NOM_PROV)) + +#' facet_wrap(~type) + +#' scale_fill_brewer(palette = "Blues") +#' +#' @export +aemet_alert_zones <- function(verbose = FALSE, return_sf = FALSE) { + # Validate inputs---- + stopifnot(is.logical(verbose)) + stopifnot(is.logical(return_sf)) + + cached_sf <- file.path(tempdir(), "aemet_alert_zones.gpkg") + cached_date <- file.path(tempdir(), "aemet_alert_zone_date.rds") + + if (file.exists(cached_sf)) { + sf_areas <- sf::read_sf(cached_sf) + dat <- readRDS(cached_date) + + if (verbose) { + message( + "Loading alert zones from temporal cached file saved at ", + format(dat, usetz = TRUE) + ) + } + } else { + # download beaches + url <- paste0( + "https://www.aemet.es/documentos/es/eltiempo/prediccion/", + "avisos/plan_meteoalerta/", + "AEMET-meteoalerta-delimitacion-zonas.zip" + ) + r <- httr2::request(url) + + outdir <- file.path(tempdir(), "alertzones") + outfile <- file.path(outdir, "alertzones.zip") + if (!dir.exists(outdir)) dir.create(outdir, recursive = TRUE) + + r <- httr2::req_perform(r, path = outfile) + + # unzip + unzip(outfile, exdir = outdir, junkpaths = TRUE) + + # Get shp files + shpf <- list.files(outdir, pattern = ".shp$", full.names = TRUE) + + sf_areas <- lapply(shpf, sf::read_sf) + sf_areas <- dplyr::bind_rows(sf_areas) + sf_areas <- sf::st_make_valid(sf_areas) + sf_areas <- sf::st_transform(sf_areas, 4326) + + + # Cache on temp dir + sf::st_write(sf_areas, cached_sf, quiet = TRUE) + saveRDS(Sys.time(), cached_date) + } + + # Validate sf---- + if (!return_sf) { + sf_areas <- sf::st_drop_geometry(sf_areas) + sf_areas <- dplyr::as_tibble(sf_areas) + } + + sf_areas +} diff --git a/R/aemet_alerts.R b/R/aemet_alerts.R index 1d9c74b7..9e4575f6 100644 --- a/R/aemet_alerts.R +++ b/R/aemet_alerts.R @@ -10,6 +10,7 @@ #' @param ccaa A vector of names for autonomous communities or `NULL` to get all #' the autonomous communities. #' @inheritParams get_data_aemet +#' @inheritParams aemet_daily #' @param lang Language of the results. It can be `"es"` (Spanish) or `"en"` #' (English). #' @@ -17,27 +18,37 @@ #' #' . #' -#' @return A \CRANpkg{sf} object. +#' @return A [`tibble`][tibble::tibble()] or a \CRANpkg{sf} object. +#' +#' @source +#' +#' . See also +#' Annex 2 and Annex 3 docs, linked in this page. #' #' @export #' @seealso -#' [mapSpain::esp_get_ccaa()]. See also [mapSpain::esp_codelist], -#' [mapSpain::esp_dict_region_code()] and **Examples** to get the names of the +#' [aemet_alert_zones()]. See also [mapSpain::esp_codelist], +#' [mapSpain::esp_dict_region_code()] to get the names of the #' autonomous communities. #' #' @examplesIf aemet_detect_api_key() -#' #' # Display names of CCAAs -#' unique(mapSpain::esp_codelist$ine.ccaa.name) +#' library(dplyr) +#' aemet_alert_zones() %>% +#' select(NOM_CCAA) %>% +#' distinct() #' #' # Base map #' cbasemap <- mapSpain::esp_get_ccaa(ccaa = c( -#' "Galicia", -#' "Asturias, Principado de" +#' "Galicia", "Asturias", "Cantabria", +#' "Euskadi" #' )) #' #' # Alerts -#' alerts_north <- aemet_alerts(ccaa = c("Galicia", "Asturias, Principado de")) +#' alerts_north <- aemet_alerts( +#' ccaa = c("Galicia", "Asturias", "Cantabria", "Euskadi"), +#' return_sf = TRUE +#' ) #' #' # If any alert #' if (inherits(alerts_north, "sf")) { @@ -54,7 +65,7 @@ #' data = cbasemap, fill = "transparent", color = "black", #' linewidth = 0.5 #' ) + -#' facet_wrap(vars(day, `AEMET-Meteoalerta fenomeno`)) + +#' facet_grid(vars(`AEMET-Meteoalerta fenomeno`), vars(day)) + #' scale_fill_manual(values = c( #' "amarillo" = "yellow", naranja = "orange", #' "rojo" = "red" @@ -62,14 +73,34 @@ #' } #' #' @export -aemet_alerts <- function(ccaa = NULL, lang = c("es", "en"), verbose = FALSE) { +aemet_alerts <- function(ccaa = NULL, lang = c("es", "en"), verbose = FALSE, + return_sf = FALSE, extract_metadata = FALSE, + progress = TRUE) { + # 1. Validate inputs---- lang <- match.arg(lang) + stopifnot(is.logical(return_sf)) + stopifnot(is.logical(verbose)) + stopifnot(is.logical(progress)) + + # 2. Call API---- + + ## Metadata ---- + if (extract_metadata) { + apidest <- "/api/avisos_cap/ultimoelaborado/area/esp" + final_result <- get_metadata_aemet(apidest = apidest, verbose = verbose) + return(final_result) + } + + + ## Normal call ---- + + # Extract links using a master table df_links <- aemet_hlp_alerts_master(verbose = verbose) # nocov start if (is.null(df_links)) { - message("No upcoming alerts for ccaas selected") + message("No upcoming alerts") return(NULL) } # nocov end @@ -77,25 +108,27 @@ aemet_alerts <- function(ccaa = NULL, lang = c("es", "en"), verbose = FALSE) { # Filter by CCAAs if requested if (!is.null(ccaa)) { - # Get AEMET Code + # Get codauto + # Extra for Ceuta and Melilla + ccaa <- gsub("Ciudad de ", "", ccaa, ignore.case = TRUE) + ccaa_code <- unique(mapSpain::esp_dict_region_code(ccaa, destination = "codauto" )) ccaa_code <- ccaa_code[!is.na(ccaa_code)] ccaa_code <- unique(ccaa_code[nchar(ccaa_code) > 1]) - df_map <- ccaa_to_aemet() - aemet_code <- df_map[df_map$codauto %in% ccaa_code, ] - if (nrow(aemet_code) == 0) stop("In ccaa param: No matches") - + if (length(ccaa_code) < 1) stop("In ccaa param: No matches") - df_links <- merge(aemet_code[, "aemet"], df_links, by = c("aemet")) + # Unique map + df_links <- df_links[df_links$codauto %in% ccaa_code, ] if (nrow(df_links) == 0) { message("No upcoming alerts for ccaas selected") return(NULL) } df_links <- dplyr::as_tibble(df_links) + # Order df_links$sort <- factor(df_links$codauto, levels = ccaa_code) df_links <- df_links[order(df_links$sort, df_links$link), ] @@ -104,61 +137,103 @@ aemet_alerts <- function(ccaa = NULL, lang = c("es", "en"), verbose = FALSE) { df_links <- df_links[, setdiff(names(df_links), "sort")] } - if (verbose) message(nrow(df_links), " alert(s) found") + # Done - # Start loop here - ntot <- seq_len(nrow(df_links)) + # Make calls on loop for progress bar + # Rename to adapt to other funs + db_cuts <- df_links + final_result <- list() # Store results + ln <- seq_len(nrow(db_cuts)) - get_results <- lapply(ntot, function(i) { - iter <- df_links[i, ] + # Deactive progressbar if verbose + if (verbose) progress <- FALSE + if (!cli::is_dynamic_tty()) progress <- FALSE - link <- as.vector(iter$link) + # nolint start + # nocov start + if (progress) { + opts <- options() + options( + cli.progress_bar_style = "fillsquares", cli.progress_show_after = 3, + cli.spinner = "clock" + ) + cli::cli_progress_bar( + format = paste0( + "{cli::pb_spin} AEMET API ({cli::pb_current}/{cli::pb_total}) ", + "| {cli::pb_bar} {cli::pb_percent} ", + "| ETA:{cli::pb_eta} [{cli::pb_elapsed}]" + ), + total = nrow(db_cuts), clear = FALSE + ) + } + # nocov end + # nolint end - # Perform req - req1 <- httr2::request(link) + id <- 1 - response <- httr2::req_perform(req1) - response <- httr2::resp_body_xml(response) - response <- xml2::as_list(response) + ### API Loop ---- + for (id in ln) { + this <- db_cuts[id, ] + if (progress) cli::cli_progress_update() # nocov - # Extract info in required language - info <- response$alert[names(response$alert) == "info"] - getlang <- unlist(lapply(info, "[", "language")) - info <- info[grepl(lang, getlang)]$info - the_area <- aemet_hlp_area_alert(info) - # Extra labs - labs <- info[names(info) != "area"] - labs_df <- aemet_hlp_labs_alert(labs) - # Join everything + df <- aemet_hlp_single_alert(this, lang) - the_df <- dplyr::bind_cols( - iter[, c("codauto", "ccaa_name")], - sf::st_drop_geometry(the_area), - labs_df - ) + final_result <- c(final_result, list(df)) + } - geometry <- sf::st_geometry(the_area) - final_sf <- sf::st_sf(the_df, geometry) + # nolint start + # nocov start + if (progress) { + cli::cli_progress_done() + options( + cli.progress_bar_style = opts$cli.progress_bar_style, + cli.progress_show_after = opts$cli.progress_show_after, + cli.spinner = opts$cli.spinner + ) + } - final_sf <- aemet_hlp_sf_to_tbl(final_sf) + # nocov end + # nolint end + + # Final tweaks + final_result <- dplyr::bind_rows(final_result) + final_result <- dplyr::as_tibble(final_result) + final_result <- dplyr::distinct(final_result) + final_result <- aemet_hlp_guess(final_result, c( + "AEMET-Meteoalerta zona", + "COD_Z" + )) + + # Check spatial---- + if (return_sf) { + # Geoms of zones + sf_zones <- aemet_alert_zones(return_sf = TRUE) + final_result <- dplyr::left_join(final_result, sf_zones, + by = "COD_Z" + ) - final_sf - }) + final_result <- sf::st_as_sf(final_result) + } else { + # data of zones + data_zones <- aemet_alert_zones(return_sf = FALSE) + final_result <- dplyr::left_join(final_result, data_zones, + by = "COD_Z" + ) + } - get_results <- dplyr::bind_rows(get_results) + vnames <- unique(c( + "NOM_CCAA", "COD_CCAA", "NOM_PROV", "COD_PROV", "NOM_Z", + "COD_Z", names(final_result) + )) - get_results <- aemet_hlp_guess(get_results, - preserve = c( - "AEMET-Meteoalerta zona", - "geometry" - ) - ) + # Relocate + final_result <- final_result[, vnames] - get_results + final_result } @@ -171,13 +246,22 @@ ccaa_to_aemet <- function(...) { "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19" ), - aemet = c( + COD_CCAA = c( "61", "62", "63", "64", "65", "66", "67", "68", "69", "77", "70", "71", "72", "73", "74", "75", "76", "78", "79" ) ) + # Add name of ccaa + full_zones <- aemet_alert_zones()[, c("COD_CCAA", "NOM_CCAA")] + full_zones <- dplyr::distinct(full_zones) + + # Add codauto for interfacing with mapSpain + df <- merge(df, full_zones) + + # Re-arrange + df <- df[, c("COD_CCAA", "NOM_CCAA", "codauto")] dplyr::as_tibble(df) } @@ -221,6 +305,7 @@ aemet_hlp_alerts_master <- function(verbose = FALSE) { return(NULL) } # nocov end + # Create df with id of CCAAs # Get codes from links ccaa_alert <- unlist(lapply(links, function(x) { @@ -228,83 +313,78 @@ aemet_hlp_alerts_master <- function(verbose = FALSE) { substr(ident, 1, 2) })) - df_links <- data.frame(link = links, aemet = ccaa_alert) - - # Add ine code and name of ccaa - df_links <- merge(df_links, ccaa_to_aemet()) + df_links <- data.frame(link = links, COD_CCAA = ccaa_alert) - nms <- mapSpain::esp_dict_region_code(df_links$codauto, "codauto", "text") - nms <- mapSpain::esp_dict_translate(nms, lang = "es") - df_links$ccaa_name <- nms + df_links <- merge(df_links, ccaa_to_aemet()) df_links <- dplyr::as_tibble(df_links) + # Rearrange + df_links <- df_links[, c("COD_CCAA", "NOM_CCAA", "codauto", "link")] + df_links } -aemet_hlp_area_alert <- function(info) { - # Geometries - area <- info$area +aemet_hlp_single_alert <- function(this, lang) { + link <- as.vector(this$link) - polys <- area[names(area) == "polygon"] - to_sf <- lapply(polys, function(x) { - coords <- unlist(strsplit(unlist(x), ",| ")) - coords <- as.double(coords) - pol <- matrix(coords, ncol = 2, byrow = TRUE) - pol <- sf::st_polygon(list(pol[, c(2, 1)])) - geometry <- sf::st_sfc(pol, crs = 4326) - df <- sf::st_sf(x = 1, geometry) - df - }) + # Perform req + req1 <- httr2::request(link) - to_sf <- dplyr::bind_rows(to_sf) - if (nrow(to_sf) > 1) { - geometry <- sf::st_geometry(to_sf) - geometry <- sf::st_combine(geometry) - } else { - geometry <- sf::st_geometry(to_sf) - } - - # Labels - labs <- area[names(area) != "polygon"] + response <- httr2::req_perform(req1) + response <- httr2::resp_body_xml(response) + response <- xml2::as_list(response) - labs_df <- aemet_hlp_labs_alert(labs) + # Extract info in required language + info <- response$alert[names(response$alert) == "info"] + getlang <- unlist(lapply(info, "[", "language")) + info <- info[grepl(lang, getlang)]$info - final_sf <- sf::st_sf(labs_df, geometry) + # Extract and parse - final_sf -} + lng_parse <- seq_len(length(info)) -aemet_hlp_labs_alert <- function(labs) { - labs_n <- names(labs) + parsed <- lapply(lng_parse, function(x) { + id_list <- info[x] + values_list <- unlist(id_list) - labs_df <- lapply(labs_n, function(x) { - dat <- labs[[x]] - if (length(dat) == 1) { - df <- data.frame(xname = unlist(dat)) - names(df) <- x - df <- dplyr::as_tibble(df) + if (length(values_list) == 1) { + df <- tibble::tibble(id = as.character(values_list)) + names(df) <- names(id_list) return(df) } - nm <- grepl("Name", names(dat)) - name_is <- unname(unlist(dat[nm])[1]) - value_is <- unname(unlist(dat[!nm])[1]) - - df <- data.frame(v = value_is) - - df <- dplyr::as_tibble(df) - names(df) <- name_is + if (length(values_list) == 2) { + name_pos <- grep("name", names(values_list), ignore.case = TRUE) + df <- tibble::tibble(id = as.character(values_list[-name_pos])) + names(df) <- values_list[name_pos] + return(df) + } + # Area, the shp would be extracted latter for speeding + if (names(id_list) == "area") { + df_area <- tibble::tibble( + dsc = as.character(id_list$area$areaDesc), + id = as.character(id_list$area$geocode$value), + # Add COD_Z for merges + id2 = as.character(id_list$area$geocode$value) + ) + names(df_area) <- c( + "areaDesc", + as.character(id_list$area$geocode$valueName), + "COD_Z" + ) + return(df_area) + } - df + NULL # nocov }) - labs_df <- dplyr::bind_cols(unique(labs_df)) + parsed <- dplyr::bind_cols(parsed) - labs_df + parsed } diff --git a/R/climaemet-package.R b/R/climaemet-package.R index f1ba7f7e..2110104f 100644 --- a/R/climaemet-package.R +++ b/R/climaemet-package.R @@ -2,7 +2,7 @@ "_PACKAGE" ## usethis namespace: start -#' @importFrom utils packageVersion head tail +#' @importFrom utils head tail unzip #' @importFrom grDevices hcl.colors hcl.pals #' @importFrom stats aggregate approx #' @importFrom rlang .data diff --git a/R/utils.R b/R/utils.R index ba994ef5..bd684383 100644 --- a/R/utils.R +++ b/R/utils.R @@ -64,32 +64,5 @@ aemet_hlp_sf <- function(tbl, lat, lon, verbose = FALSE) { } } -#' Convert sf objects to tibble plus sf -#' -#' @param x a [`sf`][sf::st_sf] object -#' @return A \CRANpkg{sf} object -#' @noRd -aemet_hlp_sf_to_tbl <- function(x) { - if (all(!inherits(x, "tbl"), inherits(x, "sf"))) { - # If not, just add the same class - # Template sf with tbl - tmpl <- data.frame(x = 1) - tmpl$geometry <- "POINT EMPTY" - tmpl <- dplyr::as_tibble(tmpl) - tmpl <- sf::st_as_sf(tmpl, wkt = "geometry", crs = sf::st_crs(4326)) - template <- class(tmpl) - class(x) <- template - } - - # Reorder columns - geom in geometry, it is sticky so even if - # not select would be kept in the last position - x <- x[, setdiff(names(x), "geometry")] - - result_out <- sf::st_make_valid(x) - - result_out -} - - # Default station for metadata default_station <- "9434" diff --git a/README.md b/README.md index 31328046..1f1bea06 100644 --- a/README.md +++ b/README.md @@ -119,22 +119,21 @@ library(climaemet) # See a tibble in action aemet_last_obs("9434") -#> # A tibble: 13 × 25 +#> # A tibble: 12 × 25 #> idema lon fint prec alt vmax vv dv lat dmax #> -#> 1 9434 -1.00 2024-07-30 01:00:00 0 249 9.8 2.6 300 41.7 250 -#> 2 9434 -1.00 2024-07-30 02:00:00 0 249 9.2 6.7 106 41.7 115 -#> 3 9434 -1.00 2024-07-30 03:00:00 0 249 8.4 2.7 163 41.7 130 -#> 4 9434 -1.00 2024-07-30 04:00:00 0 249 8.5 2.5 196 41.7 235 -#> 5 9434 -1.00 2024-07-30 05:00:00 0 249 11.7 7.8 141 41.7 120 -#> 6 9434 -1.00 2024-07-30 06:00:00 0 249 10.6 1.6 338 41.7 135 -#> 7 9434 -1.00 2024-07-30 07:00:00 0 249 3.5 1.6 53 41.7 315 -#> 8 9434 -1.00 2024-07-30 08:00:00 0 249 5.1 2.7 112 41.7 75 -#> 9 9434 -1.00 2024-07-30 09:00:00 0 249 5.8 3.5 120 41.7 125 -#> 10 9434 -1.00 2024-07-30 10:00:00 0 249 8.2 3.4 334 41.7 108 -#> 11 9434 -1.00 2024-07-30 11:00:00 0 249 7.6 3.2 90 41.7 55 -#> 12 9434 -1.00 2024-07-30 12:00:00 0 249 7 4.4 99 41.7 110 -#> 13 9434 -1.00 2024-07-30 13:00:00 0 249 8.4 5.2 93 41.7 120 +#> 1 9434 -1.00 2024-07-31 01:00:00 0 249 7.4 2.5 111 41.7 123 +#> 2 9434 -1.00 2024-07-31 02:00:00 0 249 4.6 2.4 123 41.7 143 +#> 3 9434 -1.00 2024-07-31 03:00:00 0 249 4.2 1.8 99 41.7 90 +#> 4 9434 -1.00 2024-07-31 04:00:00 0 249 4.1 0.9 54 41.7 133 +#> 5 9434 -1.00 2024-07-31 05:00:00 0 249 3.5 2.1 101 41.7 88 +#> 6 9434 -1.00 2024-07-31 06:00:00 0 249 3.4 2.5 98 41.7 100 +#> 7 9434 -1.00 2024-07-31 07:00:00 0 249 2.9 1.7 84 41.7 93 +#> 8 9434 -1.00 2024-07-31 08:00:00 0 249 4.7 2.7 100 41.7 110 +#> 9 9434 -1.00 2024-07-31 09:00:00 0 249 5.2 2.5 96 41.7 113 +#> 10 9434 -1.00 2024-07-31 10:00:00 0 249 4 2.8 97 41.7 93 +#> 11 9434 -1.00 2024-07-31 11:00:00 0 249 4.3 2.7 97 41.7 110 +#> 12 9434 -1.00 2024-07-31 12:00:00 0 249 5.2 2.9 101 41.7 123 #> # ℹ 15 more variables: ubi , pres , hr , stdvv , ts , #> # pres_nmar , tamin , ta , tamax , tpr , #> # stddv , inso , tss5cm , pacutp , tss20cm @@ -173,12 +172,12 @@ knitr::kable(head(data_observation)) | idema | lon | fint | prec | alt | vmax | vv | dv | lat | dmax | ubi | pres | hr | stdvv | ts | pres_nmar | tamin | ta | tamax | tpr | stddv | inso | tss5cm | pacutp | tss20cm | |:------|----------:|:--------------------|-----:|----:|-----:|----:|----:|---------:|-----:|:--------------------|------:|----:|------:|-----:|----------:|------:|-----:|------:|-----:|------:|-----:|-------:|-------:|--------:| -| 9434 | -1.004167 | 2024-07-30 01:00:00 | 0 | 249 | 9.8 | 2.6 | 300 | 41.66056 | 250 | ZARAGOZA AEROPUERTO | 985.6 | 34 | 0.6 | 30.5 | 1013.8 | 30.1 | 30.6 | 32.0 | 13.0 | 118 | 0.0 | 35.2 | 0.00 | 36.1 | -| 9434 | -1.004167 | 2024-07-30 02:00:00 | 0 | 249 | 9.2 | 6.7 | 106 | 41.66056 | 115 | ZARAGOZA AEROPUERTO | 984.7 | 35 | 1.0 | 29.2 | 1012.9 | 29.0 | 29.6 | 30.8 | 12.5 | 16 | 0.0 | 34.6 | 0.00 | 35.9 | -| 9434 | -1.004167 | 2024-07-30 03:00:00 | 0 | 249 | 8.4 | 2.7 | 163 | 41.66056 | 130 | ZARAGOZA AEROPUERTO | 986.0 | 38 | 0.8 | 28.8 | 1014.3 | 29.3 | 29.4 | 29.9 | 13.6 | 17 | 0.0 | 34.1 | 0.00 | 35.7 | -| 9434 | -1.004167 | 2024-07-30 04:00:00 | 0 | 249 | 8.5 | 2.5 | 196 | 41.66056 | 235 | ZARAGOZA AEROPUERTO | 988.1 | 42 | 1.2 | 29.0 | 1016.4 | 29.2 | 29.5 | 29.6 | 15.3 | 43 | 0.0 | 33.7 | 0.01 | 35.5 | -| 9434 | -1.004167 | 2024-07-30 05:00:00 | 0 | 249 | 11.7 | 7.8 | 141 | 41.66056 | 120 | ZARAGOZA AEROPUERTO | 985.9 | 42 | 1.5 | 28.4 | 1014.2 | 28.1 | 28.7 | 29.5 | 14.5 | 11 | 0.0 | 33.2 | 0.04 | 35.2 | -| 9434 | -1.004167 | 2024-07-30 06:00:00 | 0 | 249 | 10.6 | 1.6 | 338 | 41.66056 | 135 | ZARAGOZA AEROPUERTO | 986.2 | 41 | 0.8 | 28.8 | 1014.6 | 28.1 | 28.6 | 28.7 | 14.0 | 145 | 5.1 | 32.8 | 0.00 | 35.0 | +| 9434 | -1.004167 | 2024-07-31 01:00:00 | 0 | 249 | 7.4 | 2.5 | 111 | 41.66056 | 123 | ZARAGOZA AEROPUERTO | 983.6 | 28 | 0.4 | 27.4 | 1011.9 | 28.9 | 28.9 | 30.4 | 8.5 | 13 | 0 | 34.5 | 0 | 36.5 | +| 9434 | -1.004167 | 2024-07-31 02:00:00 | 0 | 249 | 4.6 | 2.4 | 123 | 41.66056 | 143 | ZARAGOZA AEROPUERTO | 983.3 | 31 | 0.4 | 26.9 | 1011.7 | 27.5 | 27.8 | 28.9 | 9.2 | 21 | 0 | 33.9 | 0 | 36.2 | +| 9434 | -1.004167 | 2024-07-31 03:00:00 | 0 | 249 | 4.2 | 1.8 | 99 | 41.66056 | 90 | ZARAGOZA AEROPUERTO | 983.3 | 34 | 0.6 | 24.9 | 1011.9 | 26.1 | 26.1 | 27.8 | 9.0 | 15 | 0 | 33.2 | 0 | 35.9 | +| 9434 | -1.004167 | 2024-07-31 04:00:00 | 0 | 249 | 4.1 | 0.9 | 54 | 41.66056 | 133 | ZARAGOZA AEROPUERTO | 983.6 | 36 | 0.4 | 23.9 | 1012.2 | 25.4 | 25.4 | 26.1 | 9.3 | 44 | 0 | 32.6 | 0 | 35.5 | +| 9434 | -1.004167 | 2024-07-31 05:00:00 | 0 | 249 | 3.5 | 2.1 | 101 | 41.66056 | 88 | ZARAGOZA AEROPUERTO | 983.9 | 40 | 0.3 | 23.1 | 1012.7 | 24.0 | 24.0 | 25.4 | 9.6 | 8 | 0 | 32.0 | 0 | 35.2 | +| 9434 | -1.004167 | 2024-07-31 06:00:00 | 0 | 249 | 3.4 | 2.5 | 98 | 41.66056 | 100 | ZARAGOZA AEROPUERTO | 984.3 | 41 | 0.3 | 25.4 | 1013.0 | 23.8 | 24.7 | 24.7 | 10.5 | 7 | 15 | 31.5 | 0 | 34.9 | ``` r diff --git a/codemeta.json b/codemeta.json index e08c25c8..19d84ad8 100644 --- a/codemeta.json +++ b/codemeta.json @@ -351,7 +351,7 @@ }, "applicationCategory": "Meteorology", "isPartOf": "https://ropenspain.es/", - "fileSize": "829.593KB", + "fileSize": "838.826KB", "citation": [ { "@type": "SoftwareSourceCode", diff --git a/man/aemet_alert_zones.Rd b/man/aemet_alert_zones.Rd new file mode 100644 index 00000000..b3132484 --- /dev/null +++ b/man/aemet_alert_zones.Rd @@ -0,0 +1,79 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/aemet_alert_zones.R +\name{aemet_alert_zones} +\alias{aemet_alert_zones} +\title{AEMET alert zones} +\source{ +\url{https://www.aemet.es/es/eltiempo/prediccion/avisos/ayuda}. See also +Annex 2 and Annex 3 docs, linked in this page. +} +\usage{ +aemet_alert_zones(verbose = FALSE, return_sf = FALSE) +} +\arguments{ +\item{verbose}{Logical \code{TRUE/FALSE}. Provides information about the flow of +information between the client and server.} + +\item{return_sf}{Logical \code{TRUE} or \code{FALSE}. +Should the function return an \code{\link[sf:sf]{sf}} spatial object? If \code{FALSE} +(the default value) it returns a \code{\link[tibble:tibble]{tibble}}. Note that +you need to have the \CRANpkg{sf} package installed.} +} +\value{ +A \code{\link[tibble:tibble]{tibble}} or a \CRANpkg{sf} object. +} +\description{ +Get AEMET alert zones. +} +\details{ +The first result of the call on each session is (temporarily) cached in +the assigned \code{\link[=tempdir]{tempdir()}} for avoiding unneeded API calls. +} +\examples{ +\dontshow{if (aemet_detect_api_key()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} +library(tibble) +alert_zones <- aemet_alert_zones() +alert_zones + +# Cached during this R session +alert_zones2 <- aemet_alert_zones(verbose = TRUE) + +identical(alert_zones, alert_zones2) + +# Select an map beaches +library(dplyr) +library(ggplot2) + + +# Galicia +alert_zones_sf <- aemet_alert_zones(return_sf = TRUE) \%>\% + filter(COD_CCAA == "71") + +# Coast zones are identified by a "C" in COD_Z +alert_zones_sf$type <- ifelse(grepl("C$", alert_zones_sf$COD_Z), + "Coast", "Mainland" +) + + +ggplot(alert_zones_sf) + + geom_sf(aes(fill = NOM_PROV)) + + facet_wrap(~type) + + scale_fill_brewer(palette = "Blues") +\dontshow{\}) # examplesIf} +} +\seealso{ +\code{\link[=aemet_alerts]{aemet_alerts()}} + +Other aemet_api_data: +\code{\link{aemet_alerts}()}, +\code{\link{aemet_beaches}()}, +\code{\link{aemet_daily_clim}()}, +\code{\link{aemet_extremes_clim}()}, +\code{\link{aemet_forecast_beaches}()}, +\code{\link{aemet_forecast_daily}()}, +\code{\link{aemet_last_obs}()}, +\code{\link{aemet_monthly}}, +\code{\link{aemet_normal}}, +\code{\link{aemet_stations}()} +} +\concept{aemet_api_data} diff --git a/man/aemet_alerts.Rd b/man/aemet_alerts.Rd index fd3b2809..08ee6353 100644 --- a/man/aemet_alerts.Rd +++ b/man/aemet_alerts.Rd @@ -5,9 +5,19 @@ \title{AEMET Meteorological warnings} \source{ \url{https://www.aemet.es/en/eltiempo/prediccion/avisos}. + +\url{https://www.aemet.es/es/eltiempo/prediccion/avisos/ayuda}. See also +Annex 2 and Annex 3 docs, linked in this page. } \usage{ -aemet_alerts(ccaa = NULL, lang = c("es", "en"), verbose = FALSE) +aemet_alerts( + ccaa = NULL, + lang = c("es", "en"), + verbose = FALSE, + return_sf = FALSE, + extract_metadata = FALSE, + progress = TRUE +) } \arguments{ \item{ccaa}{A vector of names for autonomous communities or \code{NULL} to get all @@ -18,9 +28,21 @@ the autonomous communities.} \item{verbose}{Logical \code{TRUE/FALSE}. Provides information about the flow of information between the client and server.} + +\item{return_sf}{Logical \code{TRUE} or \code{FALSE}. +Should the function return an \code{\link[sf:sf]{sf}} spatial object? If \code{FALSE} +(the default value) it returns a \code{\link[tibble:tibble]{tibble}}. Note that +you need to have the \CRANpkg{sf} package installed.} + +\item{extract_metadata}{Logical \code{TRUE/FALSE}. On \code{TRUE} the output is +a \code{\link[tibble:tibble]{tibble}} with the description of the fields. See also +\code{\link[=get_metadata_aemet]{get_metadata_aemet()}}.} + +\item{progress}{Logical, display a \code{\link[cli:cli_progress_bar]{cli::cli_progress_bar()}} object. If +\code{verbose = TRUE} won't be displayed.} } \value{ -A \CRANpkg{sf} object. +A \code{\link[tibble:tibble]{tibble}} or a \CRANpkg{sf} object. } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} Get a database of current meteorological @@ -28,18 +50,23 @@ alerts. } \examples{ \dontshow{if (aemet_detect_api_key()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} - # Display names of CCAAs -unique(mapSpain::esp_codelist$ine.ccaa.name) +library(dplyr) +aemet_alert_zones() \%>\% + select(NOM_CCAA) \%>\% + distinct() # Base map cbasemap <- mapSpain::esp_get_ccaa(ccaa = c( - "Galicia", - "Asturias, Principado de" + "Galicia", "Asturias", "Cantabria", + "Euskadi" )) # Alerts -alerts_north <- aemet_alerts(ccaa = c("Galicia", "Asturias, Principado de")) +alerts_north <- aemet_alerts( + ccaa = c("Galicia", "Asturias", "Cantabria", "Euskadi"), + return_sf = TRUE +) # If any alert if (inherits(alerts_north, "sf")) { @@ -56,7 +83,7 @@ if (inherits(alerts_north, "sf")) { data = cbasemap, fill = "transparent", color = "black", linewidth = 0.5 ) + - facet_wrap(vars(day, `AEMET-Meteoalerta fenomeno`)) + + facet_grid(vars(`AEMET-Meteoalerta fenomeno`), vars(day)) + scale_fill_manual(values = c( "amarillo" = "yellow", naranja = "orange", "rojo" = "red" @@ -65,11 +92,12 @@ if (inherits(alerts_north, "sf")) { \dontshow{\}) # examplesIf} } \seealso{ -\code{\link[mapSpain:esp_get_ccaa]{mapSpain::esp_get_ccaa()}}. See also \link[mapSpain:esp_codelist]{mapSpain::esp_codelist}, -\code{\link[mapSpain:esp_dict]{mapSpain::esp_dict_region_code()}} and \strong{Examples} to get the names of the +\code{\link[=aemet_alert_zones]{aemet_alert_zones()}}. See also \link[mapSpain:esp_codelist]{mapSpain::esp_codelist}, +\code{\link[mapSpain:esp_dict]{mapSpain::esp_dict_region_code()}} to get the names of the autonomous communities. Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, \code{\link{aemet_extremes_clim}()}, diff --git a/man/aemet_beaches.Rd b/man/aemet_beaches.Rd index 897f8252..2f12981c 100644 --- a/man/aemet_beaches.Rd +++ b/man/aemet_beaches.Rd @@ -63,6 +63,7 @@ ggplot(prov) + \code{\link[=aemet_forecast_beaches]{aemet_forecast_beaches()}} Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_daily_clim}()}, \code{\link{aemet_extremes_clim}()}, diff --git a/man/aemet_daily.Rd b/man/aemet_daily.Rd index abfca2a2..d3bfa00a 100644 --- a/man/aemet_daily.Rd +++ b/man/aemet_daily.Rd @@ -95,6 +95,7 @@ glimpse(meta$campos) \code{\link[=aemet_api_key]{aemet_api_key()}}, \code{\link[=as.Date]{as.Date()}} Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_extremes_clim}()}, diff --git a/man/aemet_extremes_clim.Rd b/man/aemet_extremes_clim.Rd index 5d6ffcdc..6605a34c 100644 --- a/man/aemet_extremes_clim.Rd +++ b/man/aemet_extremes_clim.Rd @@ -57,6 +57,7 @@ glimpse(obs) \code{\link[=aemet_api_key]{aemet_api_key()}} Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_forecast.Rd b/man/aemet_forecast.Rd index 7a34f8d0..5468b1ef 100644 --- a/man/aemet_forecast.Rd +++ b/man/aemet_forecast.Rd @@ -153,6 +153,7 @@ working with \code{sf} objects of municipalities (see \code{\link[mapSpain:esp_get_munic]{mapSpain::esp_get_munic()}} and \strong{Examples}). Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_forecast_beaches.Rd b/man/aemet_forecast_beaches.Rd index 819e4e7d..2e3351ce 100644 --- a/man/aemet_forecast_beaches.Rd +++ b/man/aemet_forecast_beaches.Rd @@ -69,6 +69,7 @@ ggplot(forecast_b) + \code{\link[=aemet_beaches]{aemet_beaches()}} for beaches codes. Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_last_obs.Rd b/man/aemet_last_obs.Rd index 759f14cf..d2ac3e2b 100644 --- a/man/aemet_last_obs.Rd +++ b/man/aemet_last_obs.Rd @@ -51,6 +51,7 @@ glimpse(obs) } \seealso{ Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_monthly.Rd b/man/aemet_monthly.Rd index f97407c3..53c152b4 100644 --- a/man/aemet_monthly.Rd +++ b/man/aemet_monthly.Rd @@ -82,6 +82,7 @@ glimpse(obs) } \seealso{ Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_normal.Rd b/man/aemet_normal.Rd index cc9c3177..9d72b34b 100644 --- a/man/aemet_normal.Rd +++ b/man/aemet_normal.Rd @@ -64,6 +64,7 @@ glimpse(obs) } \seealso{ Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/man/aemet_stations.Rd b/man/aemet_stations.Rd index 8bbe5f4e..702e8b63 100644 --- a/man/aemet_stations.Rd +++ b/man/aemet_stations.Rd @@ -46,6 +46,7 @@ identical(stations, stations2) } \seealso{ Other aemet_api_data: +\code{\link{aemet_alert_zones}()}, \code{\link{aemet_alerts}()}, \code{\link{aemet_beaches}()}, \code{\link{aemet_daily_clim}()}, diff --git a/tests/testthat/_snaps/aemet_alert_zones.md b/tests/testthat/_snaps/aemet_alert_zones.md new file mode 100644 index 00000000..bd73f984 --- /dev/null +++ b/tests/testthat/_snaps/aemet_alert_zones.md @@ -0,0 +1,16 @@ +# Errors and validations + + Code + aemet_alert_zones(return_sf = "A") + Condition + Error in `aemet_alert_zones()`: + ! is.logical(return_sf) is not TRUE + +--- + + Code + aemet_alert_zones(verbose = "A") + Condition + Error in `aemet_alert_zones()`: + ! is.logical(verbose) is not TRUE + diff --git a/tests/testthat/_snaps/helpers.md b/tests/testthat/_snaps/helpers.md index b291cdb1..3ae47a84 100644 --- a/tests/testthat/_snaps/helpers.md +++ b/tests/testthat/_snaps/helpers.md @@ -48,3 +48,35 @@ Output [1] "2020-12-31" +--- + + Code + first_day_of_year() + Condition + Error in `first_day_of_year()`: + ! Year can't be missing + +--- + + Code + last_day_of_year() + Condition + Error in `last_day_of_year()`: + ! Year can't be missing + +--- + + Code + first_day_of_year("A") + Condition + Error in `first_day_of_year()`: + ! Year need to be numeric + +--- + + Code + last_day_of_year("B") + Condition + Error in `last_day_of_year()`: + ! Year need to be numeric + diff --git a/tests/testthat/test-aemet_alert_zones.R b/tests/testthat/test-aemet_alert_zones.R new file mode 100644 index 00000000..1ac9e210 --- /dev/null +++ b/tests/testthat/test-aemet_alert_zones.R @@ -0,0 +1,38 @@ +test_that("Errors and validations", { + # Validations + expect_snapshot(aemet_alert_zones(return_sf = "A"), error = TRUE) + expect_snapshot(aemet_alert_zones(verbose = "A"), error = TRUE) +}) + + +test_that("Online", { + skip_on_cran() + skip_if_offline() + + + # First clean cache + cached_df <- file.path(tempdir(), "aemet_alert_zones.gpkg") + cached_date <- file.path(tempdir(), "aemet_alert_zone_date.rds") + + unlink(cached_df) + unlink(cached_date) + + # First download + s <- aemet_alert_zones() + + + # Now is cached + expect_message( + aemet_alert_zones(verbose = TRUE), + regexp = "Loading alert zones from temporal cached file" + ) + + st1 <- aemet_alert_zones() + expect_s3_class(st1, "tbl_df") + + # sf + alll_sf <- aemet_alert_zones(return_sf = TRUE) + + expect_s3_class(alll_sf, "sf") + expect_s3_class(alll_sf, "tbl") +}) diff --git a/tests/testthat/test-aemet_alerts.R b/tests/testthat/test-aemet_alerts.R index dad4c03f..9ba5a352 100644 --- a/tests/testthat/test-aemet_alerts.R +++ b/tests/testthat/test-aemet_alerts.R @@ -7,6 +7,16 @@ test_that("Errors", { expect_snapshot(aemet_alerts(lang = "frr"), error = TRUE) }) +test_that("Metadata", { + skip_on_cran() + skip_if_offline() + skip_if_not(aemet_detect_api_key(), message = "No API KEY") + + meta <- aemet_alerts(st, extract_metadata = TRUE) + # Same as + meta2 <- aemet_alerts("NOEXIST", extract_metadata = TRUE) + expect_identical(meta, meta2) +}) test_that("In no alerts", { skip_on_cran() @@ -15,12 +25,12 @@ test_that("In no alerts", { df <- aemet_hlp_alerts_master() - seehere <- sort(unique(df$aemet)) + seehere <- sort(unique(df$COD_CCAA)) # Check if there is any CCAA without alert to perform test df_map <- ccaa_to_aemet() - df_map <- df_map[!df_map$aemet %in% seehere, ] + df_map <- df_map[!df_map$COD_CCAA %in% seehere, ] skip_if(nrow(df_map) == 0, message = "All CCAA with alerts, can't perform test" @@ -41,12 +51,12 @@ test_that("In alerts", { df <- aemet_hlp_alerts_master() - seehere <- sort(unique(df$aemet)) + seehere <- sort(unique(df$COD_CCAA)) # Check if there is any CCAA with alert to perform test df_map <- ccaa_to_aemet() - df_map <- df_map[df_map$aemet %in% seehere, ] + df_map <- df_map[df_map$COD_CCAA %in% seehere, ] skip_if(nrow(df_map) == 0, message = "All CCAA without alerts, can't perform test" @@ -56,9 +66,14 @@ test_that("In alerts", { ca <- mapSpain::esp_dict_region_code(smp, "codauto") - expect_message(res <- aemet_alerts(ca, verbose = TRUE)) + expect_message(res <- aemet_alerts(ca, verbose = TRUE, return_sf = TRUE)) expect_s3_class(res, c("sf", "tbl_df", "tbl", "data.frame"), exact = TRUE) + # No sf + res_tbl <- aemet_alerts(ca, return_sf = FALSE) + + expect_identical(sf::st_drop_geometry(res), res_tbl) + # Now lang res2 <- aemet_alerts(ca, verbose = TRUE, lang = "en") diff --git a/tests/testthat/test-helpers.R b/tests/testthat/test-helpers.R index 4b9c8c08..d10ae83c 100644 --- a/tests/testthat/test-helpers.R +++ b/tests/testthat/test-helpers.R @@ -16,4 +16,10 @@ test_that("dms2decdegrees_2 works", { test_that("first and last works", { expect_snapshot(first_day_of_year(2000)) expect_snapshot(last_day_of_year(2020)) + + expect_snapshot(first_day_of_year(), error = TRUE) + expect_snapshot(last_day_of_year(), error = TRUE) + + expect_snapshot(first_day_of_year("A"), error = TRUE) + expect_snapshot(last_day_of_year("B"), error = TRUE) }) diff --git a/vignettes/articles/api_metadata.Rmd b/vignettes/articles/api_metadata.Rmd index 62c2b5c3..e2a03f6b 100644 --- a/vignettes/articles/api_metadata.Rmd +++ b/vignettes/articles/api_metadata.Rmd @@ -62,11 +62,19 @@ mon$fun <- "aemet_monthly" norm <- aemet_normal_clim_all(extract_metadata = TRUE)$campos norm$fun <- "aemet_normal" +alert_meta <- aemet_alerts(extract_metadata = TRUE) +alerts <- tibble::tibble( + fun = "aemet_alerts", + descripcion = as.character(alert_meta$campos) +) + + end <- daily %>% - bind_rows(exend, fday, fhour, fbeach, lobs, mon, norm) %>% + bind_rows(exend, fday, fhour, fbeach, lobs, mon, norm, alerts) %>% relocate(fun, .before = 1) %>% as_tibble() %>% - distinct_all() + distinct() %>% + arrange(fun) library(reactable) reactable::reactable(end, diff --git a/vignettes/climaemet.Rmd b/vignettes/climaemet.Rmd index e85e3507..7ec07534 100644 --- a/vignettes/climaemet.Rmd +++ b/vignettes/climaemet.Rmd @@ -87,23 +87,22 @@ See how a `tibble` is displayed: # See a tibble in action aemet_last_obs("9434") -#> # A tibble: 13 × 25 -#> idema lon fint prec alt vmax vv dv lat dmax ubi pres hr stdvv ts pres_nmar tamin ta tamax tpr stddv -#> -#> 1 9434 -1.00 2024-07-30 01:00:00 0 249 9.8 2.6 300 41.7 250 ZARAGOZA … 986. 34 0.6 30.5 1014. 30.1 30.6 32 13 118 -#> 2 9434 -1.00 2024-07-30 02:00:00 0 249 9.2 6.7 106 41.7 115 ZARAGOZA … 985. 35 1 29.2 1013. 29 29.6 30.8 12.5 16 -#> 3 9434 -1.00 2024-07-30 03:00:00 0 249 8.4 2.7 163 41.7 130 ZARAGOZA … 986 38 0.8 28.8 1014. 29.3 29.4 29.9 13.6 17 -#> 4 9434 -1.00 2024-07-30 04:00:00 0 249 8.5 2.5 196 41.7 235 ZARAGOZA … 988. 42 1.2 29 1016. 29.2 29.5 29.6 15.3 43 -#> 5 9434 -1.00 2024-07-30 05:00:00 0 249 11.7 7.8 141 41.7 120 ZARAGOZA … 986. 42 1.5 28.4 1014. 28.1 28.7 29.5 14.5 11 -#> 6 9434 -1.00 2024-07-30 06:00:00 0 249 10.6 1.6 338 41.7 135 ZARAGOZA … 986. 41 0.8 28.8 1015. 28.1 28.6 28.7 14 145 -#> 7 9434 -1.00 2024-07-30 07:00:00 0 249 3.5 1.6 53 41.7 315 ZARAGOZA … 987. 44 0.5 29.9 1015 28.6 28.8 29.2 15.3 21 -#> 8 9434 -1.00 2024-07-30 08:00:00 0 249 5.1 2.7 112 41.7 75 ZARAGOZA … 986. 44 0.8 33.9 1015. 28.8 29.7 29.8 16.1 15 -#> 9 9434 -1.00 2024-07-30 09:00:00 0 249 5.8 3.5 120 41.7 125 ZARAGOZA … 986. 36 0.7 37.4 1014. 29.6 32.4 32.4 15.4 13 -#> 10 9434 -1.00 2024-07-30 10:00:00 0 249 8.2 3.4 334 41.7 108 ZARAGOZA … 986. 37 1.4 32.9 1014. 31.2 31.6 32.9 15.2 23 -#> 11 9434 -1.00 2024-07-30 11:00:00 0 249 7.6 3.2 90 41.7 55 ZARAGOZA … 986. 31 0.8 40.4 1013. 31.6 35 35.1 15.3 16 -#> 12 9434 -1.00 2024-07-30 12:00:00 0 249 7 4.4 99 41.7 110 ZARAGOZA … 985 25 1 43 1012. 35 37.4 37.4 14 12 -#> 13 9434 -1.00 2024-07-30 13:00:00 0 249 8.4 5.2 93 41.7 120 ZARAGOZA … 984 23 1.2 44.5 1011. 37.4 39 39 14.1 14 -#> # ℹ 4 more variables: inso , tss5cm , pacutp , tss20cm +#> # A tibble: 12 × 25 +#> idema lon fint prec alt vmax vv dv lat dmax ubi pres hr stdvv ts pres_nmar tamin ta tamax +#> +#> 1 9434 -1.00 2024-07-31 01:00:00 0 249 7.4 2.5 111 41.7 123 ZARAGOZ… 984. 28 0.4 27.4 1012. 28.9 28.9 30.4 +#> 2 9434 -1.00 2024-07-31 02:00:00 0 249 4.6 2.4 123 41.7 143 ZARAGOZ… 983. 31 0.4 26.9 1012. 27.5 27.8 28.9 +#> 3 9434 -1.00 2024-07-31 03:00:00 0 249 4.2 1.8 99 41.7 90 ZARAGOZ… 983. 34 0.6 24.9 1012. 26.1 26.1 27.8 +#> 4 9434 -1.00 2024-07-31 04:00:00 0 249 4.1 0.9 54 41.7 133 ZARAGOZ… 984. 36 0.4 23.9 1012. 25.4 25.4 26.1 +#> 5 9434 -1.00 2024-07-31 05:00:00 0 249 3.5 2.1 101 41.7 88 ZARAGOZ… 984. 40 0.3 23.1 1013. 24 24 25.4 +#> 6 9434 -1.00 2024-07-31 06:00:00 0 249 3.4 2.5 98 41.7 100 ZARAGOZ… 984. 41 0.3 25.4 1013 23.8 24.7 24.7 +#> 7 9434 -1.00 2024-07-31 07:00:00 0 249 2.9 1.7 84 41.7 93 ZARAGOZ… 985. 38 0.3 30.5 1013. 24.7 27 27 +#> 8 9434 -1.00 2024-07-31 08:00:00 0 249 4.7 2.7 100 41.7 110 ZARAGOZ… 985. 33 0.7 33.6 1013. 27 29.5 29.5 +#> 9 9434 -1.00 2024-07-31 09:00:00 0 249 5.2 2.5 96 41.7 113 ZARAGOZ… 984. 30 0.5 36.5 1012. 29.5 31.6 31.6 +#> 10 9434 -1.00 2024-07-31 10:00:00 0 249 4 2.8 97 41.7 93 ZARAGOZ… 984. 28 0.6 39.2 1012. 31.6 33.9 33.9 +#> 11 9434 -1.00 2024-07-31 11:00:00 0 249 4.3 2.7 97 41.7 110 ZARAGOZ… 984. 24 0.6 41 1011. 33.9 36 36 +#> 12 9434 -1.00 2024-07-31 12:00:00 0 249 5.2 2.9 101 41.7 123 ZARAGOZ… 983 22 0.9 42.9 1010. 36 37.9 37.9 +#> # ℹ 6 more variables: tpr , stddv , inso , tss5cm , pacutp , tss20cm ``` Note that when possible, data representing dates and numbers are converted to diff --git a/vignettes/example-gif.gif b/vignettes/example-gif.gif index 4d23fe5e..6ddadfad 100644 Binary files a/vignettes/example-gif.gif and b/vignettes/example-gif.gif differ