From 34476b4b12233bce51d0a17c05301250c0896b75 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 15:31:51 +0100 Subject: [PATCH 01/83] Fix get_mis_time_steps for custom files --- R/_delta_downscale.R | 48 + R/get_mis_time_steps.R | 10 +- .../rawdata_scripts/data_files/spratt2016.txt | 895 ++++++++++++++++++ tests/testthat/test_get_mis_time_steps.R | 12 + 4 files changed, 960 insertions(+), 5 deletions(-) create mode 100644 R/_delta_downscale.R create mode 100644 inst/rawdata_scripts/data_files/spratt2016.txt create mode 100644 tests/testthat/test_get_mis_time_steps.R diff --git a/R/_delta_downscale.R b/R/_delta_downscale.R new file mode 100644 index 00000000..22e07299 --- /dev/null +++ b/R/_delta_downscale.R @@ -0,0 +1,48 @@ +delta_compute <- function(x_modern, high_res) { + # get the extent of the high res reference + + # check that it is compatible with the x_modern + + # disaggregate the x_modern SpatRaster to the resolution of the high res with "near" + + # compute anomalies against the modern + + +} + +delta_downscale <- function (x, delta_rast, time_point, sea_level_path) { + # check that extent and resolutions are compatible + + # sea level https://www.ncdc.noaa.gov/paleo-search/study/19982 + etopo1 <- terra::rast("ETOPO1_Ice_c_gmt4.grd") + sea_level <- read.table("spratt2016.txt", header = TRUE, row.names = 1) + + # downscale x with near + + # adjust the land area based on sea level + + # expand with bilinear for areas without information for both x and delta_rast + + # apply the delta_rast to x + +} + + +eaf <- extent(30,55,-15,20) +r <- rast() +e <- ext(r) +as.vector(e) +as.character(e) + +ext(r) <- c(0, 2.5, 0, 1.5) +af_precip <- crop(bio12_rasterbrick, eaf) #crop to africa + + +eaf <- terra::ext(30,55,-15,20) + +etopo1 <- terra::rast("ETOPO1_Ice_c_gmt4.grd") +etopo30 <- terra::agg(etopo1,fact=30) + + +bi <- terra::boundaries(land_mask) +bi_up <- terra::disagg(bi, fact = 30) diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index f6272052..4f6cf286 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -19,13 +19,13 @@ get_mis_time_steps <- function(mis, dataset, path_to_nc = NULL) { if (is.null(path_to_nc)) { path_to_nc <- get_pastclimdata_path() + # we get the first available file to get info for the dataset + possible_vars <- get_vars_for_dataset(dataset) + this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name + path_to_nc <- file.path(path_to_nc, this_file) } - # we get the first available file to get info for the dataset - possible_vars <- get_vars_for_dataset(dataset) - this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name - this_file <- file.path(path_to_nc, this_file) - climate_nc <- ncdf4::nc_open(this_file) + climate_nc <- ncdf4::nc_open(path_to_nc) time_steps <- (climate_nc$dim$time$vals) ncdf4::nc_close(climate_nc) mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis diff --git a/inst/rawdata_scripts/data_files/spratt2016.txt b/inst/rawdata_scripts/data_files/spratt2016.txt new file mode 100644 index 00000000..ae8fef42 --- /dev/null +++ b/inst/rawdata_scripts/data_files/spratt2016.txt @@ -0,0 +1,895 @@ +# Global Sea Level Reconstruction using Stacked Records from 0-800 ka +#----------------------------------------------------------------------- +# World Data Center for Paleoclimatology, Boulder +# and +# NOAA Paleoclimatology Program +#----------------------------------------------------------------------- +# NOTE: Please cite original publication, online resource and date accessed when using this data. +# If there is no publication information, please cite Investigator, title, online resource and date accessed. +# +# Description/Documentation lines begin with # +# Data lines have no # +# +# Online_Resource: http://www.ncdc.noaa.gov/paleo/study/19982 +# Online_Resource: http://www1.ncdc.noaa.gov/pub/data/paleo/paleocean/by_contributor/spratt2016/spratt2016.txt +# +# Archive: Paleoceanography +# +# Parameter_Keywords: reconstruction +#--------------------------------------- +# Contribution_Date +# Date: 2016-04-12 +#--------------------------------------- +# Title +# Study_Name: Global Sea Level Reconstruction using Stacked Records from 0-800 ka +#--------------------------------------- +# Investigators +# Investigators: Spratt, Rachel M.; Lisiecki, Lorraine E. +#--------------------------------------- +# Description and Notes +# Description: Late Pleistocene sea level stack based on marine sediment core data (foraminiferal carbonate d18O) as estimated by several different techniques in seven different studies. Sea level stack is a compilation data from the following published sea level reconstructions: Bintanja, R., Roderik, S.W., van de Wal, O. J.: Modeled atmospheric temperatures and global sea levels over the past million years, Nature, 437, 125-128 doi:10.1038/nature03975, 2005. ; Elderfield, H., Ferretti, P., Greaves, M., Crowhurst, S.J., McCave, I.N., Hodell, D.A., Piotrowski, A.M.: Evolution of ocean temperature and ice volume through the Mid-Pleistocene Climate Transition, Science, 337,6095, 704-709, doi:10.1126/science.1221294, 2012.; Rohling, E.J., Grant, K., Bolshaw, M., Roberts, A.P., Siddall, M., Hemleben, C., Kucera, M.: Antarctic temperature and global sea level closely coupled over the past five glacial cycles. Nat. Geosci., 2, 500-504, 2009.; Rohling, E.J., Grant, K.M., Bolshaw, M., Roberts, A. P., Siddall, M., Hemleben C. Kucera, M., Foster, G.L., Marino, G., Roberts, A.P., Tamisiea, M.E., and Williams, F.: Sea-level and deep-sea-temperature variability over the past 5.3 million years, Nature, 508, 477-482, 2014.; Shakun, J.D., Lea, D.W., Lisiecki, L.E., Raymo, M.E.: An 800-kyr record of global surface ocean d18O and implications for ice volume-temperature coupling, Earth. Planet. Sc. Lett., 426, 58-68, 2015.; Sosdian S., Rosenthal Y.: Deep-Sea Temperature and Ice Volume Changes Across the Pliocene-Pleistocene Climate Transitions, Science, 17, 325, 5938, 306-310, doi: 10.1126/science.1169938, 2009.; Waelbroeck, C., Labeyrie, L., Michel, E., Duplessy J.C., McManus J.: Sea-level and deep water temperature changes derived from benthic foraminifera isotopic records, Quaternary Sci. Rev., 21, 295-305, 2002. +# Provided Keywords: sea level, Pleistocene +#--------------------------------------- +# Publication +# Authors: Spratt, Rachel M. and Lorraine E. Lisiecki +# Published_Date_or_Year: 2016 +# Published_Title: A Late Pleistocene sea level stack +# Journal_Name: Climate of the Past +# Volume: in press +# Edition: +# Issue: +# Pages: +# Report Number: +# DOI: 10.5194/cp-12-1-2016 +# Online_Resource: +# Full_Citation: +# Abstract: Late Pleistocene sea level has been reconstructed from ocean sediment core data using a wide variety of proxies and models. However, the accuracy of individual reconstructions is limited by measurement error, local variations in salinity and temperature, and assumptions particular to each technique. Here we present a sea level stack (average) which increases the signal-to-noise ratio of individual reconstructions. Specifically, we perform principal component analysis (PCA) on seven records from 0-430 ka and five records from 0-798 ka. The first principal component, which we use as the stack, describes ~80% of the variance in the data and is similar using either five or seven records. After scaling the stack based on Holocene and Last Glacial Maximum (LGM) sea level estimates, the stack agrees to within 5 m with isostatically adjusted coral sea level estimates for Marine Isotope Stages 5e and 11 (125 and 400 ka, respectively). Bootstrapping and random sampling yield mean uncertainty estimates of 9-12 m (1s) for the scaled stack. Sea level change accounts for about 45% of the total orbital-band variance in benthic d18O, compared to a 65% contribution during the LGM-to-Holocene transition. Additionally, the second and third principal components of our analyses reflect differences between proxy records associated with spatial variations in the d18O of seawater. +#--------------------------------------- +# Funding_Agency +# Funding_Agency_Name: +# Grant: +#--------------------------------------- +# Site Information +# Site_Name: Global 61.5N to 41S +# Location: +# Country: +# Northernmost_Latitude: 61.425 +# Southernmost_Latitude: -40.937 +# Easternmost_Longitude: 180 +# Westernmost_Longitude: -180 +# Elevation: +#--------------------------------------- +# Data_Collection +# Collection_Name: global_sealevel_stack Spratt16 +# First_Year: -798000 +# Last_Year: 0 +# Time_Unit: cal yr BP +# Core_Length: +# Notes: Sea level stack is a compilation data from the following published sea level reconstructions: Bintanja, R., Roderik, S.W., van de Wal, O. J.: Modeled atmospheric temperatures and global sea levels over the past million years, Nature, 437, 125-128 doi:10.1038/nature03975, 2005. ; Elderfield, H., Ferretti, P., Greaves, M., Crowhurst, S. J., McCave, I.N., Hodell, D. A., Piotrowski, A. M.: Evolution of ocean temperature and ice volume through the Mid-Pleistocene Climate Transition, Science, 337,6095, 704-709, doi:10.1126/science.1221294, 2012.; Rohling, E.J., Grant, K., Bolshaw, M., Roberts, A.P., Siddall, M., Hemleben, C., Kucera, M.: Antarctic temperature and global sea level closely coupled over the past five glacial cycles. Nat. Geosci., 2, 500–504, 2009.; Rohling, E. J., Grant, K.M., Bolshaw, M., Roberts, A. P., Siddall, M., Hemleben C. Kucera, M., Foster, G.L., Marino, G., Roberts, A.P., Tamisiea, M.E., and Williams, F.: Sea-level and deep-sea-temperature variability over the past 5.3 million years, Nature, 508, 477–482, 2014.; Shakun, J. D., Lea, D. W., Lisiecki, L. E., Raymo, M. E.: An 800-kyr record of global surface ocean d18O and implications for ice volume-temperature coupling, Earth. Planet. Sc. Lett., 426, 58–68, 2015.; Sosdian S., Rosenthal Y.: Deep-Sea Temperature and Ice Volume Changes Across the Pliocene-Pleistocene Climate Transitions, Science, 17, 325, 5938, 306-310, doi: 10.1126/science.1169938, 2009.; Waelbroeck, C., Labeyrie, L., Michel, E., Duplessy J.C., McManus J.: Sea-level and deep water temperature changes derived from benthic foraminifera isotopic records, Quaternary Sci. Rev., 21, 295–305, 2002. +#--------------------------------------- +# Species +# Species_Name: Any additional species should be entered in Columns C,D, etc. +# Common_Name: +# Tree_Species_Code: +#--------------------------------------- +# Chronology_Information +# Chronology: +# For cores with benthic foraminiferal carbonate d18O, age models were developed by aligning the carbonate d18O to the "LR04" benthic d18O stack (Lisiecki and Raymo, Paleoceanography, 2005). In most cases, this was the published timescale of the original record. For cores which lacked benthic carbonate d18O, their sea level estimates were aligned to the sea level estimates of the records that were already on the LR04 age model. +#--------------------------------------- +# Variables +# Data variables follow that are preceded by "##" in columns one and two. +# Variables list, one per line, shortname-tab-longname components (9 components: what, material, error, units, seasonality, archive, detail, method, C or N for Character or Numeric data) +## age_calkaBP Age,,,calendar ka BP,,,,,N +## SeaLev_shortPC1 Sea Level,,,meters above present day,,climate reconstructions,,Scaled first principal component of seven sea level reconstructions (0-430 ka),N +## SeaLev_shortPC1_err_sig Sea Level,,standard deviation from bootstrap,meters,,climate reconstructions,,Scaled first principal component of seven sea level reconstructions (0-430 ka),N +## SeaLev_shortPC1_err_lo Sea Level,,95% confidence interval, lower bound,meters,,climate reconstructions,,Scaled first principal component of seven sea level reconstructions (0-430 ka),N +## SeaLev_shortPC1_err_up Sea Level,,95% confidence interval, upper bound,meters,,climate reconstructions,,Scaled first principal component of seven sea level reconstructions (0-430 ka),N +## SeaLev_longPC1 Sea Level,,,meters above present day,,climate reconstructions,,Scaled first principal component of five sea level reconstructions (0-798 ka),N +## SeaLev_longPC1_err_sig Sea Level,,standard deviation from bootstrap,meters,,climate reconstructions,,Scaled first principal component of five sea level reconstructions (0-798 ka),N +## SeaLev_longPC1_err_lo Sea Level,,95% confidence interval, lower bound,meters,,climate reconstructions,,Scaled first principal component of five sea level reconstructions (0-798 ka),N +## SeaLev_longPC1_err_up Sea Level,,95% confidence interval, upper bound,meters,,climate reconstructions,,Scaled first principal component of five sea level reconstructions (0-798 ka),N +#------------------------ +# Data +# Data lines follow (have no #) +# Data line format - tab-delimited text, variable short name as header +# Missing Value: NaN +age_calkaBP SeaLev_shortPC1 SeaLev_shortPC1_err_sig SeaLev_shortPC1_err_lo SeaLev_shortPC1_err_up SeaLev_longPC1 SeaLev_longPC1_err_sig SeaLev_longPC1_err_lo SeaLev_longPC1_err_up +0 8.49 5.23 -1.72 17.93 8.96 5.72 -1.21 20.38 +1 7.63 4.87 -2.9 16.39 7.72 5.13 -2.77 17.1 +2 4.01 4.83 -4.51 13.59 5.96 4.69 -5.01 14.21 +3 4.35 4.72 -6.93 12.08 3.54 4.42 -7.28 10.9 +4 3.13 4.74 -10.43 8.41 1.88 4.39 -10.54 7.63 +5 0 4.57 -12.34 5.12 0 4.54 -13.3 4.37 +6 -4.01 5.04 -16.88 0.62 -2 5.43 -17.74 2.26 +7 -6.11 5.9 -22.41 -0.39 -5.38 6.89 -23.29 3.65 +8 -9.09 6.79 -28.86 -2.96 -7.12 8.66 -31.66 4.08 +9 -15.83 8.3 -37.35 -5.22 -11.6 10.87 -43.21 2.56 +10 -24.59 10.3 -50.17 -9.89 -23.18 13.05 -56.03 -3.78 +11 -35.85 11.93 -63.99 -17.59 -36.19 14.92 -70.95 -11.27 +12 -51.05 12.3 -76.8 -28.6 -49.72 15.38 -83.72 -23.99 +13 -66.3 12.38 -89.53 -39.9 -64.58 15.65 -96.11 -34.83 +14 -76.64 12.35 -100.98 -53.14 -75.97 16.08 -107.68 -44.5 +15 -86.57 12.34 -111.08 -61.67 -86.08 16.33 -117.47 -53.8 +16 -98.06 12.28 -119.52 -70.64 -98.43 16.26 -125.37 -61.94 +17 -107.3 11.96 -125.43 -77.7 -108.23 15.9 -129.99 -68.78 +18 -113.01 11.16 -129.44 -84.14 -113.85 15.05 -131.8 -73.18 +19 -116.68 10.61 -131.87 -89.61 -117 14.4 -133.08 -77.34 +20 -117.56 10.08 -134.08 -95.64 -116.4 13.9 -134.48 -81.87 +21 -120.01 9.14 -134.42 -99.63 -118.61 12.14 -134.47 -91.28 +22 -125.82 7.66 -134.52 -106.07 -125.91 9.97 -134.42 -98.21 +23 -128.72 5.27 -134.73 -113.3 -128.71 6.49 -134.95 -107.99 +24 -130 4.38 -134.58 -118.09 -130 5.48 -134.67 -113.97 +25 -126.89 4.5 -134.18 -116.35 -124.87 5.87 -133.38 -110.94 +26 -122.4 5.15 -133.23 -113.54 -119.21 6.84 -132.58 -106.41 +27 -118.28 5.44 -130.83 -109.88 -114.93 6.7 -128.15 -102.77 +28 -115.19 5.74 -126.93 -105.25 -112.19 6.88 -123.71 -97.4 +29 -110.87 5.64 -123.11 -100.95 -107.65 6.6 -119.34 -94.56 +30 -105.78 5.72 -119.56 -97.59 -102.07 6.62 -116.2 -90.66 +31 -103.5 5.81 -115.83 -93.16 -99.97 6.49 -112.82 -87.56 +32 -100.63 6.14 -113.45 -89.89 -96.01 6.64 -109.99 -83.39 +33 -97.48 6.4 -112.63 -87.9 -93.69 7.04 -110.23 -81.11 +34 -97.37 6.64 -113.54 -87.15 -93.5 7.22 -109.73 -81.26 +35 -98.83 6.67 -112.65 -85.72 -97.21 7.47 -110.41 -80.66 +36 -99.39 6.81 -112.85 -85.29 -97.2 7.4 -110.04 -81.44 +37 -95.27 6.54 -112.21 -85.74 -93.4 6.97 -110.08 -82.41 +38 -93.1 6.16 -109.47 -84.17 -91.82 6.34 -105.63 -80.62 +39 -90.3 5.66 -102.83 -81.14 -88.47 6.05 -100.13 -76.35 +40 -85.9 5.74 -98.89 -76.15 -83.13 6.16 -96.46 -72.62 +41 -79.76 5.67 -94.67 -72.62 -78.15 5.79 -91.91 -69.35 +42 -75.76 5.2 -90.5 -70.82 -74.61 5.18 -87.44 -67.59 +43 -73.91 4.72 -86.67 -68.74 -73.02 4.9 -84.37 -65.65 +44 -76.18 4.8 -86.65 -67.68 -72.61 4.81 -82.85 -64.04 +45 -74.49 4.75 -85.47 -67.24 -71.01 5.04 -82.81 -63.31 +46 -75.78 5.1 -87.56 -67.52 -70.25 5.33 -83.46 -63.27 +47 -73.7 5.05 -87.1 -67.3 -71.4 5.51 -83.75 -62.55 +48 -76.71 5.49 -86.89 -65.61 -72.53 5.81 -83.47 -60.96 +49 -73.75 6.04 -84.42 -61.64 -70.54 6.08 -82.85 -58.88 +50 -68.3 6.49 -84.91 -58.55 -66.69 6.4 -82.6 -56.45 +51 -63.11 6.1 -82.32 -58.02 -65.99 7.09 -83.41 -54.32 +52 -67.88 6.37 -81.7 -57.24 -68.05 8.26 -86.9 -52.81 +53 -68.85 6.73 -82.99 -55.98 -69.87 8.81 -90.39 -53.31 +54 -70.05 7.18 -87.88 -58.82 -71.66 8.88 -91.32 -56.22 +55 -72.26 7.5 -91.66 -60.95 -74.43 8.82 -93.18 -58.04 +56 -78.03 7.9 -95.37 -63.04 -78.68 8.6 -95.38 -61.12 +57 -82.74 7.91 -96.96 -65.22 -81.58 8.51 -96.03 -62.47 +58 -85.71 8.31 -99.32 -66.11 -81.85 9.41 -96.26 -60.6 +59 -82.94 8.97 -101.54 -65.19 -76.51 10.52 -96.88 -56.87 +60 -84.73 8.89 -101.4 -66 -77.47 10.88 -96.11 -53.14 +61 -86.56 8.68 -100.68 -63.69 -78.58 10.85 -94.61 -52.59 +62 -84.32 8.77 -101.28 -65.01 -79.92 10.8 -95.35 -52.06 +63 -84.75 8.15 -100.11 -66.5 -79.92 9.73 -94.29 -57.7 +64 -83.48 7.06 -97.89 -69.28 -77.22 8.38 -94.13 -62.19 +65 -82.72 6.97 -97.61 -70.76 -78.75 7.52 -93.97 -65.09 +66 -82.47 7.09 -95.62 -68.69 -80.56 8.15 -92.94 -61.24 +67 -79.37 6.84 -92.04 -64.64 -80.05 8.98 -91.88 -56.91 +68 -71.06 7.3 -88.56 -60.46 -68.37 10.11 -89.84 -50.72 +69 -68.11 8.84 -85.22 -51.15 -64.56 11.75 -87.23 -40.67 +70 -65.39 9.4 -79.2 -42.67 -60.11 12.01 -79.15 -31.38 +71 -51.43 10.04 -75 -35.42 -50.56 12.81 -74.57 -24.32 +72 -46.71 10.17 -69.63 -29.21 -43.6 12.96 -69.85 -18.74 +73 -43.84 9.97 -63.09 -23.31 -38.44 12.79 -63.57 -14.2 +74 -36.18 9.68 -58.1 -21.24 -33.23 12.02 -57.33 -11.56 +75 -33.06 9.11 -54.25 -18.43 -27.81 10.95 -52.9 -10.46 +76 -31.58 8.27 -48.54 -16.13 -26.3 9.82 -48.48 -10.78 +77 -27.69 8.14 -47.93 -16.5 -26.85 9.73 -49.32 -10.95 +78 -28.8 7.77 -47.08 -16.64 -27.39 9.61 -50.73 -12.8 +79 -32.95 7.26 -46.29 -18.08 -32.53 9.36 -51.9 -14.88 +80 -30.85 6.98 -46.3 -19.38 -32.48 8.55 -51.28 -16.91 +81 -31.36 7.63 -47.64 -17.23 -32.68 9.9 -50.7 -10.68 +82 -29.64 8.01 -47.38 -15.87 -31.47 10.85 -49.49 -8.32 +83 -25.57 8.5 -47.98 -14.51 -24.15 11.71 -50.32 -6.17 +84 -30.66 8.78 -49.34 -14.48 -27.87 11.41 -48.53 -4.67 +85 -29.99 9.31 -51.51 -15.62 -29.96 11.38 -49.44 -5.73 +86 -38.77 9.49 -56.47 -18.08 -33.19 11.17 -52.56 -9.52 +87 -43.09 9.38 -58.24 -20.89 -36.68 10.74 -53.59 -11.63 +88 -46.6 8.91 -59.96 -25.72 -41.15 10.33 -54.29 -16.12 +89 -43.44 8.46 -60.47 -28.6 -40.65 10.16 -55.53 -17.01 +90 -43.63 7.86 -59.49 -28.89 -38.17 9.71 -55.64 -17.34 +91 -40.3 7.57 -57.19 -27.2 -35.91 9.1 -54.7 -17.93 +92 -41.67 7.59 -56.66 -26.28 -39.37 8.27 -53.95 -21.94 +93 -42.16 6.86 -53.77 -27.85 -41.76 7.38 -53.51 -25.78 +94 -40.52 6.68 -52.39 -26.54 -41.82 7.82 -53.09 -22.36 +95 -34.24 7.47 -50.8 -21.52 -34.94 8.88 -51.46 -16.76 +96 -27.74 7.82 -47.5 -16.22 -25.61 9.35 -48.3 -12.73 +97 -22.19 7.77 -42.02 -11.02 -20.95 8.82 -42.96 -9.02 +98 -21.19 7.38 -38.21 -9.82 -19.34 8.28 -39.79 -7.23 +99 -17.26 6.77 -35.67 -9.34 -16.97 8.25 -38.04 -5.59 +100 -19.49 7.09 -36.28 -9.29 -17 8.92 -40.14 -6.04 +101 -17.72 7.5 -37.95 -9.32 -16.69 9.75 -42.09 -4.56 +102 -25.03 7.58 -40.61 -10.86 -28.14 9.47 -44.78 -8.59 +103 -29.47 7.36 -43.31 -14.15 -31.04 8.95 -46.35 -11.46 +104 -31.71 6.23 -47.08 -22.42 -32.55 7.02 -48.84 -21.83 +105 -34.36 6.23 -51.67 -27 -35.22 6.72 -51.35 -24.85 +106 -40.33 5.91 -53.53 -30.24 -38.48 6.43 -53.04 -28.33 +107 -45.01 5.84 -56.12 -33.4 -41.37 6.19 -54.64 -30.54 +108 -44.48 5.71 -58.2 -36.24 -42.91 6.04 -56.07 -32.59 +109 -45.95 5.85 -59.78 -37.21 -45.17 6.4 -58.64 -32.84 +110 -46.36 5.75 -58.82 -35.63 -45.58 6.92 -59.89 -32.75 +111 -47.08 6.09 -57.92 -34.02 -47.87 7.55 -60.36 -30.63 +112 -43.18 6.7 -57.09 -29.37 -44.12 8.36 -60.54 -26.62 +113 -38.93 7.92 -55.23 -23.47 -39.03 9.4 -59.03 -21.34 +114 -33.84 8.71 -52.11 -17.24 -33.75 10.23 -56.93 -15.3 +115 -26.17 9.45 -48.38 -10.53 -29.37 10.8 -52.47 -8.99 +116 -22.5 9.57 -44.07 -4.55 -24.99 11.12 -49.38 -4.85 +117 -14.09 9.32 -38.52 -0.28 -17.04 11.13 -44.01 -0.79 +118 -7.56 9.65 -33.23 5.2 -9.18 11.01 -37.21 5.05 +119 -4 9.15 -25.34 11.96 -4.89 10.4 -30.7 10.75 +120 2.68 8.61 -20.11 13.96 -0.63 9.71 -25.06 12.85 +121 3.13 8.04 -16.66 14.85 0.38 9.35 -23.45 14.03 +122 2.27 7.48 -16.48 14.31 -2.96 9.3 -24.67 12.96 +123 1.2 7.72 -18.3 11.66 -2.86 9.73 -27.07 12.16 +124 -1.18 7.99 -23.61 7.96 -5.42 10.15 -31.36 8.15 +125 -6.65 9.21 -32.76 3.82 -9.84 11.39 -41.73 3.22 +126 -12.72 10.54 -42.73 -1.38 -16.25 13.48 -53.24 0.31 +127 -27.1 12.33 -57.04 -8.26 -26.94 16.01 -68.7 -5.1 +128 -36.13 14.35 -73.57 -15.92 -37.21 18.44 -84.72 -13.24 +129 -52.95 15.22 -87.96 -27.15 -54.53 19.9 -101.68 -23.06 +130 -73.72 16.05 -103.29 -40.2 -73.85 20.5 -117.67 -36.29 +131 -82.93 15.15 -117.04 -58.22 -90.07 19.13 -129.13 -54.68 +132 -102.14 13.62 -126.92 -75.11 -105.22 16.88 -134.27 -70.2 +133 -114.38 11.96 -132.62 -86.55 -115.02 14.67 -139.11 -83.56 +134 -120.26 9.69 -137.19 -98.98 -119.82 12.29 -142.25 -95.78 +135 -123.76 8.33 -139.42 -106.97 -123.64 10.54 -144.19 -104.37 +136 -123.95 7.89 -140.25 -109.67 -124.5 9.81 -144.28 -107.08 +137 -123.61 7.89 -140.8 -109.74 -123.8 9.73 -143.23 -106.28 +138 -122.72 7.99 -140.44 -108.75 -124.64 9.91 -142.56 -104.96 +139 -120.94 8.16 -138.39 -106.21 -122.69 10.13 -140.76 -102.04 +140 -118.88 8.2 -135.75 -103.44 -120.22 10.39 -139.53 -99.63 +141 -113.45 8.21 -131.84 -100.51 -114.5 10.42 -136.21 -96.44 +142 -110.78 8.29 -128.79 -96.54 -110.49 10.55 -133.45 -93.15 +143 -106.75 8.15 -124.16 -93.07 -107.59 10.45 -128.06 -88.82 +144 -102.4 8.25 -122.01 -89.94 -103.3 10.48 -123.89 -85.28 +145 -100.34 8.37 -119.96 -87.28 -98.2 10.5 -120.57 -81.37 +146 -100.71 8.59 -118.42 -84.49 -94.8 10.41 -116.9 -77.74 +147 -99.52 8.79 -117.05 -83.15 -93.03 10.43 -114.75 -75.88 +148 -99.18 9.39 -116.17 -80.07 -92.8 11.32 -112.58 -71.73 +149 -96.94 9.97 -115.26 -76.2 -91.47 12.4 -111.92 -66.16 +150 -90.79 10.67 -113.37 -71.82 -83.71 13.48 -111.56 -61.09 +151 -86.92 11.06 -111.4 -68.1 -81.41 14.21 -111.89 -57.3 +152 -85.9 11.34 -110.22 -66.06 -80.76 14.82 -111.28 -55.56 +153 -85.22 11.65 -110.45 -64.99 -81.58 15.31 -112.57 -54.96 +154 -86.91 11.83 -111.48 -65.21 -83.01 15.55 -114.78 -55.48 +155 -89.71 11.76 -112.73 -66.29 -85.36 15.51 -115.89 -57.6 +156 -92.51 11.48 -113.12 -67.92 -88.64 15.35 -115.36 -58.52 +157 -92.67 11.16 -113.7 -69.1 -87.48 14.99 -115.47 -58.14 +158 -88.91 10.91 -113.41 -69.99 -85.64 14.56 -114.52 -57.37 +159 -87.94 10.66 -111.3 -69.84 -86.43 14.05 -112.27 -57.92 +160 -86.95 10.45 -107.26 -68.12 -85.83 13.94 -110.13 -57.24 +161 -85.29 10.52 -105.8 -65.74 -81.01 14.14 -108.09 -54.07 +162 -78.74 10.6 -102.72 -61.83 -75.82 14.17 -104.53 -49.52 +163 -77.08 10.76 -99.81 -58.32 -72.41 14.06 -101.04 -46.87 +164 -74.42 10.78 -95.44 -53.52 -71.32 13.88 -97.6 -45.39 +165 -71.66 10.86 -93.59 -49.49 -68.5 13.58 -93.71 -42.36 +166 -65.29 10.33 -89.4 -48.42 -64.42 13.09 -90.94 -41.53 +167 -61.69 9.94 -85.89 -46.43 -60.97 12.81 -87.29 -39.12 +168 -61.52 9.69 -81.74 -43.66 -60.17 12.67 -84.25 -36.2 +169 -58.16 9.47 -79.33 -42.17 -58.35 12.29 -81.7 -35.58 +170 -55.34 9.17 -78.42 -41.44 -55.7 12.05 -80.82 -34.96 +171 -57.68 8.82 -77.29 -42.28 -56.66 11.73 -80.34 -35.67 +172 -64.45 8.71 -77.05 -43.49 -62.17 11.36 -78.98 -36.67 +173 -63.15 8.51 -78.23 -45.47 -59.51 11.03 -78.52 -36.88 +174 -62.14 8.69 -80.7 -46.37 -57.15 11.12 -78.87 -37 +175 -61.42 8.85 -80.24 -45.66 -56.68 11.45 -79.48 -35.8 +176 -63.68 9.14 -82.02 -45.79 -56.38 11.68 -80.39 -36.03 +177 -62.61 9.6 -83.68 -46.73 -57.08 11.92 -81.64 -36.64 +178 -66.03 9.64 -85.56 -48.19 -60.21 11.96 -83.15 -38.56 +179 -68.55 9.63 -86.44 -49.27 -61.56 11.86 -84.55 -40.33 +180 -68.12 9.68 -88.3 -50.86 -62.88 11.79 -85.81 -42.16 +181 -68.54 9.97 -89.62 -51.12 -63.81 12.12 -87.17 -41.85 +182 -68.59 9.78 -89.92 -51.76 -61.87 12.28 -88.1 -40.23 +183 -68.86 9.53 -90.08 -52.43 -61.47 12.18 -88.54 -40.34 +184 -70.31 9.51 -89.26 -52.66 -63.82 12.05 -88.8 -41.42 +185 -71.5 9.33 -87.58 -52.5 -66.14 11.78 -87.06 -42.63 +186 -67.51 8.85 -84.5 -51.87 -63.1 11.35 -86.29 -41.78 +187 -64.13 8.55 -82.3 -50.01 -58.35 11.01 -84.66 -39.31 +188 -58.98 8.27 -77.78 -46.09 -54.36 10.12 -79.21 -38.24 +189 -55.74 8.01 -72.75 -42.15 -49.67 9.03 -72.46 -35.38 +190 -50.97 8.16 -67.22 -35.52 -47.83 8.39 -66.65 -32.68 +191 -44.91 8.28 -63.42 -30.9 -43.67 8.09 -61.02 -28.97 +192 -37.3 7.61 -57.51 -27.61 -38.93 8.06 -57.07 -26.77 +193 -32.83 7.39 -52.64 -22.84 -34.32 8.22 -54.47 -22.57 +194 -32.43 7.44 -47.73 -18.33 -31.73 8.37 -51.01 -17.98 +195 -26.14 8.12 -43.23 -11.59 -27.93 8.44 -46.1 -11.87 +196 -21.7 8.7 -40.12 -5.12 -22.54 9.06 -41.46 -4.62 +197 -13.48 8.23 -33.94 -1.05 -16.51 9.31 -36.75 1.28 +198 -10.09 7.57 -29.94 -1.05 -10.22 9.21 -33.71 3.2 +199 -7.72 7.42 -30.57 -1.53 -8.16 9.04 -32.73 3.63 +200 -11.24 7.13 -31.15 -2.01 -10.01 8.93 -32.19 3.34 +201 -19.54 7.77 -34.84 -3.75 -14.6 9.21 -35.18 1.86 +202 -21.5 7.83 -37.32 -6.2 -19.64 9.19 -37.44 -0.26 +203 -26.54 8.11 -38.42 -6.62 -23.23 9.66 -38.41 0.28 +204 -23.73 8.69 -36.98 -3.55 -21.01 10.82 -38.56 3.78 +205 -14.39 9.93 -37.8 1.82 -10.72 11.8 -38.25 6.84 +206 -9.84 10.21 -36.75 4.99 -5.44 12.5 -36.92 11.27 +207 -14.1 10.51 -33.07 6.94 -8.14 13.15 -37.01 13.6 +208 -10.13 11.06 -33.91 8.75 -8.33 13.92 -37.17 15.02 +209 -10.6 11.02 -36.1 8.31 -9.22 13.92 -36.96 16.32 +210 -11.47 10.71 -32.76 9.33 -7.95 14.09 -36.16 17.17 +211 -9.45 10.92 -32.57 10.22 -4.98 14.07 -36.25 18.64 +212 -7.38 10.99 -32.48 11.61 -3.69 13.86 -35.44 19.02 +213 -6.78 10.76 -31.36 10.74 -4.4 13.52 -34.5 18.84 +214 -7.05 10.24 -31.51 9.5 -5.98 13.15 -35.33 16.19 +215 -9.18 10.12 -34.08 5.23 -6.5 13.16 -38.46 12.26 +216 -13.1 10 -38.83 0.74 -10.74 13.28 -43.41 7.3 +217 -23.51 10.25 -45.06 -3.7 -21.54 13.72 -51.94 1.35 +218 -29.26 10.72 -53.69 -11.56 -29.58 14.19 -61.91 -7.44 +219 -36.81 11.23 -64.27 -20.7 -39.16 14.14 -69.02 -16.17 +220 -49.33 11.33 -71.72 -28.9 -49.6 14.5 -80.09 -24.51 +221 -61.75 11.26 -80.17 -37.15 -57.84 14.11 -85.81 -32.27 +222 -67.03 9.92 -84.7 -45.94 -67.09 12.99 -89.46 -38.6 +223 -73.68 9.27 -88.74 -51.96 -69.51 12.08 -90.25 -44.46 +224 -72.15 8.69 -89.32 -55.76 -68.74 11.13 -89.71 -47.8 +225 -74.35 8.04 -89.03 -57.44 -69.26 10.04 -88.17 -50.63 +226 -70.68 7.46 -86.16 -56.76 -66.5 8.93 -85.06 -51.88 +227 -66.88 7.29 -84.73 -56.14 -65.16 8.43 -83.75 -51.78 +228 -65.45 6.79 -80.39 -53.85 -65.57 8.34 -81.38 -49.85 +229 -65.06 6.97 -76.6 -48.78 -62.94 8.9 -78.86 -44.16 +230 -59.11 8.12 -73.94 -43 -57.47 10.2 -75.76 -36.81 +231 -51.45 9.5 -71.41 -34.05 -49.89 11.19 -71.31 -27.67 +232 -41.76 9.78 -64.11 -25.49 -39.44 11.15 -65.33 -23.22 +233 -31.59 9.65 -55.79 -18.92 -31.3 10.86 -58.33 -16.43 +234 -26.47 9.23 -48.17 -12.27 -27.81 10.54 -51.57 -11.15 +235 -21.38 9.05 -41.7 -6.3 -22.9 10.31 -46.25 -6.4 +236 -15.03 8.65 -38.02 -3.4 -15.91 9.65 -43.15 -4.8 +237 -9.44 7.89 -36.19 -4.69 -12.93 9.15 -41.15 -4.99 +238 -14.82 7.64 -36.23 -6.94 -16.93 9.56 -43.28 -5.43 +239 -21.09 8.31 -42.1 -9.7 -21.86 10.97 -50.39 -8.26 +240 -26.31 9.54 -51.68 -14.84 -26.82 13.23 -61.67 -11.52 +241 -35.14 11.06 -62.85 -19.38 -35.26 14.85 -70.07 -14.17 +242 -44.9 11.35 -70.17 -25.03 -45.72 15.45 -78.36 -19.18 +243 -53.72 13.94 -78.84 -22.33 -51.2 20.06 -85.62 -3.09 +244 -58.16 14.8 -86.18 -28.82 -56.73 20.96 -91.1 -8.29 +245 -54.47 15.51 -94.02 -31.75 -47.88 22.01 -99.39 -4.83 +246 -73.1 15.29 -99.84 -33.85 -71.87 19.58 -102 -27.57 +247 -80.67 15.9 -103.93 -44.67 -80.64 19.05 -106.73 -31.42 +248 -86.71 11.84 -108.53 -63.57 -84.39 14.97 -112.95 -53.49 +249 -88.3 11.47 -113.32 -67.55 -87.15 15.13 -116.72 -57.81 +250 -91.78 11.89 -116.99 -70.79 -90.08 16.02 -121.78 -58.85 +251 -94.31 12.64 -121.22 -71.6 -90.51 17.42 -127.21 -57.26 +252 -94.5 13.98 -124.4 -69.21 -90.18 19.46 -132.35 -53.3 +253 -95.65 15.37 -125.79 -66.56 -90.12 21.7 -132.18 -45.17 +254 -91.97 15.42 -125.08 -65 -87.6 21.92 -131.21 -43.51 +255 -86.9 15 -122.85 -62.25 -80.55 21.42 -129.54 -37.55 +256 -90.3 14.86 -120.89 -58.5 -85.9 19.95 -127.97 -47.41 +257 -93.53 13.45 -115.4 -63.22 -90.55 16.89 -122.98 -51.19 +258 -93.71 9.74 -110.67 -72.6 -92.35 12.38 -113.79 -65.83 +259 -89.89 8.38 -105.76 -73.2 -86.91 10.68 -106.28 -66.68 +260 -81.06 8.34 -102.07 -69.33 -76.07 10.53 -102.56 -61.99 +261 -78.3 8.1 -97.55 -64.74 -72.52 10.09 -96.21 -56.91 +262 -77.68 7.99 -95.22 -63.24 -72.1 10.12 -93.77 -54.43 +263 -77.5 7.75 -95.57 -64.72 -73.52 10.36 -93.86 -54.12 +264 -85.38 7.98 -96.3 -65.2 -80.85 10.72 -95.73 -53.74 +265 -84.54 8.57 -97.51 -63.91 -81.29 11.55 -95.76 -50.78 +266 -82.09 8.24 -100.42 -66.92 -76.74 11.11 -97.23 -56.07 +267 -82.77 8.23 -102.76 -69.7 -75.43 10.87 -99.1 -57.85 +268 -93.08 8.29 -105.22 -71.69 -88.1 9.91 -100.6 -62.83 +269 -97.2 8.13 -105.72 -74.37 -92.09 9.52 -102.3 -65.07 +270 -98.21 7.59 -106.96 -76.66 -93.27 9.7 -102.15 -64.23 +271 -89.95 8.74 -106.36 -72.7 -84.89 10.91 -101.86 -59.96 +272 -80.22 8.89 -103.98 -68.03 -77.94 11.06 -98.46 -56.41 +273 -76.12 9.05 -95.65 -60.21 -74.2 11.8 -92.3 -45.91 +274 -74.38 10.08 -90.81 -51.7 -70.83 13.74 -89.75 -36.9 +275 -67.56 10.91 -87.92 -45.78 -61.01 15.14 -86.5 -29.97 +276 -61.85 11.39 -85.09 -39.84 -54.32 15.7 -83.81 -23.75 +277 -56.75 11.7 -81.33 -35.27 -50.86 15.6 -79.97 -18.51 +278 -53.52 11.69 -76.99 -30.72 -48.23 15.21 -76.1 -15.97 +279 -50.13 11.32 -72.06 -28.17 -45.62 14.4 -72.86 -15.4 +280 -46.06 10.87 -68.1 -25.86 -42.44 13.22 -68.88 -16.69 +281 -39.89 10.49 -64.17 -24.11 -39.03 12.29 -65.78 -16.23 +282 -37.94 10.12 -60.7 -20.73 -38.63 11.58 -63.71 -17.49 +283 -36.69 9.61 -57.69 -19.92 -39.26 10.93 -62.69 -18.19 +284 -35.06 9.13 -57.35 -20.51 -39.35 10.18 -63.18 -21.63 +285 -35.22 8.91 -57.53 -22.68 -40.03 9.58 -63.33 -24.92 +286 -36.57 8.56 -57.52 -23.9 -43 9.18 -63.96 -27.26 +287 -37.85 8.06 -58.54 -27.87 -45.5 8.77 -64.89 -31.17 +288 -42.69 7.5 -61.7 -32.08 -48.56 8.25 -67.49 -35.4 +289 -51.85 6.73 -65.11 -38.34 -54.86 7.17 -69.01 -40.68 +290 -56.69 5.79 -67.24 -43.98 -58.27 6.37 -69.15 -44.34 +291 -58.53 5.45 -68.42 -45.73 -58.29 6.64 -69.35 -42.89 +292 -56.55 6.25 -69.2 -43.86 -53.8 7.61 -69.33 -39.12 +293 -54.88 7.04 -69.64 -41.51 -50.77 8.51 -68.73 -35.49 +294 -53.23 7.88 -69.59 -38.94 -49.26 10.07 -68.33 -29.28 +295 -52.4 8.67 -69.62 -36.37 -48.22 11.43 -70.16 -25.2 +296 -48.95 9.48 -69.27 -31.55 -43.51 12.71 -71.22 -22.87 +297 -48.35 10.18 -68.92 -29.19 -44.77 13.56 -72.96 -19.97 +298 -47.6 10.68 -68.21 -26.13 -44.88 14.31 -72.45 -17.57 +299 -45.33 10.85 -67.89 -23.81 -42.38 14.66 -70.7 -15.6 +300 -42.88 10.97 -65.95 -22.3 -40.16 14.79 -68.55 -13.22 +301 -39.59 11.07 -62.9 -19.17 -38.64 14.99 -65.8 -8.2 +302 -38.01 11.11 -61.01 -16.17 -36.2 15 -64.75 -5.66 +303 -33.06 11.36 -59.05 -13.66 -33 15.53 -64.41 -4.3 +304 -35.35 11.39 -57.74 -13.53 -36.19 15.79 -64.09 -2.71 +305 -39.42 11.35 -56.45 -11.93 -38.96 16.14 -63.8 -1.27 +306 -35.78 11.62 -56.32 -10.9 -35.29 16.31 -63.06 0.38 +307 -30.55 11.97 -56.58 -9.17 -31.22 16.45 -62.2 3.47 +308 -31.07 11.69 -53.72 -6.86 -31.05 16.25 -58.96 5.36 +309 -27.9 11.35 -49.42 -4.7 -27.33 15.97 -54.88 7.8 +310 -24.1 11.5 -47.32 -2.16 -22.67 15.82 -52.42 10.75 +311 -22.22 11.34 -44.13 -0.47 -20.87 15.62 -48.7 13.8 +312 -21.88 11.5 -41.69 2.81 -19.67 15.83 -46.37 15.84 +313 -18.42 11.79 -39.75 5.31 -16.66 16.13 -44.64 18.48 +314 -12.39 11.84 -37.87 8.08 -9.95 16.16 -43.45 20.33 +315 -12.12 11.74 -36.81 9.2 -7.03 15.91 -41.04 21.39 +316 -9.85 11.38 -34.64 11.03 -7.3 15.51 -38.73 22.64 +317 -10.45 11.21 -33.84 10.57 -6.47 15.27 -38.2 20.8 +318 -10.88 10.65 -33.02 9.32 -7.02 14.76 -39.23 19.62 +319 -11.31 10.57 -32.95 8.94 -8.25 14.65 -39.2 17.7 +320 -8.44 10.89 -32.93 9.94 -6.17 15.06 -39.38 19.7 +321 -6.92 11.43 -32.43 12.01 -5.29 15.78 -39.44 21.68 +322 -2.51 11.42 -31.39 14.09 -2.38 15.81 -39.6 22.18 +323 -0.71 11.56 -30.6 14.09 -0.59 15.91 -39.57 22.22 +324 -2.6 11.63 -31.24 15.55 -4.09 15.51 -40.71 18.78 +325 -7.77 11.33 -31.62 13.45 -11.39 14.64 -40.16 17.48 +326 -7.75 10.74 -32.07 12.16 -11.59 14.28 -40.27 16.13 +327 -3.06 10.95 -31.6 12.43 -5.54 15.04 -38.72 18.4 +328 -0.61 11.34 -31.93 13.74 -1.95 15.97 -40.51 22.29 +329 -1.75 12.28 -36.17 13.26 -1.78 17.14 -46.5 20.83 +330 -6.91 14.31 -43.22 11.35 -9.32 18.55 -55.34 17.37 +331 -16.42 14.71 -50.72 7.66 -25.22 18.8 -61.73 12.48 +332 -34.02 15.01 -59.96 -2.48 -37.73 18.5 -69.61 1.97 +333 -36.93 14.93 -70.56 -12.17 -41.34 18.79 -78.2 -4.6 +334 -48.06 14.72 -81.97 -22.33 -44.91 19.88 -87.17 -8.58 +335 -58.42 15.56 -91.03 -29.64 -54.11 20.9 -97.73 -14.71 +336 -74.42 15.43 -99.04 -37.25 -72.67 19.53 -105.3 -26.54 +337 -85.7 13.57 -105.56 -50.24 -86.27 16.71 -110.62 -46.11 +338 -91.22 10.66 -111.45 -70.11 -88.48 13.35 -113.9 -63.25 +339 -93.28 9.94 -115.29 -76.44 -89.68 12.52 -116.17 -68.84 +340 -96.85 9.92 -118.92 -80.68 -91.34 12.4 -119.5 -70.68 +341 -103.88 10.14 -122.21 -82.82 -99.06 12.01 -120.61 -75.12 +342 -108.11 10.24 -124.95 -84.19 -102.83 11.89 -121.45 -76.22 +343 -108.58 9.81 -126.14 -87.27 -99.93 11.34 -121.8 -80 +344 -108.8 9.8 -126.25 -88.11 -99.95 11.26 -121.92 -79.42 +345 -109.04 9.73 -126.09 -88.15 -99.99 11.02 -120.2 -79.13 +346 -105.84 9.4 -124.52 -87.98 -97.26 10.46 -118.82 -79.68 +347 -103.3 8.87 -122.26 -87.03 -95.41 9.65 -116.51 -80.37 +348 -102.66 7.97 -119.25 -86.87 -95.79 8.51 -113.63 -81.78 +349 -101.03 7.71 -116.23 -85.74 -95.19 8.33 -110.48 -79.19 +350 -97.04 8.01 -113.59 -81.57 -93.87 9.05 -107.9 -73.29 +351 -89.97 7.9 -109.5 -78.18 -87.03 9.39 -104.83 -69.31 +352 -85.13 6.99 -103.99 -76.67 -83.3 8.86 -101.82 -66.44 +353 -84.01 6.34 -100.74 -74.97 -83.38 7.97 -98.82 -66.93 +354 -86.49 6.59 -101.15 -74.89 -86.1 7.73 -99.83 -68.96 +355 -91.12 6.18 -102.67 -77.23 -90.32 6.86 -101.59 -72.8 +356 -94.94 5.36 -104.3 -82.64 -92.82 5.9 -103.79 -80.73 +357 -92.98 5.05 -105.54 -85.04 -92.4 5.44 -104.48 -83.2 +358 -91.88 5.71 -105.08 -82.6 -92.14 6.1 -105.27 -81.54 +359 -88.63 6.18 -102.37 -77.46 -89.06 7.2 -103.98 -75.29 +360 -84.77 7.21 -99.94 -70.5 -85.53 8.75 -102.06 -66.8 +361 -78.88 8.03 -94.43 -62.55 -78.35 9.88 -95.82 -56.31 +362 -71.15 7.99 -89.74 -57.9 -69.37 9.75 -89.75 -50.33 +363 -66.07 7.2 -85.09 -56.4 -63.03 9.4 -84.54 -46.44 +364 -66.4 8.01 -81.76 -50.81 -63.89 11.56 -83.55 -37.49 +365 -65.1 9.52 -79.82 -43.21 -64.07 13.55 -83.85 -30.26 +366 -57.26 10.39 -78.6 -38.53 -54.84 14.55 -80.2 -25.58 +367 -50.45 10.52 -75.97 -35.61 -48.23 14.69 -78.17 -20.58 +368 -49.22 10.33 -72.75 -32.44 -45.47 13.84 -74.41 -19.19 +369 -48 9.97 -69.29 -30.39 -44.13 12.9 -70.09 -17.66 +370 -49.3 9.38 -67.21 -30.36 -44.26 11.59 -67.52 -21.63 +371 -49.22 8.48 -65.93 -32.84 -45.09 10.02 -65.48 -25.36 +372 -46.86 7.41 -63.79 -35.17 -45.15 8.41 -63.78 -31.1 +373 -48.47 6.48 -62.66 -37.38 -47.15 7.63 -63.73 -34.77 +374 -47.49 5.99 -61.34 -38.47 -48.95 7.26 -64 -37.24 +375 -50.2 5.87 -62.1 -39.49 -50.8 7.15 -65.41 -37.71 +376 -48.25 6.18 -62.7 -38.45 -51.26 7.77 -66.76 -36.86 +377 -50.22 7.72 -62.63 -31.42 -52.15 10.45 -66.32 -25.72 +378 -49.56 10.45 -61 -20.34 -51.2 14.67 -64.82 -7.79 +379 -37.79 12.3 -60.15 -11.74 -37.02 17.08 -63.02 1.64 +380 -26.52 12.52 -58.06 -9.37 -23.08 17.27 -59.39 7.96 +381 -24 12.06 -52.83 -5.55 -19.11 16.4 -54.93 11.46 +382 -28.9 12.2 -49.81 -3.72 -22.54 15.73 -50.98 8.75 +383 -27.84 11.58 -47.87 -4.59 -21.71 14.8 -49.84 7.23 +384 -27.28 10.54 -47.83 -7.63 -21.71 13.54 -49.19 2.43 +385 -25.07 10.36 -45.95 -6.55 -20.54 13.03 -47.5 2.14 +386 -22.2 9.68 -44.24 -7.77 -18.5 11.99 -45.42 -0.06 +387 -19.07 8.52 -41.12 -8.93 -15.47 10.75 -44.66 -1.33 +388 -21.65 7.86 -40.12 -10.57 -18.8 10.09 -43.89 -4.09 +389 -26.23 7.65 -40.02 -11.02 -27.18 9.71 -43.71 -5.36 +390 -28.4 7.96 -41.1 -9.4 -28.92 10.03 -44.27 -4.15 +391 -23.4 8.89 -39.74 -5.45 -23.33 11.17 -43.51 1.64 +392 -17.91 10.18 -38.09 2.42 -18.28 12.84 -40.71 9.46 +393 -9.74 10.75 -33.46 8.84 -10.19 14 -37.11 17.04 +394 -6.53 11.1 -29.88 11.66 -4.18 14.76 -34.25 21.37 +395 -4.22 10.89 -26.82 15.57 -3.31 15 -32.94 25.47 +396 -3.16 11.27 -25.75 18.29 -3.48 15.62 -32.73 27.79 +397 -1.02 12.18 -24.75 24 -1.81 16.86 -32.44 34.04 +398 3.18 13.07 -23.35 28 4.9 18.16 -30.71 39.73 +399 10.24 13.31 -22.27 32.48 12.47 18.54 -27.6 44.73 +400 14.59 13.4 -19.35 34.17 18.54 18.36 -24.64 45.33 +401 14.05 13.42 -16 35.96 17.75 17.77 -21.78 46.84 +402 13.01 12.88 -13.46 36.1 16.3 16.79 -19.33 47.05 +403 14.56 12.01 -12.24 34.72 17.86 15.81 -17.01 44.67 +404 15.96 11.51 -12.5 33 19.02 15.41 -17.28 43.52 +405 12.86 11.57 -13.54 33.47 16.07 15.29 -18.94 41.83 +406 11.17 11.44 -14.26 30.78 13.25 15.09 -19.87 39.11 +407 10.25 10.94 -16.91 27.24 12.61 14.8 -22.55 35.28 +408 7.79 10.99 -19.62 24.12 10.54 14.92 -27.51 30.69 +409 5.44 11.27 -24.25 21.21 5.67 15.32 -33.06 28.51 +410 -3.39 11.33 -27.75 16.63 -4.34 15.03 -38.72 20.63 +411 -7.72 11.08 -33.95 10.75 -11.41 14.82 -42.48 15.08 +412 -11.95 10.7 -38.67 3.83 -13.32 14.32 -46.11 8.41 +413 -12.45 10.63 -42.22 0.57 -14.63 14.15 -50.6 4.93 +414 -17.47 10.52 -45.35 -2.75 -19.34 13.78 -52.65 1.42 +415 -25.15 10.43 -48.72 -7.67 -27.13 13.3 -57.04 -5.39 +416 -31.74 11.41 -56.93 -13.09 -34.82 15.08 -69.57 -9.06 +417 -35.59 12.48 -66.53 -19.72 -36.4 17.32 -76.83 -11.5 +418 -46.88 13.08 -73.37 -24.06 -50.54 18.88 -85.12 -12.2 +419 -51.43 13.42 -80.07 -26.95 -53.85 19.07 -91.09 -14.9 +420 -58.11 13.93 -84.58 -27.29 -61.18 18.69 -98.22 -21.36 +421 -63.52 13.71 -89.57 -36.64 -68.48 17.65 -103.69 -33.45 +422 -66.31 12.29 -94.51 -47.72 -75.22 16.24 -109.29 -44.9 +423 -77.44 12.55 -103.53 -54.59 -81.07 15.95 -115.92 -54.15 +424 -78.65 13.81 -117.38 -63.16 -85.93 18.13 -131.11 -62.04 +425 -95.55 14.58 -127.97 -71.58 -97.74 19.75 -142.89 -70.69 +426 -109.94 14.7 -135.51 -78.84 -114.91 19.96 -153.15 -76.59 +427 -116.49 13.76 -143.17 -89.19 -122.93 18.99 -158.6 -86.09 +428 -118.26 13.87 -148.56 -94.4 -123.82 18.21 -161.3 -90.69 +429 -120.44 13.74 -150.2 -96.76 -124.4 17.39 -157.19 -93.27 +430 -119.42 12.81 -149.38 -97.73 -119.99 15.79 -152.28 -94.75 +431 NaN NaN NaN NaN -119.41 14.67 -150.37 -95.74 +432 NaN NaN NaN NaN -120.06 14.12 -148.76 -95.47 +433 NaN NaN NaN NaN -121.15 13.75 -145.59 -94.41 +434 NaN NaN NaN NaN -119.52 13.43 -142.99 -93.02 +435 NaN NaN NaN NaN -115.07 13.44 -141.72 -92.12 +436 NaN NaN NaN NaN -113.91 13.59 -140.13 -89.18 +437 NaN NaN NaN NaN -113.26 13.88 -137.59 -86.1 +438 NaN NaN NaN NaN -110.74 14.33 -135.79 -82.22 +439 NaN NaN NaN NaN -105.38 14.5 -132.91 -78.82 +440 NaN NaN NaN NaN -99.91 14.46 -129.35 -76 +441 NaN NaN NaN NaN -98.54 14.31 -127.64 -74.87 +442 NaN NaN NaN NaN -100.55 14.1 -126.65 -74.57 +443 NaN NaN NaN NaN -101.78 13.66 -125.9 -75.24 +444 NaN NaN NaN NaN -99.69 12.91 -124 -75.56 +445 NaN NaN NaN NaN -96.28 11.88 -121.22 -76.64 +446 NaN NaN NaN NaN -95.52 10.55 -117.85 -78.42 +447 NaN NaN NaN NaN -95.02 9.39 -115.18 -80 +448 NaN NaN NaN NaN -96.95 8.66 -114.45 -82.02 +449 NaN NaN NaN NaN -98.28 8.65 -114.6 -82 +450 NaN NaN NaN NaN -97.77 9.22 -117.04 -80.19 +451 NaN NaN NaN NaN -97.82 10.04 -118.6 -77.63 +452 NaN NaN NaN NaN -96.21 10.55 -118.14 -75.34 +453 NaN NaN NaN NaN -94.59 10.62 -115.48 -73.63 +454 NaN NaN NaN NaN -92.94 10.48 -112.57 -72.38 +455 NaN NaN NaN NaN -90.9 10.35 -111.16 -70.77 +456 NaN NaN NaN NaN -89.86 10.2 -109.39 -69.75 +457 NaN NaN NaN NaN -88.33 9.96 -107.48 -68.55 +458 NaN NaN NaN NaN -85.94 9.51 -105.95 -67.9 +459 NaN NaN NaN NaN -81.84 9.04 -102.72 -66.4 +460 NaN NaN NaN NaN -81.43 8.85 -99.88 -65.26 +461 NaN NaN NaN NaN -79.68 8.89 -97.25 -62.77 +462 NaN NaN NaN NaN -78.29 9.26 -95.54 -60.63 +463 NaN NaN NaN NaN -75.46 10.02 -95.82 -58.26 +464 NaN NaN NaN NaN -73.88 12 -99.84 -53.37 +465 NaN NaN NaN NaN -74.83 13.88 -98.79 -47.1 +466 NaN NaN NaN NaN -71.39 15.8 -98.39 -35.63 +467 NaN NaN NaN NaN -62.89 16.6 -94.17 -27.85 +468 NaN NaN NaN NaN -52 17.1 -89.89 -22.12 +469 NaN NaN NaN NaN -51.21 17.12 -86.79 -18.9 +470 NaN NaN NaN NaN -51.75 17.35 -83.43 -15.39 +471 NaN NaN NaN NaN -49.63 17.46 -80.63 -13.96 +472 NaN NaN NaN NaN -41.58 17.18 -75.91 -11.84 +473 NaN NaN NaN NaN -36.84 16.45 -71.05 -10.09 +474 NaN NaN NaN NaN -34.5 15.35 -66.49 -7.86 +475 NaN NaN NaN NaN -32.09 14.63 -62.85 -6.72 +476 NaN NaN NaN NaN -29.84 13.61 -60.19 -7.39 +477 NaN NaN NaN NaN -27.61 12.41 -56.85 -8.81 +478 NaN NaN NaN NaN -28.34 11.09 -53.28 -10.06 +479 NaN NaN NaN NaN -31.41 9.69 -51.47 -12.73 +480 NaN NaN NaN NaN -32.77 7.94 -49.46 -17.53 +481 NaN NaN NaN NaN -32.41 6.45 -46.88 -21.14 +482 NaN NaN NaN NaN -30.53 5.98 -45.58 -21.15 +483 NaN NaN NaN NaN -28.2 6.29 -43.54 -17.6 +484 NaN NaN NaN NaN -24.91 6.75 -41.12 -14.13 +485 NaN NaN NaN NaN -21.12 7.02 -39.32 -11.37 +486 NaN NaN NaN NaN -17.44 7.68 -37.89 -8.61 +487 NaN NaN NaN NaN -17.62 8.78 -38.84 -5.35 +488 NaN NaN NaN NaN -17.2 10.06 -40.79 -2.45 +489 NaN NaN NaN NaN -18.07 11.25 -42.05 1.07 +490 NaN NaN NaN NaN -15.29 12.28 -43.15 4.63 +491 NaN NaN NaN NaN -12.74 13.15 -42.7 8.03 +492 NaN NaN NaN NaN -10.85 13.84 -43.11 10.47 +493 NaN NaN NaN NaN -12.44 14.77 -45.4 11.61 +494 NaN NaN NaN NaN -14.59 15.6 -48.38 12.74 +495 NaN NaN NaN NaN -17.71 16.32 -50.36 12.84 +496 NaN NaN NaN NaN -18.44 16.74 -51.49 12.41 +497 NaN NaN NaN NaN -15.39 16.94 -51.59 12.71 +498 NaN NaN NaN NaN -14.05 17.08 -52.54 12.79 +499 NaN NaN NaN NaN -17.99 17.16 -54.27 13.1 +500 NaN NaN NaN NaN -22.56 17.17 -55.52 11.07 +501 NaN NaN NaN NaN -23.33 16.97 -56.84 8.47 +502 NaN NaN NaN NaN -24.04 16.91 -59.08 7.26 +503 NaN NaN NaN NaN -27.37 16.94 -61.16 5.31 +504 NaN NaN NaN NaN -30.58 17.04 -64.95 1.93 +505 NaN NaN NaN NaN -33.44 17.12 -68.67 -0.82 +506 NaN NaN NaN NaN -36.11 17.25 -72.35 -5.48 +507 NaN NaN NaN NaN -42.61 17.86 -78.27 -10.12 +508 NaN NaN NaN NaN -47.62 17.94 -81.58 -14.77 +509 NaN NaN NaN NaN -55.32 17.38 -83.66 -19.05 +510 NaN NaN NaN NaN -55.45 16.9 -85.22 -20.86 +511 NaN NaN NaN NaN -53.4 16.62 -84.89 -20.68 +512 NaN NaN NaN NaN -52.81 16.26 -84.36 -21.21 +513 NaN NaN NaN NaN -53.28 15.78 -83.35 -22.35 +514 NaN NaN NaN NaN -51.5 15.18 -81.72 -21.56 +515 NaN NaN NaN NaN -50.33 14.4 -78.1 -21.74 +516 NaN NaN NaN NaN -47.55 13.41 -75.04 -21.92 +517 NaN NaN NaN NaN -44.78 12.68 -72.88 -22.92 +518 NaN NaN NaN NaN -45.35 12.07 -71.99 -22.62 +519 NaN NaN NaN NaN -46.68 11.8 -71.44 -23.66 +520 NaN NaN NaN NaN -45.1 11.66 -71.34 -25 +521 NaN NaN NaN NaN -44.99 11.73 -72.63 -26.42 +522 NaN NaN NaN NaN -46.82 11.83 -74.23 -27.7 +523 NaN NaN NaN NaN -49.7 11.84 -75.33 -28.53 +524 NaN NaN NaN NaN -50.48 11.75 -75.95 -29.38 +525 NaN NaN NaN NaN -49.96 11.64 -76.05 -29.94 +526 NaN NaN NaN NaN -49.68 11.41 -75.52 -30.65 +527 NaN NaN NaN NaN -48.46 11.09 -74.84 -31.41 +528 NaN NaN NaN NaN -48.5 10.75 -75.34 -33.08 +529 NaN NaN NaN NaN -50.66 10.42 -76.18 -34.86 +530 NaN NaN NaN NaN -52.1 10.05 -76.21 -36.67 +531 NaN NaN NaN NaN -53.67 9.63 -75.14 -38.35 +532 NaN NaN NaN NaN -53.71 9.36 -75.64 -40.12 +533 NaN NaN NaN NaN -53.01 9.38 -77.81 -40.28 +534 NaN NaN NaN NaN -56.16 9.55 -79.35 -41.9 +535 NaN NaN NaN NaN -61.14 10.17 -82.56 -43.19 +536 NaN NaN NaN NaN -64.07 10.45 -84.64 -45.4 +537 NaN NaN NaN NaN -67.39 11.01 -86.45 -45.18 +538 NaN NaN NaN NaN -64.18 12.32 -87.22 -41.16 +539 NaN NaN NaN NaN -59.91 13.34 -87.92 -35.08 +540 NaN NaN NaN NaN -58.99 14.07 -85.4 -30.45 +541 NaN NaN NaN NaN -55.16 14.86 -85.45 -27.24 +542 NaN NaN NaN NaN -51.73 15.46 -83.84 -25.01 +543 NaN NaN NaN NaN -53.72 15.58 -83.27 -24.02 +544 NaN NaN NaN NaN -53.89 15.28 -83.09 -24.64 +545 NaN NaN NaN NaN -53.73 14.52 -84.05 -28.41 +546 NaN NaN NaN NaN -55.03 13.33 -83.9 -31.42 +547 NaN NaN NaN NaN -57.35 12.1 -84.49 -36.55 +548 NaN NaN NaN NaN -59.46 11.01 -84.45 -41.25 +549 NaN NaN NaN NaN -62.11 10.18 -83.64 -44.83 +550 NaN NaN NaN NaN -62.75 9.64 -82.71 -45.13 +551 NaN NaN NaN NaN -60.01 9.24 -81.35 -46.41 +552 NaN NaN NaN NaN -58.3 9.21 -80.96 -45.72 +553 NaN NaN NaN NaN -59.76 9.27 -79.75 -44.16 +554 NaN NaN NaN NaN -61.1 9.65 -78.66 -41.3 +555 NaN NaN NaN NaN -58.37 10.29 -76.36 -37.06 +556 NaN NaN NaN NaN -52.38 11.22 -74.15 -31.7 +557 NaN NaN NaN NaN -46.51 11.64 -70.67 -26.21 +558 NaN NaN NaN NaN -40.41 11.62 -66.42 -21.99 +559 NaN NaN NaN NaN -37.88 11.51 -62.85 -18.68 +560 NaN NaN NaN NaN -36.07 11.42 -60.2 -16.62 +561 NaN NaN NaN NaN -35.39 11.34 -58.34 -15.68 +562 NaN NaN NaN NaN -34.82 11.11 -56.09 -14.39 +563 NaN NaN NaN NaN -33.39 10.8 -54.09 -13.27 +564 NaN NaN NaN NaN -30.12 10.65 -50.98 -9.15 +565 NaN NaN NaN NaN -26.73 10.89 -47.13 -5.38 +566 NaN NaN NaN NaN -19.58 10.5 -43.65 -2.77 +567 NaN NaN NaN NaN -13.9 10.21 -39.04 -0.25 +568 NaN NaN NaN NaN -13.27 9.88 -36.33 2.58 +569 NaN NaN NaN NaN -11.65 9.95 -34.8 4.61 +570 NaN NaN NaN NaN -9.06 10.38 -33.45 6.64 +571 NaN NaN NaN NaN -8.18 11.52 -35 9.06 +572 NaN NaN NaN NaN -6.66 12.45 -37.55 10.31 +573 NaN NaN NaN NaN -8.42 13.12 -39.76 11.13 +574 NaN NaN NaN NaN -8.88 13.44 -40.32 10.88 +575 NaN NaN NaN NaN -12.1 12.95 -42.6 8.98 +576 NaN NaN NaN NaN -12.36 12.53 -40.77 8.12 +577 NaN NaN NaN NaN -11.71 12.99 -44.14 6.36 +578 NaN NaN NaN NaN -13.91 13.84 -48.25 5 +579 NaN NaN NaN NaN -23.45 14.43 -54.55 0.93 +580 NaN NaN NaN NaN -32.03 14.48 -61.05 -4.55 +581 NaN NaN NaN NaN -38.35 13.45 -65.38 -14.27 +582 NaN NaN NaN NaN -44.03 12.21 -67.95 -22.38 +583 NaN NaN NaN NaN -50.08 11.77 -70.2 -27.45 +584 NaN NaN NaN NaN -53.33 11.51 -71.59 -29.23 +585 NaN NaN NaN NaN -54.4 11.56 -72.45 -28.93 +586 NaN NaN NaN NaN -49.95 11.65 -72.05 -28.28 +587 NaN NaN NaN NaN -47.15 12 -71.22 -25.37 +588 NaN NaN NaN NaN -45.53 12.39 -67.41 -20.2 +589 NaN NaN NaN NaN -39.1 12.82 -64.07 -16.26 +590 NaN NaN NaN NaN -33.2 13.07 -61.9 -12.85 +591 NaN NaN NaN NaN -30.1 12.8 -58.59 -10 +592 NaN NaN NaN NaN -28.97 12.71 -57.08 -8.64 +593 NaN NaN NaN NaN -30.38 12.59 -55.47 -7.65 +594 NaN NaN NaN NaN -29.81 12.4 -53.81 -7.17 +595 NaN NaN NaN NaN -26.35 12.24 -52.89 -6.36 +596 NaN NaN NaN NaN -23.46 12.31 -52.63 -5.54 +597 NaN NaN NaN NaN -24.04 13.01 -55.45 -5.64 +598 NaN NaN NaN NaN -27.53 14.98 -63.57 -5.36 +599 NaN NaN NaN NaN -33.05 16.15 -70.08 -7.06 +600 NaN NaN NaN NaN -41.35 16.78 -73.57 -8.57 +601 NaN NaN NaN NaN -41.23 16.95 -74.09 -9.11 +602 NaN NaN NaN NaN -39.41 16.81 -73 -9.43 +603 NaN NaN NaN NaN -37.95 16.28 -68.81 -6.89 +604 NaN NaN NaN NaN -33.99 15.84 -64.22 -3.89 +605 NaN NaN NaN NaN -24.09 15.68 -59.74 1.52 +606 NaN NaN NaN NaN -16.31 15.58 -54.45 5.39 +607 NaN NaN NaN NaN -14.29 16.12 -51.81 9.76 +608 NaN NaN NaN NaN -15.19 16.8 -53.77 11.59 +609 NaN NaN NaN NaN -17.77 17.28 -55.56 11.14 +610 NaN NaN NaN NaN -17.66 17.15 -55.27 11.62 +611 NaN NaN NaN NaN -18.6 16.77 -54.79 11.2 +612 NaN NaN NaN NaN -18.19 16.47 -50.73 12.23 +613 NaN NaN NaN NaN -12.57 15.51 -47.95 14.36 +614 NaN NaN NaN NaN -9.02 15.23 -45.9 13.27 +615 NaN NaN NaN NaN -11.31 15.61 -49.26 11.9 +616 NaN NaN NaN NaN -16.78 15.93 -52.51 11.02 +617 NaN NaN NaN NaN -29.38 15.95 -63.77 -1.98 +618 NaN NaN NaN NaN -43.34 16.58 -80.29 -15.26 +619 NaN NaN NaN NaN -65.34 15.37 -91.09 -29.53 +620 NaN NaN NaN NaN -78.36 13.46 -102.94 -50.42 +621 NaN NaN NaN NaN -86.36 12.08 -112.6 -65.02 +622 NaN NaN NaN NaN -93 12.48 -120.27 -72.53 +623 NaN NaN NaN NaN -100.28 13.93 -127.93 -75.59 +624 NaN NaN NaN NaN -103.58 15.27 -135.2 -76.75 +625 NaN NaN NaN NaN -105.54 16.51 -141.8 -78.3 +626 NaN NaN NaN NaN -110.96 17.1 -145.08 -79.51 +627 NaN NaN NaN NaN -114.62 17.5 -145.85 -78.39 +628 NaN NaN NaN NaN -111.96 17.31 -144.64 -79.49 +629 NaN NaN NaN NaN -109.41 17 -142.4 -78.67 +630 NaN NaN NaN NaN -108.5 16.3 -139.23 -77.27 +631 NaN NaN NaN NaN -105.76 15.43 -135.73 -76.97 +632 NaN NaN NaN NaN -103.04 14.5 -132.62 -77.72 +633 NaN NaN NaN NaN -102.54 13.89 -130.58 -78.76 +634 NaN NaN NaN NaN -105.6 13.89 -131.68 -80.24 +635 NaN NaN NaN NaN -109.52 13.46 -131.5 -81.89 +636 NaN NaN NaN NaN -111.27 13.21 -130.66 -82.25 +637 NaN NaN NaN NaN -105.13 13.06 -128.74 -80.26 +638 NaN NaN NaN NaN -101.37 13.05 -126.54 -76.84 +639 NaN NaN NaN NaN -99.09 12.66 -122.27 -74.11 +640 NaN NaN NaN NaN -93.77 12.56 -118.89 -71.38 +641 NaN NaN NaN NaN -92.11 12.31 -117.12 -70.11 +642 NaN NaN NaN NaN -89.12 11.82 -113.29 -69.06 +643 NaN NaN NaN NaN -85.33 11.21 -109.57 -67.71 +644 NaN NaN NaN NaN -82.43 10.92 -107.94 -66.86 +645 NaN NaN NaN NaN -82.73 11.36 -108.46 -66.01 +646 NaN NaN NaN NaN -86.64 11.91 -111.24 -65.26 +647 NaN NaN NaN NaN -88.13 12.12 -110.28 -64.36 +648 NaN NaN NaN NaN -86.44 12 -109.73 -64.12 +649 NaN NaN NaN NaN -80.26 11.49 -107.42 -63.97 +650 NaN NaN NaN NaN -79.87 10.7 -104.18 -64.27 +651 NaN NaN NaN NaN -82.42 10.02 -101.78 -64.56 +652 NaN NaN NaN NaN -85.15 9.95 -102.03 -64.95 +653 NaN NaN NaN NaN -84.29 9.68 -101.24 -65.36 +654 NaN NaN NaN NaN -78.18 9.34 -100.85 -64.37 +655 NaN NaN NaN NaN -77.24 9.44 -101.75 -64.69 +656 NaN NaN NaN NaN -82.73 9.32 -100.19 -65.37 +657 NaN NaN NaN NaN -85.16 9.56 -101.08 -65.49 +658 NaN NaN NaN NaN -83.48 10.22 -101.13 -62.83 +659 NaN NaN NaN NaN -78.4 10.39 -100.7 -61.79 +660 NaN NaN NaN NaN -75.27 10.06 -99.68 -59.23 +661 NaN NaN NaN NaN -77.61 9.81 -98.33 -60.36 +662 NaN NaN NaN NaN -79.27 9.9 -98.78 -60.7 +663 NaN NaN NaN NaN -80.2 9.8 -98.23 -61.26 +664 NaN NaN NaN NaN -79.29 10.52 -98.1 -58.05 +665 NaN NaN NaN NaN -75.05 11 -96.27 -54.91 +666 NaN NaN NaN NaN -72.45 11.2 -94.27 -52.32 +667 NaN NaN NaN NaN -69.86 11.13 -91.93 -49.63 +668 NaN NaN NaN NaN -66.7 10.94 -88.32 -47.26 +669 NaN NaN NaN NaN -62.58 10.29 -84.49 -45.49 +670 NaN NaN NaN NaN -61.52 9.72 -81.92 -44.03 +671 NaN NaN NaN NaN -61.93 9.59 -82.02 -44.05 +672 NaN NaN NaN NaN -65.68 9.57 -81.74 -46.24 +673 NaN NaN NaN NaN -67.22 9.74 -81.36 -44.54 +674 NaN NaN NaN NaN -62.65 10.04 -80.52 -42.32 +675 NaN NaN NaN NaN -56.98 10.72 -78.97 -37.17 +676 NaN NaN NaN NaN -53.47 11.36 -75.68 -31.91 +677 NaN NaN NaN NaN -50.13 11.92 -72.88 -28.58 +678 NaN NaN NaN NaN -45.5 12.69 -71.16 -23.07 +679 NaN NaN NaN NaN -42.18 13.85 -69.92 -17.32 +680 NaN NaN NaN NaN -38.49 14.87 -68.34 -11.34 +681 NaN NaN NaN NaN -34.08 15.65 -65.6 -7.72 +682 NaN NaN NaN NaN -28.89 15.92 -62.78 -3 +683 NaN NaN NaN NaN -24.49 15.71 -58.91 0.28 +684 NaN NaN NaN NaN -21.33 15.09 -54.18 2.3 +685 NaN NaN NaN NaN -17.8 14.34 -48.48 5.69 +686 NaN NaN NaN NaN -12.64 13.03 -43.42 7.27 +687 NaN NaN NaN NaN -9.14 11.55 -38.51 8.08 +688 NaN NaN NaN NaN -11.45 10.67 -35.65 7.06 +689 NaN NaN NaN NaN -13.04 10.51 -35.26 6.05 +690 NaN NaN NaN NaN -15.63 10.79 -38.45 5.46 +691 NaN NaN NaN NaN -15.7 11.37 -39.48 4.6 +692 NaN NaN NaN NaN -13.48 11.83 -40.7 4.74 +693 NaN NaN NaN NaN -12.46 12.18 -40.23 7.16 +694 NaN NaN NaN NaN -12.35 12.52 -40.33 9.32 +695 NaN NaN NaN NaN -10.64 12.74 -40.06 11.94 +696 NaN NaN NaN NaN -9.93 12.54 -39.79 11.44 +697 NaN NaN NaN NaN -11.83 12.35 -39.88 10.96 +698 NaN NaN NaN NaN -15.58 12.29 -41.55 9.53 +699 NaN NaN NaN NaN -19.13 12.33 -43.08 7.21 +700 NaN NaN NaN NaN -21.36 12.4 -45.84 5.95 +701 NaN NaN NaN NaN -23.27 12.41 -48.71 1.3 +702 NaN NaN NaN NaN -26.99 12.34 -51.37 -1.27 +703 NaN NaN NaN NaN -30.02 11.87 -53.21 -6.94 +704 NaN NaN NaN NaN -31.23 11.56 -55.74 -10.96 +705 NaN NaN NaN NaN -31.93 11.63 -60.16 -14.17 +706 NaN NaN NaN NaN -36.72 12.53 -67.85 -18.26 +707 NaN NaN NaN NaN -48.33 12.46 -73.27 -22.56 +708 NaN NaN NaN NaN -57.75 11.99 -77.85 -30.29 +709 NaN NaN NaN NaN -56.95 11.61 -83.5 -39.31 +710 NaN NaN NaN NaN -58.02 12.96 -89.66 -39.49 +711 NaN NaN NaN NaN -64.8 14.19 -95.64 -41.34 +712 NaN NaN NaN NaN -68.05 15.76 -106.24 -43.3 +713 NaN NaN NaN NaN -71.56 16.02 -108.95 -45.37 +714 NaN NaN NaN NaN -80.14 15.47 -109 -49.16 +715 NaN NaN NaN NaN -82.33 14.27 -110.18 -56.13 +716 NaN NaN NaN NaN -83.25 12.71 -108.96 -61.86 +717 NaN NaN NaN NaN -82.13 11.03 -106.72 -65.65 +718 NaN NaN NaN NaN -86.11 10.15 -105.52 -68.33 +719 NaN NaN NaN NaN -90.18 9.81 -105.46 -68.74 +720 NaN NaN NaN NaN -88.93 9.81 -104.74 -67.32 +721 NaN NaN NaN NaN -82.33 10.29 -102.44 -64.18 +722 NaN NaN NaN NaN -76.68 9.87 -99.27 -61.05 +723 NaN NaN NaN NaN -70.14 8.63 -92.39 -58.22 +724 NaN NaN NaN NaN -66.7 8.33 -87.2 -54.27 +725 NaN NaN NaN NaN -65.99 9.44 -83.16 -45.6 +726 NaN NaN NaN NaN -63.32 10.97 -79.63 -36.97 +727 NaN NaN NaN NaN -52.01 11.39 -76.64 -32.54 +728 NaN NaN NaN NaN -44.27 12.38 -73.99 -26.47 +729 NaN NaN NaN NaN -43.98 14.11 -75.58 -20.83 +730 NaN NaN NaN NaN -44.3 15.77 -76.73 -16.93 +731 NaN NaN NaN NaN -43.81 14.96 -76.78 -18.13 +732 NaN NaN NaN NaN -41.74 14.34 -76.91 -19.73 +733 NaN NaN NaN NaN -40.02 14.07 -77.77 -20.78 +734 NaN NaN NaN NaN -42.07 12.88 -76 -23.41 +735 NaN NaN NaN NaN -49.78 10.91 -70.43 -26.15 +736 NaN NaN NaN NaN -50.09 10.21 -69.99 -30.18 +737 NaN NaN NaN NaN -48.25 9.49 -70.58 -33.25 +738 NaN NaN NaN NaN -47.66 8 -68.08 -35.58 +739 NaN NaN NaN NaN -52.7 7.47 -68.42 -38.4 +740 NaN NaN NaN NaN -56.43 7.56 -71.27 -40.99 +741 NaN NaN NaN NaN -58.79 7.9 -75.6 -43.71 +742 NaN NaN NaN NaN -61.79 8.35 -79.93 -47.53 +743 NaN NaN NaN NaN -64.82 8.95 -83.65 -49.84 +744 NaN NaN NaN NaN -67.33 9.53 -87.32 -50.84 +745 NaN NaN NaN NaN -69.88 10.21 -90.82 -52.07 +746 NaN NaN NaN NaN -71.14 10.81 -92.85 -52.48 +747 NaN NaN NaN NaN -73.88 11.09 -94.39 -52.67 +748 NaN NaN NaN NaN -73.75 11.21 -94.79 -52.95 +749 NaN NaN NaN NaN -74.38 11.27 -93.77 -51.82 +750 NaN NaN NaN NaN -74.08 11.12 -92.75 -50.6 +751 NaN NaN NaN NaN -68.3 10.7 -90.54 -50.33 +752 NaN NaN NaN NaN -65.75 10.14 -88.45 -49.68 +753 NaN NaN NaN NaN -65.13 9.74 -85.12 -48.78 +754 NaN NaN NaN NaN -63.7 9.99 -83.39 -45.28 +755 NaN NaN NaN NaN -61.85 9.74 -81.81 -42.6 +756 NaN NaN NaN NaN -59.1 9.15 -80.41 -43.32 +757 NaN NaN NaN NaN -58.67 8.65 -78.2 -43.29 +758 NaN NaN NaN NaN -57.34 8.29 -74.9 -42.26 +759 NaN NaN NaN NaN -53.97 7.98 -72.19 -40.67 +760 NaN NaN NaN NaN -50.83 8.28 -69.9 -38.3 +761 NaN NaN NaN NaN -49.19 9.39 -68.65 -31.34 +762 NaN NaN NaN NaN -47.72 10.18 -66.67 -26.67 +763 NaN NaN NaN NaN -41.61 10.55 -65.02 -22.64 +764 NaN NaN NaN NaN -39.17 10.6 -62.98 -21.61 +765 NaN NaN NaN NaN -39.26 10.2 -60.15 -21.5 +766 NaN NaN NaN NaN -36.3 9.78 -57.3 -18.89 +767 NaN NaN NaN NaN -32.18 9.25 -53.96 -17.48 +768 NaN NaN NaN NaN -27.3 8.83 -49.14 -14.2 +769 NaN NaN NaN NaN -23.7 8.72 -44.96 -10.41 +770 NaN NaN NaN NaN -20.71 8.85 -40.62 -5.68 +771 NaN NaN NaN NaN -16.21 9.36 -36.7 0.45 +772 NaN NaN NaN NaN -12.24 9.65 -33.34 4.81 +773 NaN NaN NaN NaN -8 9.32 -29.97 5.74 +774 NaN NaN NaN NaN -6.3 9.02 -28.85 5.77 +775 NaN NaN NaN NaN -8.56 9.14 -29.26 5.33 +776 NaN NaN NaN NaN -9.1 9.35 -30.76 5.17 +777 NaN NaN NaN NaN -12.76 9.52 -33.06 4.17 +778 NaN NaN NaN NaN -13.68 9.66 -34.57 3.18 +779 NaN NaN NaN NaN -14.49 9.66 -36.23 1.96 +780 NaN NaN NaN NaN -13.01 10.2 -38.78 1.15 +781 NaN NaN NaN NaN -15.3 10.96 -42.45 0.43 +782 NaN NaN NaN NaN -20.86 11.62 -46.82 0.01 +783 NaN NaN NaN NaN -23.36 11.87 -48.82 -2.96 +784 NaN NaN NaN NaN -22.74 11.74 -50.84 -5.59 +785 NaN NaN NaN NaN -26.25 11.63 -54.36 -10.44 +786 NaN NaN NaN NaN -31.59 11.87 -61.27 -15.69 +787 NaN NaN NaN NaN -41.56 12.46 -72.15 -23.61 +788 NaN NaN NaN NaN -54.67 13.48 -87.77 -33.61 +789 NaN NaN NaN NaN -67.49 13.35 -96.99 -44.85 +790 NaN NaN NaN NaN -79.84 12.08 -102.8 -56.52 +791 NaN NaN NaN NaN -86.47 10.82 -106.83 -64.92 +792 NaN NaN NaN NaN -87.48 9.95 -107.39 -69.51 +793 NaN NaN NaN NaN -86.61 10.48 -112.01 -71.38 +794 NaN NaN NaN NaN -88.16 11.22 -116.26 -72.14 +795 NaN NaN NaN NaN -93.19 11.18 -118.5 -73.3 +796 NaN NaN NaN NaN -95.67 11.11 -119.16 -74.88 +797 NaN NaN NaN NaN -93.05 10.78 -117.79 -74.96 +798 NaN NaN NaN NaN -92.37 10.08 -114.68 -75.73 diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R new file mode 100644 index 00000000..02b21b36 --- /dev/null +++ b/tests/testthat/test_get_mis_time_steps.R @@ -0,0 +1,12 @@ +test_that("get_mis_time_steps for pastclimData", { + skip_if(requireNamespace("pastclimData")) + expect_equal(get_mis_time_steps(mis="2",dataset="Example"), + c(-20000,-15000), ignore_attr = TRUE) +}) + +test_that("get_mis_time_steps for local file", { + path_to_example_nc <- system.file("/extdata/", "example_climate.nc", + package = "pastclim") + expect_equal(get_mis_time_steps(mis="2",path_to_nc = path_to_example_nc), + c(-20000,-15000), ignore_attr = TRUE) +}) From a94ffe3d315a69dbf5988463b7bb10a32baaad86 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 15:41:24 +0100 Subject: [PATCH 02/83] Full testing of get_mis_time_steps --- DESCRIPTION | 2 +- NAMESPACE | 1 + R/get_mis_time_steps.R | 7 ++++- R/get_time_steps.R | 40 ++++++++++++++++++++++++ man/Beyer2020.Rd | 10 +++--- man/Krapp2021.Rd | 14 ++++----- man/check_var_downloaded.Rd | 6 ++-- man/check_var_in_nc.Rd | 3 +- man/climate_for_locations.Rd | 19 ++++++----- man/climate_for_time_slice.Rd | 5 +-- man/download_dataset.Rd | 10 +++--- man/get_biome_classes.Rd | 7 +++-- man/get_ice_mask.Rd | 9 +++--- man/get_land_mask.Rd | 9 +++--- man/get_mis_time_steps.Rd | 7 +++-- man/get_time_steps.Rd | 20 ++++++++++++ man/get_vars_for_dataset.Rd | 8 ++--- man/region_outline_union.Rd | 3 +- man/time_series_for_locations.Rd | 5 +-- tests/testthat/test_get_mis_time_steps.R | 10 +++++- tests/testthat/test_get_time_steps.R | 3 ++ 21 files changed, 144 insertions(+), 54 deletions(-) create mode 100644 R/get_time_steps.R create mode 100644 man/get_time_steps.Rd create mode 100644 tests/testthat/test_get_time_steps.R diff --git a/DESCRIPTION b/DESCRIPTION index b41a0d5e..b6bdc22a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,7 +24,7 @@ BugReports: https://github.com/EvolEcolGroup/pastclim/issues Encoding: UTF-8 LazyData: true LazyDataCompression: xz -RoxygenNote: 7.1.2 +RoxygenNote: 7.2.1 Depends: R (>= 3.5.0) Imports: diff --git a/NAMESPACE b/NAMESPACE index a15f5969..9a638e99 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,7 @@ export(get_ice_mask) export(get_land_mask) export(get_mis_time_steps) export(get_pastclimdata_path) +export(get_time_steps) export(get_vars_for_dataset) export(time_series_for_locations) import(terra) diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index 4f6cf286..d50291f4 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -12,10 +12,15 @@ #' #' @export -get_mis_time_steps <- function(mis, dataset, path_to_nc = NULL) { +get_mis_time_steps <- function(mis, dataset = NULL, path_to_nc = NULL) { if (!mis %in% mis_boundaries$mis) { stop("'mis' should be one of ", paste(mis_boundaries$mis, collapse = ",")) } + if (all(is.null(dataset),is.null(path_to_nc))){ + stop("Either 'dataset' or 'path_to_nc' needs to be given") + } else if (!any(is.null(dataset),is.null(path_to_nc))) { + stop("Only 'dataset' or 'path_to_nc' can be given") + } if (is.null(path_to_nc)) { path_to_nc <- get_pastclimdata_path() diff --git a/R/get_time_steps.R b/R/get_time_steps.R new file mode 100644 index 00000000..5ee767c6 --- /dev/null +++ b/R/get_time_steps.R @@ -0,0 +1,40 @@ +#' Get time steps for a given dataset +#' +#' Get the time steps available in a given dataset. +#' +#' @param dataset string defining dataset to be downloaded (currently only +#' "Beyer2020", "Krapp2021" and "Example" are available). Leave it unset +#' if using `path_to_nc` +#' @param path_to_nc the path to the directory containing the downloaded +#' reconstructions. Leave it unset if you are using the companion +#' `pastclimData` to store datasets. +#' +#' @export + +get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { + if (all(is.null(dataset),is.null(path_to_nc))){ + stop("Either dataset or path_to_nc needs to be given") + } else if (!any(is.null(dataset),is.null(path_to_nc))) { + stop("Only dataset or path_to_nc can be given") + } + + if (is.null(path_to_nc)) { + path_to_nc <- get_pastclimdata_path() + } + # we get the first available file to get info for the dataset + if (!is.null(dataset)) { + possible_vars <- get_vars_for_dataset(dataset) + this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name + this_file <- file.path(path_to_nc, this_file) + } else { + + } + + climate_nc <- ncdf4::nc_open(this_file) + time_steps <- (climate_nc$dim$time$vals) + ncdf4::nc_close(climate_nc) + mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis + == mis, "start"] * 1000) & + time_steps <= (mis_boundaries[mis_boundaries$mis == mis, "end"] * 1000)] + return(mis_time_steps) +} diff --git a/man/Beyer2020.Rd b/man/Beyer2020.Rd index a07aff0d..8a9fb9e1 100644 --- a/man/Beyer2020.Rd +++ b/man/Beyer2020.Rd @@ -12,12 +12,12 @@ files can be found at Manica, Andrea (2022): pastclim_beyer2020_v1.0.0. figshare. Dataset. \url{https://doi.org/10.6084/m9.figshare.19723405.v1} - + based on -Beyer, R.M., -Krapp, M. & Manica, A. High-resolution terrestrial climate, bioclimate and -vegetation for the last 120,000 years. Sci Data 7, 236 (2020). +Beyer, R.M., +Krapp, M. & Manica, A. High-resolution terrestrial climate, bioclimate and +vegetation for the last 120,000 years. Sci Data 7, 236 (2020). \url{https://doi.org/10.1038/s41597-020-0552-1} The version included in `pastclim` has the icesheets masked, as well as @@ -28,7 +28,7 @@ internal seas (Black and Caspian Sea) removed. The latter are based on: \url{https://www.marineregions.org/gazetteer.php?p=details&id=4282} As there is no reconstruction of their depth throught time, modern outlines -were used for all time steps. +were used for all time steps. Also, for bio15, the coefficient of variation was computed after adding one to monthly estimates, and it was multiplied by 100 following diff --git a/man/Krapp2021.Rd b/man/Krapp2021.Rd index 7ad7733b..a383a8ea 100644 --- a/man/Krapp2021.Rd +++ b/man/Krapp2021.Rd @@ -10,18 +10,18 @@ This dataset covers the last 800k years, at intervals of 1k years. This dataset can be downloaded with \code{\link{download_dataset}}. The files can be found at -Manica, Andrea (2022): pastclim_krapp2021_v1.0.0. figshare. Dataset. -\url{https://doi.org/10.6084/m9.figshare.19733680.v1} - +Manica, Andrea (2022): pastclim_krapp2021_v1.0.0. figshare. Dataset. +\url{https://doi.org/10.6084/m9.figshare.19733680.v1} + based on -Krapp, M., Beyer, R.M., Edmundson, S.L. et al. A statistics-based -reconstruction of high-resolution global terrestrial climate for the last -800,000 years. Sci Data 8, 228 (2021). +Krapp, M., Beyer, R.M., Edmundson, S.L. et al. A statistics-based +reconstruction of high-resolution global terrestrial climate for the last +800,000 years. Sci Data 8, 228 (2021). \url{https://doi.org/10.1038/s41597-021-01009-3} The version included in `pastclim` has the icesheets masked. -Note that, for bio15, we use the corrected version, which follows +Note that, for bio15, we use the corrected version, which follows \url{https://pubs.usgs.gov/ds/691/ds691.pdf} } diff --git a/man/check_var_downloaded.Rd b/man/check_var_downloaded.Rd index 15e68513..6cf53f9a 100644 --- a/man/check_var_downloaded.Rd +++ b/man/check_var_downloaded.Rd @@ -2,7 +2,8 @@ % Please edit documentation in R/get_downloaded_datasets.R \name{check_var_downloaded} \alias{check_var_downloaded} -\title{Internal functions to check whether we have downloaded a given variable for a dataset} +\title{Internal functions to check whether we have downloaded a given variable +for a dataset} \usage{ check_var_downloaded(variable, dataset, path_to_nc = NULL) } @@ -14,6 +15,7 @@ check_var_downloaded(variable, dataset, path_to_nc = NULL) \item{path_to_nc}{path to the netcdf datasets} } \description{ -Internal functions to check whether we have downloaded a given variable for a dataset +Internal functions to check whether we have downloaded a given variable +for a dataset } \keyword{internal} diff --git a/man/check_var_in_nc.Rd b/man/check_var_in_nc.Rd index cee46010..ac6aeccb 100644 --- a/man/check_var_in_nc.Rd +++ b/man/check_var_in_nc.Rd @@ -9,7 +9,8 @@ check_var_in_nc(bio_variables, path_to_nc) \arguments{ \item{bio_variables}{vector of names of variables to be extracted} -\item{path_to_nc}{the path to the file that contains the downloaded resonstructions.} +\item{path_to_nc}{the path to the file that contains the downloaded +resonstructions.} } \description{ Internal function to test a custom nc file. diff --git a/man/climate_for_locations.Rd b/man/climate_for_locations.Rd index 8fb15d5b..c0e2af0a 100644 --- a/man/climate_for_locations.Rd +++ b/man/climate_for_locations.Rd @@ -21,16 +21,19 @@ climate_for_locations( \item{bio_variables}{vector of names of variables to be extracted.} -\item{dataset}{string defining the dataset to use (one of Beyer2020, Krapp2021 or custom).} +\item{dataset}{string defining the dataset to use (one of Beyer2020, +Krapp2021 or custom).} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. +Leave it unset if you are using the companion `pastclimData` to store +datasets.} -\item{nn_interpol}{boolean determining whether nearest neighbour interpolation is -used to estimate climate for cells that lack such information (i.e. they are -under water or ice). Interpolation is only performed from the first ring of -nearest neighbours; if climate is not available, NA will be returned for -that location. Defaults to TRUE.} +\item{nn_interpol}{boolean determining whether nearest neighbour +interpolation is used to estimate climate for cells that lack such +information (i.e. they are under water or ice). Interpolation is only +performed from the first ring of nearest neighbours; if climate is not +available, NA will be returned for that location. Defaults to TRUE.} } \description{ This function extract local climate from Beyer et al for a set of locations diff --git a/man/climate_for_time_slice.Rd b/man/climate_for_time_slice.Rd index 219f14e4..a0ad60d0 100644 --- a/man/climate_for_time_slice.Rd +++ b/man/climate_for_time_slice.Rd @@ -13,8 +13,9 @@ climate_for_time_slice(time_bp, bio_variables, dataset, path_to_nc = NULL) \item{dataset}{string defining the dataset to use} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion `pastclimData` +to store datasets.} } \description{ This function extracts a raster brick for a given time slice. diff --git a/man/download_dataset.Rd b/man/download_dataset.Rd index 8dfe8716..7f580720 100644 --- a/man/download_dataset.Rd +++ b/man/download_dataset.Rd @@ -10,12 +10,12 @@ download_dataset(dataset, bio_variables = NULL, path_to_nc = NULL) \item{dataset}{string defining dataset to be downloaded (currently only "Beyer" is available)} -\item{bio_variables}{one or more variable names to be downloaded. If left to NULL, -all variables available for this dataset will be downloaded} +\item{bio_variables}{one or more variable names to be downloaded. If left +to NULL, all variables available for this dataset will be downloaded} -\item{path_to_nc}{directory where the files will be saved. If not set, the data will -be downloaded into the storage package pastclimData; an error will be -returned if this package is not installed.} +\item{path_to_nc}{directory where the files will be saved. If not set, the +data will be downloaded into the storage package pastclimData; an error +will be returned if this package is not installed.} } \description{ This function downloads paleoclimate reconstructions diff --git a/man/get_biome_classes.Rd b/man/get_biome_classes.Rd index 6cfc7e90..f70bd97b 100644 --- a/man/get_biome_classes.Rd +++ b/man/get_biome_classes.Rd @@ -8,10 +8,11 @@ get_biome_classes(dataset, path_to_nc = NULL) } \arguments{ \item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available)} +"Beyer2020", "Krapp2021" or "Example" are available)} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} } \description{ Get a full list of biomes and how their id as coded in the biome variable diff --git a/man/get_ice_mask.Rd b/man/get_ice_mask.Rd index 860440d9..71931cbd 100644 --- a/man/get_ice_mask.Rd +++ b/man/get_ice_mask.Rd @@ -10,11 +10,12 @@ get_ice_mask(time_bp, dataset, path_to_nc = NULL) \item{time_bp}{time slice in years before present (negative)} \item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available). Note that this function will not work -on an custom dataset.} +"Beyer2020" or "Krapp2021" are available). Note that this function will +not work on an custom dataset.} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} } \description{ Get the ice mask for a dataset at a given timepoint. diff --git a/man/get_land_mask.Rd b/man/get_land_mask.Rd index 2c1923c1..b7344ff8 100644 --- a/man/get_land_mask.Rd +++ b/man/get_land_mask.Rd @@ -10,11 +10,12 @@ get_land_mask(time_bp, dataset, path_to_nc = NULL) \item{time_bp}{time slice in years before present (negative)} \item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available). Note that this function will not work -on an custom dataset.} +"Beyer2020" or "Krapp2021" are available). Note that this function will +not work on an custom dataset.} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} } \description{ Get the land mask for a dataset at a given timepoint. diff --git a/man/get_mis_time_steps.Rd b/man/get_mis_time_steps.Rd index bb0a6a79..b4a92290 100644 --- a/man/get_mis_time_steps.Rd +++ b/man/get_mis_time_steps.Rd @@ -4,7 +4,7 @@ \alias{get_mis_time_steps} \title{Get time steps for a given MIS} \usage{ -get_mis_time_steps(mis, dataset, path_to_nc = NULL) +get_mis_time_steps(mis, dataset = NULL, path_to_nc = NULL) } \arguments{ \item{mis}{string giving the mis; it must use the same spelling as used in @@ -13,8 +13,9 @@ get_mis_time_steps(mis, dataset, path_to_nc = NULL) \item{dataset}{string defining dataset to be downloaded (currently only "Beyer2020" or "Krapp2021" are available)} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} } \description{ Get the time steps available in a given dataset for a MIS. diff --git a/man/get_time_steps.Rd b/man/get_time_steps.Rd new file mode 100644 index 00000000..7ef59203 --- /dev/null +++ b/man/get_time_steps.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get_time_steps.R +\name{get_time_steps} +\alias{get_time_steps} +\title{Get time steps for a given dataset} +\usage{ +get_time_steps(dataset = NULL, path_to_nc = NULL) +} +\arguments{ +\item{dataset}{string defining dataset to be downloaded (currently only +"Beyer2020", "Krapp2021" and "Example" are available). Leave it unset +if using `path_to_nc`} + +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} +} +\description{ +Get the time steps available in a given dataset. +} diff --git a/man/get_vars_for_dataset.Rd b/man/get_vars_for_dataset.Rd index daf35683..ca2eb788 100644 --- a/man/get_vars_for_dataset.Rd +++ b/man/get_vars_for_dataset.Rd @@ -7,12 +7,12 @@ get_vars_for_dataset(dataset) } \arguments{ -\item{dataset}{string defining dataset to for which variables are given. It can take the value -"Beyer2020", "Krapp2021" or "Example"} +\item{dataset}{string defining dataset to for which variables are given. +It can take the value "Beyer2020", "Krapp2021" or "Example"} } \description{ This function lists the variables available for a given dataset. Note that the spelling and use of capitals in names might differ from the original -publications, as `pastclim` harmonises the names of variables across different -reconstructions. +publications, as `pastclim` harmonises the names of variables across +different reconstructions. } diff --git a/man/region_outline_union.Rd b/man/region_outline_union.Rd index b9c7409a..cd666352 100644 --- a/man/region_outline_union.Rd +++ b/man/region_outline_union.Rd @@ -15,6 +15,7 @@ region_outline_union } \description{ An \code{sf} object containing outlines for major regions. Each outline is -represented as a single polygon. If you want multiple polygons, use \link{region_outline}. +represented as a single polygon. If you want multiple polygons, use +\link{region_outline}. } \keyword{datasets} diff --git a/man/time_series_for_locations.Rd b/man/time_series_for_locations.Rd index b43c19c2..5530a656 100644 --- a/man/time_series_for_locations.Rd +++ b/man/time_series_for_locations.Rd @@ -14,8 +14,9 @@ time_series_for_locations(x, bio_variables, dataset, path_to_nc = NULL) \item{dataset}{string defining the dataset to use.} -\item{path_to_nc}{the path to the directory containing the downloaded resonstructions. -Leave it unset if you are using the companion `pastclimData` to store datasets.} +\item{path_to_nc}{the path to the directory containing the downloaded +reconstructions. Leave it unset if you are using the companion +`pastclimData` to store datasets.} } \description{ This function extract a time series of local climate for diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R index 02b21b36..55180a18 100644 --- a/tests/testthat/test_get_mis_time_steps.R +++ b/tests/testthat/test_get_mis_time_steps.R @@ -1,5 +1,5 @@ test_that("get_mis_time_steps for pastclimData", { - skip_if(requireNamespace("pastclimData")) + skip_if(!requireNamespace("pastclimData")) expect_equal(get_mis_time_steps(mis="2",dataset="Example"), c(-20000,-15000), ignore_attr = TRUE) }) @@ -10,3 +10,11 @@ test_that("get_mis_time_steps for local file", { expect_equal(get_mis_time_steps(mis="2",path_to_nc = path_to_example_nc), c(-20000,-15000), ignore_attr = TRUE) }) + +test_that("get_mis_time_steps requires correct variables", { + expect_error(get_mis_time_steps(mis="blah",path_to_nc = path_to_example_nc), + "'mis' should be one of") + expect_error(get_mis_time_steps(mis=2), "Either 'dataset' or ") + expect_error(get_mis_time_steps(mis=2, dataset = "blah", path_to_nc = "blah"), + "Only 'dataset' or ") +}) \ No newline at end of file diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R new file mode 100644 index 00000000..4089525c --- /dev/null +++ b/tests/testthat/test_get_time_steps.R @@ -0,0 +1,3 @@ +test_that("get_time_steps", { + +}) From 9f8101c73e8219398bd0c3e4e312ec30335054cc Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 15:48:11 +0100 Subject: [PATCH 03/83] Quick lint [skip ci] --- R/_delta_downscale.R | 32 +++++++++---------- R/check_var_in_nc.R | 3 +- R/climate_for_location.R | 20 ++++++++---- R/climate_for_time_slice.R | 6 ++-- R/download_dataset.R | 3 +- R/get_available_datasets.R | 3 +- R/get_biome_classes.R | 6 ++-- R/get_downloaded_datasets.R | 22 ++++++++----- R/get_mis_time_steps.R | 6 ++-- R/get_pastclimdata_path.R | 3 +- R/get_time_steps.R | 10 +++--- R/get_vars_for_dataset.R | 2 +- R/time_series_for_location.R | 6 ++-- .../extract_topography_for_beyer2020_part1.R | 2 +- .../extract_topography_for_krapp2021_part1.R | 2 +- .../verify_completeness_of_variables.R | 3 +- tests/testthat/test_climate_for_location.R | 3 +- tests/testthat/test_climate_for_slice.R | 9 +++--- tests/testthat/test_get_available_dataset.R | 8 +++-- tests/testthat/test_get_downloaded_datasets.R | 5 +-- tests/testthat/test_get_mis_time_steps.R | 31 +++++++++++------- tests/testthat/test_get_time_steps.R | 2 +- .../testthat/test_time_series_for_location.R | 3 +- 23 files changed, 113 insertions(+), 77 deletions(-) diff --git a/R/_delta_downscale.R b/R/_delta_downscale.R index 22e07299..0f91ae94 100644 --- a/R/_delta_downscale.R +++ b/R/_delta_downscale.R @@ -1,47 +1,45 @@ delta_compute <- function(x_modern, high_res) { # get the extent of the high res reference - + # check that it is compatible with the x_modern - - # disaggregate the x_modern SpatRaster to the resolution of the high res with "near" - + + # disaggregate the x_modern SpatRaster to the resolution of + # the high res with "near" + # compute anomalies against the modern - - } -delta_downscale <- function (x, delta_rast, time_point, sea_level_path) { +delta_downscale <- function(x, delta_rast, time_point, sea_level_path) { # check that extent and resolutions are compatible - + # sea level https://www.ncdc.noaa.gov/paleo-search/study/19982 etopo1 <- terra::rast("ETOPO1_Ice_c_gmt4.grd") sea_level <- read.table("spratt2016.txt", header = TRUE, row.names = 1) - + # downscale x with near - + # adjust the land area based on sea level - + # expand with bilinear for areas without information for both x and delta_rast - + # apply the delta_rast to x - } -eaf <- extent(30,55,-15,20) +eaf <- extent(30, 55, -15, 20) r <- rast() e <- ext(r) as.vector(e) as.character(e) ext(r) <- c(0, 2.5, 0, 1.5) -af_precip <- crop(bio12_rasterbrick, eaf) #crop to africa +af_precip <- crop(bio12_rasterbrick, eaf) # crop to africa -eaf <- terra::ext(30,55,-15,20) +eaf <- terra::ext(30, 55, -15, 20) etopo1 <- terra::rast("ETOPO1_Ice_c_gmt4.grd") -etopo30 <- terra::agg(etopo1,fact=30) +etopo30 <- terra::agg(etopo1, fact = 30) bi <- terra::boundaries(land_mask) diff --git a/R/check_var_in_nc.R b/R/check_var_in_nc.R index da355570..5aebc279 100644 --- a/R/check_var_in_nc.R +++ b/R/check_var_in_nc.R @@ -25,7 +25,8 @@ check_var_in_nc <- function(bio_variables, path_to_nc) { if (!all(bio_variables %in% nc_in_vars)) { stop( "variable (", paste(bio_variables[!bio_variables %in% nc_in_vars], - collapse = ", "), + collapse = ", " + ), ") not present in the file" ) } diff --git a/R/climate_for_location.R b/R/climate_for_location.R index c08fb598..e61335dc 100644 --- a/R/climate_for_location.R +++ b/R/climate_for_location.R @@ -64,8 +64,10 @@ climate_for_locations <- this_var_nc <- this_var } if (is.null(time_indeces)) { - time_indeces <- time_bp_to_index(time_bp = time_bp, path_to_nc = - this_file) + time_indeces <- time_bp_to_index( + time_bp = time_bp, path_to_nc = + this_file + ) unique_time_indeces <- unique(time_indeces) } climate_brick <- terra::rast(this_file, subds = this_var_nc) @@ -79,19 +81,23 @@ climate_for_locations <- x = this_slice, y = x[this_slice_indeces, ] ) - locations_data[this_slice_indeces, this_var] <- this_climate[, - ncol(this_climate)] + locations_data[this_slice_indeces, this_var] <- this_climate[ + , + ncol(this_climate) + ] if (nn_interpol) { locations_to_move <- this_slice_indeces[this_slice_indeces %in% - which(is.na(locations_data[, this_var]))] + which(is.na(locations_data[, this_var]))] if (length(locations_to_move) == 0) { next } for (i in locations_to_move) { if (inherits(x, "data.frame")) { cell_id <- - terra::cellFromXY(climate_brick, as.matrix(locations_data[i, - 1:2])) + terra::cellFromXY(climate_brick, as.matrix(locations_data[ + i, + 1:2 + ])) } else { cell_id <- x[i] } diff --git a/R/climate_for_time_slice.R b/R/climate_for_time_slice.R index c0d2956f..472a037b 100644 --- a/R/climate_for_time_slice.R +++ b/R/climate_for_time_slice.R @@ -44,8 +44,10 @@ climate_for_time_slice <- this_var_nc <- this_var } if (is.null(time_index)) { - time_index <- time_bp_to_index(time_bp = time_bp, path_to_nc = - this_file) + time_index <- time_bp_to_index( + time_bp = time_bp, path_to_nc = + this_file + ) } var_brick <- terra::rast(this_file, subds = this_var_nc) var_slice <- terra::subset(var_brick, subset = time_index) diff --git a/R/download_dataset.R b/R/download_dataset.R index 323feeb1..2f757af5 100644 --- a/R/download_dataset.R +++ b/R/download_dataset.R @@ -16,7 +16,8 @@ download_dataset <- function(dataset, bio_variables = NULL, path_to_nc = NULL) { if (is.null(path_to_nc)) { path_to_nc <- system.file("extdata", package = "pastclimData") if (path_to_nc == "") { - stop("the parameter path was not set, and the package pastclimData is not installed.") + stop("the parameter path was not set, and the package pastclimData", + "is not installed.") } } diff --git a/R/get_available_datasets.R b/R/get_available_datasets.R index d1c2597c..9008903d 100644 --- a/R/get_available_datasets.R +++ b/R/get_available_datasets.R @@ -21,7 +21,8 @@ get_available_datasets <- function() { check_available_dataset <- function(dataset) { if (!dataset %in% get_available_datasets()) { stop("'dataset' must be one of ", paste(get_available_datasets(), - collapse = ", ")) + collapse = ", " + )) } else { return(TRUE) } diff --git a/R/get_biome_classes.R b/R/get_biome_classes.R index 0306d099..cde74e38 100644 --- a/R/get_biome_classes.R +++ b/R/get_biome_classes.R @@ -31,8 +31,10 @@ get_biome_classes <- function(dataset, path_to_nc = NULL) { ) row.names(biome_categories) <- NULL } else if (dataset == "Krapp2021") { - biomes_string <- trimws(unlist(strsplit(biome_attributes$biomes, split = - ";"))) + biomes_string <- trimws(unlist(strsplit(biome_attributes$biomes, + split = + ";" + ))) biomes_string <- biomes_string[-length(biomes_string)] biomes_string <- substr(biomes_string, 4, nchar(biomes_string)) biome_categories <- data.frame( diff --git a/R/get_downloaded_datasets.R b/R/get_downloaded_datasets.R index be0b425d..972755f7 100644 --- a/R/get_downloaded_datasets.R +++ b/R/get_downloaded_datasets.R @@ -12,11 +12,13 @@ get_downloaded_datasets <- function(path_to_nc = NULL) { } all_nc_files <- list.files(path_to_nc) files_subset <- files_by_dataset[files_by_dataset$file_name %in% - all_nc_files, ] + all_nc_files, ] downloaded_vars <- list() for (dataset in unique(files_subset$dataset)) { - downloaded_vars[[dataset]] <- files_subset[files_subset$dataset == dataset, - "variable"] + downloaded_vars[[dataset]] <- files_subset[ + files_subset$dataset == dataset, + "variable" + ] } downloaded_vars } @@ -36,11 +38,15 @@ check_var_downloaded <- function(variable, dataset, path_to_nc = NULL) { # test if we have downloaded already if (!all(variable %in% get_downloaded_datasets(path_to_nc = path_to_nc) - [[dataset]])) { - missing_vars <- variable[!variable %in% get_downloaded_datasets(path_to_nc = - path_to_nc)[[dataset]]] - stop("variable (", paste(missing_vars, collapse = ", "), - ") not yet downloaded, use `download_dataset()`") + [[dataset]])) { + missing_vars <- variable[!variable %in% get_downloaded_datasets( + path_to_nc = + path_to_nc + )[[dataset]]] + stop( + "variable (", paste(missing_vars, collapse = ", "), + ") not yet downloaded, use `download_dataset()`" + ) } return(TRUE) } diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index d50291f4..2ce238b9 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -16,9 +16,9 @@ get_mis_time_steps <- function(mis, dataset = NULL, path_to_nc = NULL) { if (!mis %in% mis_boundaries$mis) { stop("'mis' should be one of ", paste(mis_boundaries$mis, collapse = ",")) } - if (all(is.null(dataset),is.null(path_to_nc))){ + if (all(is.null(dataset), is.null(path_to_nc))) { stop("Either 'dataset' or 'path_to_nc' needs to be given") - } else if (!any(is.null(dataset),is.null(path_to_nc))) { + } else if (!any(is.null(dataset), is.null(path_to_nc))) { stop("Only 'dataset' or 'path_to_nc' can be given") } @@ -34,7 +34,7 @@ get_mis_time_steps <- function(mis, dataset = NULL, path_to_nc = NULL) { time_steps <- (climate_nc$dim$time$vals) ncdf4::nc_close(climate_nc) mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis - == mis, "start"] * 1000) & + == mis, "start"] * 1000) & time_steps <= (mis_boundaries[mis_boundaries$mis == mis, "end"] * 1000)] return(mis_time_steps) } diff --git a/R/get_pastclimdata_path.R b/R/get_pastclimdata_path.R index 007fbe35..b3018ff6 100644 --- a/R/get_pastclimdata_path.R +++ b/R/get_pastclimdata_path.R @@ -8,7 +8,8 @@ get_pastclimdata_path <- function() { path_to_nc <- system.file("extdata", package = "pastclimData") if (path_to_nc == "") { - stop('the parameter path_to_nc was not set, and the package pastclimData is not installed.\n + stop('the parameter path_to_nc was not set, and the package pastclimData', + 'is not installed.\n You can install pastclimData with the following command:\n devtools::install_github("EvolEcolGroup/pastclimData",ref="master")') } diff --git a/R/get_time_steps.R b/R/get_time_steps.R index 5ee767c6..55e2dbd0 100644 --- a/R/get_time_steps.R +++ b/R/get_time_steps.R @@ -3,7 +3,7 @@ #' Get the time steps available in a given dataset. #' #' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020", "Krapp2021" and "Example" are available). Leave it unset +#' "Beyer2020", "Krapp2021" and "Example" are available). Leave it unset #' if using `path_to_nc` #' @param path_to_nc the path to the directory containing the downloaded #' reconstructions. Leave it unset if you are using the companion @@ -12,9 +12,9 @@ #' @export get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { - if (all(is.null(dataset),is.null(path_to_nc))){ + if (all(is.null(dataset), is.null(path_to_nc))) { stop("Either dataset or path_to_nc needs to be given") - } else if (!any(is.null(dataset),is.null(path_to_nc))) { + } else if (!any(is.null(dataset), is.null(path_to_nc))) { stop("Only dataset or path_to_nc can be given") } @@ -27,14 +27,14 @@ get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name this_file <- file.path(path_to_nc, this_file) } else { - + } climate_nc <- ncdf4::nc_open(this_file) time_steps <- (climate_nc$dim$time$vals) ncdf4::nc_close(climate_nc) mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis - == mis, "start"] * 1000) & + == mis, "start"] * 1000) & time_steps <= (mis_boundaries[mis_boundaries$mis == mis, "end"] * 1000)] return(mis_time_steps) } diff --git a/R/get_vars_for_dataset.R b/R/get_vars_for_dataset.R index 0640952d..f83cf212 100644 --- a/R/get_vars_for_dataset.R +++ b/R/get_vars_for_dataset.R @@ -48,5 +48,5 @@ check_available_variable <- function(variable, dataset) { get_varname <- function(variable, dataset) { return(files_by_dataset$ncvar[files_by_dataset$variable == variable & - files_by_dataset$dataset == dataset]) + files_by_dataset$dataset == dataset]) } diff --git a/R/time_series_for_location.R b/R/time_series_for_location.R index 3ae62e53..43a0a5a3 100644 --- a/R/time_series_for_location.R +++ b/R/time_series_for_location.R @@ -53,8 +53,10 @@ time_series_for_locations <- if (!("time" %in% names(time_series_df))) { n_time_steps <- length(time(climate_brick)) n_locations <- nrow(time_series_df) - time_series_df <- time_series_df[rep(seq_len(nrow(time_series_df)), - n_time_steps), ] + time_series_df <- time_series_df[rep( + seq_len(nrow(time_series_df)), + n_time_steps + ), ] time_series_df$time <- rep(time(climate_brick), each = n_locations) } this_var_ts <- terra::extract(climate_brick, x) diff --git a/inst/rawdata_scripts/extract_topography_for_beyer2020_part1.R b/inst/rawdata_scripts/extract_topography_for_beyer2020_part1.R index 041bd1e8..471b0afe 100644 --- a/inst/rawdata_scripts/extract_topography_for_beyer2020_part1.R +++ b/inst/rawdata_scripts/extract_topography_for_beyer2020_part1.R @@ -25,7 +25,7 @@ problematic_cells <- list() library(terra) for (i in time_steps_bp) { - time_step_row <- -(i / 1000) + 1 + time_step_row <- - (i / 1000) + 1 # now check neighbours of each boundary cell, and expand out if < sea level sea_level_now <- sea_level$SeaLev_longPC1[time_step_row] diff --git a/inst/rawdata_scripts/extract_topography_for_krapp2021_part1.R b/inst/rawdata_scripts/extract_topography_for_krapp2021_part1.R index 15631e82..5e3de64b 100644 --- a/inst/rawdata_scripts/extract_topography_for_krapp2021_part1.R +++ b/inst/rawdata_scripts/extract_topography_for_krapp2021_part1.R @@ -47,7 +47,7 @@ time_steps_bp <- time_steps_bp[!is.na(time_steps_bp)] for (i in time_steps_bp) { cat(i) - time_step_row <- -(i / 1000) + 1 + time_step_row <- - (i / 1000) + 1 # now check neighbours of each boundary cell, and expand out if < sea level sea_level_now <- sea_level$SeaLev_longPC1[time_step_row] diff --git a/inst/rawdata_scripts/verify_completeness_of_variables.R b/inst/rawdata_scripts/verify_completeness_of_variables.R index c7c7fb41..f025fc35 100644 --- a/inst/rawdata_scripts/verify_completeness_of_variables.R +++ b/inst/rawdata_scripts/verify_completeness_of_variables.R @@ -2,7 +2,8 @@ dataset <- "Krapp2021" library(pastclim) this_path <- pastclim::get_pastclimdata_path() -vars_for_dataset <- pastclim:::get_file_for_dataset(get_vars_for_dataset(dataset), dataset) +vars_for_dataset <- pastclim:::get_file_for_dataset( + get_vars_for_dataset(dataset), dataset) file1 <- ncdf4::nc_open(paste0(this_path, "/", vars_for_dataset$file_name[1])) n_steps <- file1$dim$time$len diff --git a/tests/testthat/test_climate_for_location.R b/tests/testthat/test_climate_for_location.R index 24c8e28d..cb7f5eaf 100644 --- a/tests/testthat/test_climate_for_location.R +++ b/tests/testthat/test_climate_for_location.R @@ -55,7 +55,8 @@ test_that("climate_for_time_slice", { # now treat it as if it was a custom dataset path_to_example_nc <- system.file("/extdata/example_climate.nc", - package = "pastclim") + package = "pastclim" + ) this_climate <- climate_for_locations( x = locations[, c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO12"), diff --git a/tests/testthat/test_climate_for_slice.R b/tests/testthat/test_climate_for_slice.R index b9944328..55415ac2 100644 --- a/tests/testthat/test_climate_for_slice.R +++ b/tests/testthat/test_climate_for_slice.R @@ -2,7 +2,7 @@ test_that("climate_for_time_slice", { # using standard dataset path_to_example_nc <- system.file("/extdata/", package = "pastclim") expect_true(inherits(climate_for_time_slice(-20000, c("bio01", "bio12"), - "Example", + "Example", path_to_nc = path_to_example_nc ), "SpatRaster")) # if we try to use a variable that does not exist @@ -16,10 +16,11 @@ test_that("climate_for_time_slice", { # now treat it as if it was a custom dataset path_to_example_nc <- system.file("/extdata/example_climate.nc", - package = "pastclim") + package = "pastclim" + ) expect_true(inherits(climate_for_time_slice(-20000, c("BIO1", "BIO12"), - "custom", - path_to_nc = path_to_example_nc + "custom", + path_to_nc = path_to_example_nc ), "SpatRaster")) # if we try to use a variable that does not exist expect_error(climate_for_time_slice(-20000, c("BIO01", "lai"), "custom", diff --git a/tests/testthat/test_get_available_dataset.R b/tests/testthat/test_get_available_dataset.R index 8f07c6ea..212918f8 100644 --- a/tests/testthat/test_get_available_dataset.R +++ b/tests/testthat/test_get_available_dataset.R @@ -1,7 +1,9 @@ testthat::test_that("get_and_check_available_datasets", { - testthat::expect_true(all(get_available_datasets() == c("Beyer2020", - "Krapp2021", - "Example"))) + testthat::expect_true(all(get_available_datasets() == c( + "Beyer2020", + "Krapp2021", + "Example" + ))) testthat::expect_true(check_available_dataset("Example")) testthat::expect_error( check_available_dataset("foo"), diff --git a/tests/testthat/test_get_downloaded_datasets.R b/tests/testthat/test_get_downloaded_datasets.R index 98a26ae2..2e0a1ade 100644 --- a/tests/testthat/test_get_downloaded_datasets.R +++ b/tests/testthat/test_get_downloaded_datasets.R @@ -2,12 +2,13 @@ testthat::test_that("get_downloaded_datasets", { path_to_example_nc <- system.file("/extdata/", package = "pastclim") # there is only the Example dataset available expect_true(length(get_downloaded_datasets(path_to_nc = path_to_example_nc)) - == 1) + == 1) # return an empty list if there are no files expect_true(length(get_downloaded_datasets(path_to_nc = "./foo")) == 0) # check that we have downloaded a variable expect_true(check_var_downloaded("bio01", "Example", - path_to_nc = path_to_example_nc)) + path_to_nc = path_to_example_nc + )) # raise error if the variable is not available expect_error( check_var_downloaded("npp", "Example", path_to_nc = path_to_example_nc), diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R index 55180a18..e8bc1b5d 100644 --- a/tests/testthat/test_get_mis_time_steps.R +++ b/tests/testthat/test_get_mis_time_steps.R @@ -1,20 +1,29 @@ test_that("get_mis_time_steps for pastclimData", { skip_if(!requireNamespace("pastclimData")) - expect_equal(get_mis_time_steps(mis="2",dataset="Example"), - c(-20000,-15000), ignore_attr = TRUE) + expect_equal(get_mis_time_steps(mis = "2", dataset = "Example"), + c(-20000, -15000), + ignore_attr = TRUE + ) }) test_that("get_mis_time_steps for local file", { path_to_example_nc <- system.file("/extdata/", "example_climate.nc", - package = "pastclim") - expect_equal(get_mis_time_steps(mis="2",path_to_nc = path_to_example_nc), - c(-20000,-15000), ignore_attr = TRUE) + package = "pastclim" + ) + expect_equal(get_mis_time_steps(mis = "2", path_to_nc = path_to_example_nc), + c(-20000, -15000), + ignore_attr = TRUE + ) }) test_that("get_mis_time_steps requires correct variables", { - expect_error(get_mis_time_steps(mis="blah",path_to_nc = path_to_example_nc), - "'mis' should be one of") - expect_error(get_mis_time_steps(mis=2), "Either 'dataset' or ") - expect_error(get_mis_time_steps(mis=2, dataset = "blah", path_to_nc = "blah"), - "Only 'dataset' or ") -}) \ No newline at end of file + expect_error( + get_mis_time_steps(mis = "blah", path_to_nc = path_to_example_nc), + "'mis' should be one of" + ) + expect_error(get_mis_time_steps(mis = 2), "Either 'dataset' or ") + expect_error( + get_mis_time_steps(mis = 2, dataset = "blah", path_to_nc = "blah"), + "Only 'dataset' or " + ) +}) diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R index 4089525c..56581238 100644 --- a/tests/testthat/test_get_time_steps.R +++ b/tests/testthat/test_get_time_steps.R @@ -1,3 +1,3 @@ test_that("get_time_steps", { - + }) diff --git a/tests/testthat/test_time_series_for_location.R b/tests/testthat/test_time_series_for_location.R index 703cadfb..520eee9c 100644 --- a/tests/testthat/test_time_series_for_location.R +++ b/tests/testthat/test_time_series_for_location.R @@ -42,7 +42,8 @@ test_that("time_series_for_location", { # now treat it as if it was a custom dataset path_to_example_nc <- system.file("/extdata/example_climate.nc", - package = "pastclim") + package = "pastclim" + ) locations_ts <- time_series_for_locations( x = locations[, c("longitude", "latitude")], bio_variables = c("BIO1", "BIO12"), From 0262ad7d4014e666302f8d94302ca4468d2567fb Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 15:55:57 +0100 Subject: [PATCH 04/83] implement get_time_steps [skip ci] --- R/_delta_downscale.R | 2 +- R/download_dataset.R | 6 ++++-- R/get_mis_time_steps.R | 17 +---------------- R/get_pastclimdata_path.R | 8 +++++--- R/get_time_steps.R | 19 ++++++------------- tests/testthat/test_get_time_steps.R | 28 +++++++++++++++++++++++++++- 6 files changed, 44 insertions(+), 36 deletions(-) diff --git a/R/_delta_downscale.R b/R/_delta_downscale.R index 0f91ae94..df3f462c 100644 --- a/R/_delta_downscale.R +++ b/R/_delta_downscale.R @@ -3,7 +3,7 @@ delta_compute <- function(x_modern, high_res) { # check that it is compatible with the x_modern - # disaggregate the x_modern SpatRaster to the resolution of + # disaggregate the x_modern SpatRaster to the resolution of # the high res with "near" # compute anomalies against the modern diff --git a/R/download_dataset.R b/R/download_dataset.R index 2f757af5..f7b9945e 100644 --- a/R/download_dataset.R +++ b/R/download_dataset.R @@ -16,8 +16,10 @@ download_dataset <- function(dataset, bio_variables = NULL, path_to_nc = NULL) { if (is.null(path_to_nc)) { path_to_nc <- system.file("extdata", package = "pastclimData") if (path_to_nc == "") { - stop("the parameter path was not set, and the package pastclimData", - "is not installed.") + stop( + "the parameter path was not set, and the package pastclimData", + "is not installed." + ) } } diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index 2ce238b9..951ef8ee 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -16,23 +16,8 @@ get_mis_time_steps <- function(mis, dataset = NULL, path_to_nc = NULL) { if (!mis %in% mis_boundaries$mis) { stop("'mis' should be one of ", paste(mis_boundaries$mis, collapse = ",")) } - if (all(is.null(dataset), is.null(path_to_nc))) { - stop("Either 'dataset' or 'path_to_nc' needs to be given") - } else if (!any(is.null(dataset), is.null(path_to_nc))) { - stop("Only 'dataset' or 'path_to_nc' can be given") - } - - if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() - # we get the first available file to get info for the dataset - possible_vars <- get_vars_for_dataset(dataset) - this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name - path_to_nc <- file.path(path_to_nc, this_file) - } - climate_nc <- ncdf4::nc_open(path_to_nc) - time_steps <- (climate_nc$dim$time$vals) - ncdf4::nc_close(climate_nc) + time_steps <- get_time_steps(dataset = dataset, path_to_nc = path_to_nc) mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis == mis, "start"] * 1000) & time_steps <= (mis_boundaries[mis_boundaries$mis == mis, "end"] * 1000)] diff --git a/R/get_pastclimdata_path.R b/R/get_pastclimdata_path.R index b3018ff6..b7336de1 100644 --- a/R/get_pastclimdata_path.R +++ b/R/get_pastclimdata_path.R @@ -8,10 +8,12 @@ get_pastclimdata_path <- function() { path_to_nc <- system.file("extdata", package = "pastclimData") if (path_to_nc == "") { - stop('the parameter path_to_nc was not set, and the package pastclimData', - 'is not installed.\n + stop( + "the parameter path_to_nc was not set, and the package pastclimData", + 'is not installed.\n You can install pastclimData with the following command:\n - devtools::install_github("EvolEcolGroup/pastclimData",ref="master")') + devtools::install_github("EvolEcolGroup/pastclimData",ref="master")' + ) } return(path_to_nc) } diff --git a/R/get_time_steps.R b/R/get_time_steps.R index 55e2dbd0..9b9aad68 100644 --- a/R/get_time_steps.R +++ b/R/get_time_steps.R @@ -13,28 +13,21 @@ get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { if (all(is.null(dataset), is.null(path_to_nc))) { - stop("Either dataset or path_to_nc needs to be given") + stop("Either 'dataset' or 'path_to_nc' needs to be given") } else if (!any(is.null(dataset), is.null(path_to_nc))) { - stop("Only dataset or path_to_nc can be given") + stop("Only 'dataset' or 'path_to_nc' can be given") } if (is.null(path_to_nc)) { path_to_nc <- get_pastclimdata_path() - } - # we get the first available file to get info for the dataset - if (!is.null(dataset)) { + # we get the first available file to get info for the dataset possible_vars <- get_vars_for_dataset(dataset) this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name - this_file <- file.path(path_to_nc, this_file) - } else { - + path_to_nc <- file.path(path_to_nc, this_file) } - climate_nc <- ncdf4::nc_open(this_file) + climate_nc <- ncdf4::nc_open(path_to_nc) time_steps <- (climate_nc$dim$time$vals) ncdf4::nc_close(climate_nc) - mis_time_steps <- time_steps[time_steps > (mis_boundaries[mis_boundaries$mis - == mis, "start"] * 1000) & - time_steps <= (mis_boundaries[mis_boundaries$mis == mis, "end"] * 1000)] - return(mis_time_steps) + return(time_steps) } diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R index 56581238..3412635d 100644 --- a/tests/testthat/test_get_time_steps.R +++ b/tests/testthat/test_get_time_steps.R @@ -1,3 +1,29 @@ -test_that("get_time_steps", { +test_that("get_time_steps requires correct variables", { + expect_error(get_time_steps(), "Either 'dataset' or ") + expect_error( + get_time_steps(dataset = "blah", path_to_nc = "blah"), + "Only 'dataset' or " + ) +}) + +test_that("get_time_steps for pastclimData", { + skip_if(!requireNamespace("pastclimData")) + expect_equal(get_time_steps(dataset = "Example"), + c(-20000, -15000, -10000, -5000, 0), + ignore_attr = TRUE + ) +}) +test_that("get_time_steps for local file", { + path_to_example_nc <- system.file("/extdata/", "example_climate.nc", + package = "pastclim" + ) + expect_equal(get_time_steps(path_to_nc = path_to_example_nc), + c(-20000, -15000, -10000, -5000, 0), + ignore_attr = TRUE + ) + expect_equal(get_mis_time_steps(mis = "2", path_to_nc = path_to_example_nc), + c(-20000, -15000), + ignore_attr = TRUE + ) }) From 7c2b4d27eb23375b08157604134986076b2c91af Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 21:28:19 +0100 Subject: [PATCH 05/83] Remove dependency on pastclimData [skip ci] --- DESCRIPTION | 5 +- NAMESPACE | 3 +- R/climate_for_location.R | 2 +- R/climate_for_time_slice.R | 2 +- R/get_biome_classes.R | 2 +- R/get_downloaded_datasets.R | 2 +- R/get_pastclimdata_path.R | 19 --- R/get_time_steps.R | 2 +- R/set_data_path.R | 87 ++++++++++++++ R/time_series_for_location.R | 2 +- R/zzz.R | 10 ++ .../verify_completeness_of_variables.R | 2 +- {R => inst/temp}/_delta_downscale.R | 0 man/get_data_path.Rd | 19 +++ man/get_pastclimdata_path.Rd | 12 -- man/get_time_steps.Rd | 2 +- man/set_data_path.Rd | 19 +++ tests/testthat/test_get_pastclimdata_path.R | 12 -- tests/testthat/test_get_set_data_path.R | 6 + vignettes/pastclim_overview.Rmd | 112 +++++++++--------- 20 files changed, 209 insertions(+), 111 deletions(-) delete mode 100644 R/get_pastclimdata_path.R create mode 100644 R/set_data_path.R create mode 100644 R/zzz.R rename {R => inst/temp}/_delta_downscale.R (100%) create mode 100644 man/get_data_path.Rd delete mode 100644 man/get_pastclimdata_path.Rd create mode 100644 man/set_data_path.Rd delete mode 100644 tests/testthat/test_get_pastclimdata_path.R create mode 100644 tests/testthat/test_get_set_data_path.R diff --git a/DESCRIPTION b/DESCRIPTION index b6bdc22a..68863fc2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -26,7 +26,7 @@ LazyData: true LazyDataCompression: xz RoxygenNote: 7.2.1 Depends: - R (>= 3.5.0) + R (>= 4.0.0) Imports: curl, ncdf4, @@ -34,10 +34,7 @@ Imports: Suggests: rmarkdown, knitr, - pastclimData, sf, testthat (>= 3.0.0) -Remotes: - EvolEcolGroup/pastclimData VignetteBuilder: knitr Config/testthat/edition: 3 diff --git a/NAMESPACE b/NAMESPACE index 9a638e99..c33afdd5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,12 +5,13 @@ export(climate_for_time_slice) export(download_dataset) export(get_available_datasets) export(get_biome_classes) +export(get_data_path) export(get_downloaded_datasets) export(get_ice_mask) export(get_land_mask) export(get_mis_time_steps) -export(get_pastclimdata_path) export(get_time_steps) export(get_vars_for_dataset) +export(set_data_path) export(time_series_for_locations) import(terra) diff --git a/R/climate_for_location.R b/R/climate_for_location.R index e61335dc..51bd58c3 100644 --- a/R/climate_for_location.R +++ b/R/climate_for_location.R @@ -37,7 +37,7 @@ climate_for_locations <- } if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() } # reorder the inputs by time diff --git a/R/climate_for_time_slice.R b/R/climate_for_time_slice.R index 472a037b..2baa84bc 100644 --- a/R/climate_for_time_slice.R +++ b/R/climate_for_time_slice.R @@ -27,7 +27,7 @@ climate_for_time_slice <- # if path_to_nc is not set, use pastclimData if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() } time_index <- NULL diff --git a/R/get_biome_classes.R b/R/get_biome_classes.R index cde74e38..0da03063 100644 --- a/R/get_biome_classes.R +++ b/R/get_biome_classes.R @@ -13,7 +13,7 @@ get_biome_classes <- function(dataset, path_to_nc = NULL) { if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() } # test that we ahve the biome variable check_var_downloaded("biome", dataset, path_to_nc = path_to_nc) diff --git a/R/get_downloaded_datasets.R b/R/get_downloaded_datasets.R index 972755f7..1ab96854 100644 --- a/R/get_downloaded_datasets.R +++ b/R/get_downloaded_datasets.R @@ -8,7 +8,7 @@ get_downloaded_datasets <- function(path_to_nc = NULL) { if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() } all_nc_files <- list.files(path_to_nc) files_subset <- files_by_dataset[files_by_dataset$file_name %in% diff --git a/R/get_pastclimdata_path.R b/R/get_pastclimdata_path.R deleted file mode 100644 index b7336de1..00000000 --- a/R/get_pastclimdata_path.R +++ /dev/null @@ -1,19 +0,0 @@ -#' Get the path for pastclimdata. -#' -#' This function returns the path for pastclimData where reconstructions -#' are stored. -#' -#' @export - -get_pastclimdata_path <- function() { - path_to_nc <- system.file("extdata", package = "pastclimData") - if (path_to_nc == "") { - stop( - "the parameter path_to_nc was not set, and the package pastclimData", - 'is not installed.\n - You can install pastclimData with the following command:\n - devtools::install_github("EvolEcolGroup/pastclimData",ref="master")' - ) - } - return(path_to_nc) -} diff --git a/R/get_time_steps.R b/R/get_time_steps.R index 9b9aad68..24f02237 100644 --- a/R/get_time_steps.R +++ b/R/get_time_steps.R @@ -19,7 +19,7 @@ get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { } if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() # we get the first available file to get info for the dataset possible_vars <- get_vars_for_dataset(dataset) this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name diff --git a/R/set_data_path.R b/R/set_data_path.R new file mode 100644 index 00000000..dd051032 --- /dev/null +++ b/R/set_data_path.R @@ -0,0 +1,87 @@ +#' Set the data path where climate reconstructions will be stored +#' +#' This function sets the path where climate reconstructions will be stored. This +#' information is stored in a file names "pastclim_data.txt", which is found +#' in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +#' the default configuration directory for the package as set in R >= 4.0). +#' +#' @param path_to_nc the path to the file that contains the downloaded +#' resonstructions. If left unset, the default location returned by +#' `tools::R_user_dir("pastclim","data")` will be used +#' +#' @export + +set_data_path<-function(path_to_nc=NULL){ + # if we don't have a config directory, create one + if (!dir.exists(tools::R_user_dir("pastclim","config"))){ + dir.create(tools::R_user_dir("pastclim","config"), recursive = TRUE) + } + # use the default location + if (is.null(path_to_nc)){ + cat(tools::R_user_dir("pastclim","data"), "\n", + file = file.path(tools::R_user_dir("pastclim","config"), + "pastclim_data.txt")) + # and now create it if it does not exist yet + if (!dir.exists(tools::R_user_dir("pastclim","data"))){ + dir.create(tools::R_user_dir("pastclim","data"), recursive = TRUE) + } + # update option + options(pastclim.data_path = tools::R_user_dir("pastclim","data")) + } else { + # check that it exists + if (dir.exists(path_to_nc)){ + cat(path_to_nc, "\n",file = file.path(tools::R_user_dir("pastclim","config"), + "pastclim_data.txt")) + } else { + stop (path_to_nc," does not exist!") + } + # update option + options(pastclim.data_path = path_to_nc) + } + # move the example data into the new data path + copy_example_data() +} + + +#' Get the data path where climate reconstructions are stored +#' +#' This function returns the path where climate reconstructions will be stored. This +#' information is stored in a file names "pastclim_data.txt", which is found +#' in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +#' the default configuration directory for the package as set in R >= 4.0). +#' +#' If this function is run before any path was set, it calls `set_data_path`, +#' which defaults to storing data in the directory returned by +#' `tools::R_user_dir("pastclim","data")` +#' +#' @export + +get_data_path<-function(){ + # if the package already initiliased + if (!is.null(getOption("pastclim.data_path"))){ + return(getOption("pastclim.data_path")) + } else { # get the info from the config file + # if data path was never set before, we set it to its default location + if (!file.exists(file.path(tools::R_user_dir("pastclim","config"), + "pastclim_data.txt"))){ + set_data_path() + } + path_to_nc = utils::read.table(file.path(tools::R_user_dir("pastclim","config"), + "pastclim_data.txt"))[1,1] + return(path_to_nc) + } +} + +#' Internal function to copy the example dataset when a new data path is set +#' +#' Copy example dataset +#' +#' @keywords internal + +copy_example_data <-function() { + if (!file.exists(file.path(get_data_path(),"example_climate_nc"))){ + file.copy(from = system.file("/extdata/example_climate.nc", + package="pastclim"), + to= file.path(get_data_path(),"example_climate.nc")) + } +} \ No newline at end of file diff --git a/R/time_series_for_location.R b/R/time_series_for_location.R index 43a0a5a3..0ddc7919 100644 --- a/R/time_series_for_location.R +++ b/R/time_series_for_location.R @@ -27,7 +27,7 @@ time_series_for_locations <- } if (is.null(path_to_nc)) { - path_to_nc <- get_pastclimdata_path() + path_to_nc <- get_data_path() } # reorder the inputs by time diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 00000000..11e9c885 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,10 @@ +.onLoad <- function(libname, pkgname) { + op <- options() + op.pastclim <- list( + pastclim.data_path = get_data_path() + ) + toset <- !(names(op.pastclim) %in% names(op)) + if(any(toset)) options(op.pastclim[toset]) + + invisible() +} \ No newline at end of file diff --git a/inst/rawdata_scripts/verify_completeness_of_variables.R b/inst/rawdata_scripts/verify_completeness_of_variables.R index f025fc35..bfbb4cf3 100644 --- a/inst/rawdata_scripts/verify_completeness_of_variables.R +++ b/inst/rawdata_scripts/verify_completeness_of_variables.R @@ -1,7 +1,7 @@ # this script check that we have values for the same cells across all variables dataset <- "Krapp2021" library(pastclim) -this_path <- pastclim::get_pastclimdata_path() +this_path <- pastclim::get_data_path() vars_for_dataset <- pastclim:::get_file_for_dataset( get_vars_for_dataset(dataset), dataset) diff --git a/R/_delta_downscale.R b/inst/temp/_delta_downscale.R similarity index 100% rename from R/_delta_downscale.R rename to inst/temp/_delta_downscale.R diff --git a/man/get_data_path.Rd b/man/get_data_path.Rd new file mode 100644 index 00000000..65baf6d7 --- /dev/null +++ b/man/get_data_path.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/set_data_path.R +\name{get_data_path} +\alias{get_data_path} +\title{Get the data path where climate reconstructions are stored} +\usage{ +get_data_path() +} +\description{ +This function returns the path where climate reconstructions will be stored. This +information is stored in a file names "pastclim_data.txt", which is found +in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +the default configuration directory for the package as set in R >= 4.0). +} +\details{ +If this function is run before any path was set, it calls `set_data_path`, +which defaults to storing data in the directory returned by +`tools::R_user_dir("pastclim","data")` +} diff --git a/man/get_pastclimdata_path.Rd b/man/get_pastclimdata_path.Rd deleted file mode 100644 index 59438e1c..00000000 --- a/man/get_pastclimdata_path.Rd +++ /dev/null @@ -1,12 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_pastclimdata_path.R -\name{get_pastclimdata_path} -\alias{get_pastclimdata_path} -\title{Get the path for pastclimdata.} -\usage{ -get_pastclimdata_path() -} -\description{ -This function returns the path for pastclimData where reconstructions -are stored. -} diff --git a/man/get_time_steps.Rd b/man/get_time_steps.Rd index 7ef59203..9d5ddba8 100644 --- a/man/get_time_steps.Rd +++ b/man/get_time_steps.Rd @@ -8,7 +8,7 @@ get_time_steps(dataset = NULL, path_to_nc = NULL) } \arguments{ \item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020", "Krapp2021" and "Example" are available). Leave it unset +"Beyer2020", "Krapp2021" and "Example" are available). Leave it unset if using `path_to_nc`} \item{path_to_nc}{the path to the directory containing the downloaded diff --git a/man/set_data_path.Rd b/man/set_data_path.Rd new file mode 100644 index 00000000..0fd08c2c --- /dev/null +++ b/man/set_data_path.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/set_data_path.R +\name{set_data_path} +\alias{set_data_path} +\title{Set the data path where climate reconstructions will be stored} +\usage{ +set_data_path(path_to_nc = NULL) +} +\arguments{ +\item{path_to_nc}{the path to the file that contains the downloaded +resonstructions. If left unset, the default location returned by +`tools::R_user_dir("pastclim","data")` will be used} +} +\description{ +This function sets the path where climate reconstructions will be stored. This +information is stored in a file names "pastclim_data.txt", which is found +in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +the default configuration directory for the package as set in R >= 4.0). +} diff --git a/tests/testthat/test_get_pastclimdata_path.R b/tests/testthat/test_get_pastclimdata_path.R deleted file mode 100644 index 72076379..00000000 --- a/tests/testthat/test_get_pastclimdata_path.R +++ /dev/null @@ -1,12 +0,0 @@ -test_that("get_pastclimdata_path without pastclimData", { - skip_if(requireNamespace("pastclimData")) - expect_error( - get_pastclimdata_path(), - "^the parameter path_to_nc" - ) -}) - -test_that("get_pastclimdata_path with pastclimData", { - skip_if(!requireNamespace("pastclimData")) - expect_true(is.character(get_pastclimdata_path())) -}) diff --git a/tests/testthat/test_get_set_data_path.R b/tests/testthat/test_get_set_data_path.R new file mode 100644 index 00000000..e2834d5f --- /dev/null +++ b/tests/testthat/test_get_set_data_path.R @@ -0,0 +1,6 @@ +test_that("set and get data path", { + set_data_path() + expect_true(file.exists(file.path(tools::R_user_dir("pastclim","config"), + "pastclim_data.txt"))) + expect_equal(get_data_path(),tools::R_user_dir("pastclim","data")) +}) \ No newline at end of file diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index ef1e002b..e62aa0c5 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -34,9 +34,6 @@ And read it directly in R with: ```{r vignette, eval=FALSE} vignette("pastclim_overview",package="pastclim") ``` -The vignette also provides instructions on how to install the optional -companion package `pastclimData`, which simplifies the task of downloading and -storing the climate simulations. Depending on the operating system you use, you might need additional packages to build a vignette. --- @@ -53,65 +50,72 @@ of this issue can be found on [stack**overflow**](https://stackoverflow.com/ques # Download the data -You will need to download climatic reconstuctions before being able to do any -work with `pastclim`. The library focuses on two datasets: "Beyer2020" which covers the last 120k years; and, for project that go further back in time, "Krapp2021" which goes back to 800kya. For these to datasets, there are functions that help you download the data -and choose the variables. If you are familiar with handling `netcdf` files, you can also -use most functions in `pastclim` on custom datasets. +You will need to download climatic reconstructions before being able to do any +work with `pastclim`. Currently the library contains two datasets: +"Beyer2020" which covers the last 120k years; and, for project that go further back in time, "Krapp2021" which goes back to 800kya. For these two datasets, there are functions that help you download the data +and choose the variables. It is possible to add additional, custom datasets, but +you will need some familiarity with handling `netcdf` files (see the vignette on +'Formatting data to use in pastclim'). + +Datasets will stored in the package data path, which can be obtained with: -For Beyer2020 and Krapp2021, you can get a list -of available variables for each dataset with: ```{r} library(pastclim) -get_vars_for_dataset(dataset="Beyer2020") +get_data_path() ``` -and +If you prefer using a custom path (e.g. in "~/my_reconstructions"), it can be set with: -```{r} -get_vars_for_dataset(dataset="Krapp2021") +```{r eval=FALSE} +set_data_path(path_to_nc = "~/my_reconstructions") ``` -However, before being able to use these data you need to download the data files into -a directory of choice. Note that these data are large (from 100s of Mb to a few Gb). There is also a small -"Example" dataset that we will use in this vignette. This is included in the package when -you download it, and it is stored in the `/extdata` directory, which can be found in: +`pastclim` will remember this new path in the future, even after closing and restarting +R. + +The package includes a small dataset, *Example*, that we will use in this vignette; +the real datasets are large (from 100s of Mb to a few Gb), and you will need to specify +what you want to download (see below). +Let us start by inspecting the *Example* dataset. We can get a list of variables +available for this dataset with: ```{r} -path_to_example_nc <- system.file("/extdata/", package="pastclim") +get_vars_for_dataset(dataset="Example") ``` -For Beyer2020 and Krapp20221, `pastclim` manages the files necessary for each variable. So, we can download data for `bio01` and `bio05` for the Beyer2020 dataset with (this operation might take several minutes, as the datasets are large; `R` will pause until -the download is complete): +and the available time steps available can be obtained with: -```{r eval=FALSE} -download_dataset(dataset="Beyer2020", bio_variables = c("bio01","bio05"), - path_to_nc = "~/paleoclimate_reconstructions") +```{r} +get_time_steps(dataset="Example") ``` -Note that we need to provide `pastclim` with a local path where to store the reconstructions (in this case `~/paleoclimate_reconstructions`, but you probably want a different one on your machine). We will have to use that path again every time we use a command to manipulate the data. - -You can get a summary of variables already downloaded (and thus that can be used) -with: +For "Beyer2020" and "Krapp2021", you can get a list +of available variables for each dataset with: ```{r} -get_downloaded_datasets(path_to_nc = path_to_example_nc) +get_vars_for_dataset(dataset="Beyer2020") ``` -Note that for the example above, we used the directory containing the example dataset; use the path where you stored the data that you donwloaded. Note that multiple variables are packed together into a single file, so the command might list more variables than the ones that we downloaded originally. - -Having to set a path every time can be cumbersome; a simpler option to handle climate files is to install a companion package `pastclimData`: +and -```{r eval=FALSE} -devtools::install_github("EvolEcolGroup/pastclimData", ref="master") +```{r} +get_vars_for_dataset(dataset="Krapp2021") ``` -If `pastclimData` is installed, the paleoclimate reconstructions will be stored -within that package when you download them and they will then be found automatically by `pastclim` without the need to set a path). So, to download the Beyer2020 dataset, we -simply need: +You will not be able to get the available timesteps until you actual download the data. `pastclim` offers an interface +to download the necessary files into your data path. Let's download `bio01` and `bio05` for the *Beyer2020* dataset (this operation might take several minutes, as the datasets are large; `R` will pause until +the download is complete): + ```{r eval=FALSE} download_dataset(dataset="Beyer2020", bio_variables = c("bio01","bio05")) ``` -By not setting `path_to_nc` in the command above, the downloaded file will be stored in that companion library`pastclimData`. The downside of storing the data in `pastclimData` is that, if you reinstall or upgrade `R`, you will need to redownload the climate reconstructions. +You can get a summary of variables already downloaded (and thus that can be used) +with: +```{r} +get_downloaded_datasets() +``` + +Note that for the example above, we used the directory containing the example dataset; use the path where you stored the data that you downloaded. Note that multiple variables are packed together into a single file, so the command might list more variables than the ones that we downloaded originally. # Get climate for location @@ -123,23 +127,21 @@ locations<-data.frame(longitude=c(0,90,-120,-7),latitude=c(20,45,60,37), climate_for_locations (x= locations[,c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), - dataset="Example", nn_interpol = FALSE, path_to_nc = path_to_example_nc) + dataset="Example", nn_interpol = FALSE) ``` Note that the last two locations, for the appropriate time steps, are not available (either under water or ice), and so `pastclim` does not return a climate reconstruction. In some instances, this is due to the discretisation of space in the raster. We can interpolate climate among the nearest neighbours, thus using climate reconstructions for neighbouring pixels if the location is just off one or more land pixels: ```{r} climate_for_locations (x= locations[,c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), - dataset="Example", nn_interpol = TRUE, path_to_nc = path_to_example_nc) + dataset="Example", nn_interpol = TRUE) ``` In this case, the last location is indeed just off the coast, and so we can reconstruct some appropriate climate by interpolating. Note that `nn_interpol = TRUE` is the default for this function. -In both commands above, we used the local copy of the Example dataset. If `pastclimData` was installed, we could use the same commands without setting `path_to_nc`. - Get time series for these locations: ```{r} locations_ts <- time_series_for_locations(x= locations[,c("longitude", "latitude")], bio_variables=c("bio01","bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") ``` The resulting dataframe can be subsetted to get the time series for each location @@ -161,7 +163,7 @@ We can extract rasters for a given time step: ```{r} climate_20k <- climate_for_time_slice(time_bp = -20000, c("bio01","bio10","bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") ``` We can get a summary of this object: @@ -221,14 +223,14 @@ Note that the Eurasian outline is intersected by the antimeridian, and so we hav # Working with biomes and icesheets The climate reconstructions do not show areas under permanent ice. Ice sheets are stored as class 28 in the "biome" variable: ```{r} -get_biome_classes("Example", path_to_nc = path_to_example_nc) +get_biome_classes("Example") ``` To plot it, we extract the biome and then subset it to just that class ```{r, fig.width=6, fig.height=5} climate_20k <- climate_for_time_slice(-20000, c("bio01","bio10","biome"), - dataset = "Example", path_to_nc = path_to_example_nc) + dataset = "Example") climate_20k$ice<-climate_20k$biome climate_20k$ice[climate_20k$ice!=28]<-FALSE climate_20k$ice[climate_20k$ice==28]<-TRUE @@ -239,9 +241,9 @@ Or more simply, we use functions designed to get ice and land masks. ```{r, fig.width=6, fig.height=5} climate_20k <- climate_for_time_slice(-20000, c("bio01","bio10"), - dataset="Example", path_to_nc = path_to_example_nc) -climate_20k$ice_mask<-get_ice_mask(-20000, dataset="Example", path_to_nc = path_to_example_nc) -climate_20k$land_mask <- get_land_mask(-20000, dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") +climate_20k$ice_mask<-get_ice_mask(-20000, dataset="Example") +climate_20k$land_mask <- get_land_mask(-20000, dataset="Example") terra::plot(climate_20k) ``` @@ -251,7 +253,7 @@ We can visualise the environment for this time step with a PCA: ```{r, fig.width=4, fig.height=4} climate_10k <- climate_for_time_slice(-10000, c("bio01","bio10","bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") climate_values_10k <- values(climate_10k) climate_values_10k <- climate_values_10k[!is.nan(climate_values_10k[,1]),] climate_10k_pca<-prcomp(climate_values_10k,scale=TRUE, center=TRUE) @@ -264,7 +266,7 @@ We can now get the climate variables for the locations at this time step, comput locations_10k <- locations[locations$time_bp==-10000,] locations_10k_climate<- climate_for_locations (x= locations_10k[,c("longitude", "latitude")], time_bp = locations_10k$time_bp, bio_variables=c("bio01","bio10","bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") locations_10k_pca_scores <- predict(climate_10k_pca,newdata= locations_10k_climate[,-c(1:2)]) plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", xlab="PC1",ylab="PC2") @@ -276,7 +278,7 @@ points(locations_10k_pca_scores,pch=20,col="red") Sometimes we want to work with multiple time steps to represent a given MIS. We can get a list of time steps available for a given mis with: ```{r} -mis_time_steps <- get_mis_time_steps(1,"Example", path_to_nc = path_to_example_nc) +mis_time_steps <- get_mis_time_steps(1,"Example") mis_time_steps ``` @@ -285,7 +287,7 @@ And now cycle over those steps. First extract the climate into a list: mis_climate_list<-list() for (this_step in mis_time_steps){ this_step_climate <- climate_for_time_slice(this_step ,c("bio01","bio10","bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") this_step_climate <- values(this_step_climate) this_step_climate <- this_step_climate[!is.nan(this_step_climate[,1]),] mis_climate_list[[as.character(this_step)]]<- this_step_climate @@ -311,7 +313,7 @@ For a number of species distribution models, we need to subsample background poi ```{r} climate_20k <- climate_for_time_slice(-20000,c("bio01","bio10"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") this_sample<-terra::spatSample(climate_20k, 100,na.rm=TRUE,cells=TRUE,xy=TRUE) head(this_sample) ``` @@ -322,7 +324,7 @@ additional_var <- climate_for_locations ( x= data.frame(longitude = this_sample$x, latitude = this_sample$y), time_bp = rep(-20000,nrow(this_sample)), bio_variables=c("bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") head(additional_var) ``` @@ -331,6 +333,6 @@ Alternatively, we could use the cell number: additional_var <- climate_for_locations (x= this_sample$cell, time_bp = rep(-20000,nrow(this_sample)), bio_variables=c("bio12"), - dataset="Example", path_to_nc = path_to_example_nc) + dataset="Example") head(additional_var) ``` From c7b23f42a801a20228c00c9abdc351149dec76c3 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Wed, 17 Aug 2022 22:04:00 +0100 Subject: [PATCH 06/83] minor edits [skip ci] --- R/time_bp_to_index.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/time_bp_to_index.R b/R/time_bp_to_index.R index c3e6b66e..d2ac468c 100644 --- a/R/time_bp_to_index.R +++ b/R/time_bp_to_index.R @@ -1,10 +1,11 @@ -#' Convert a time BP to indeces in a netcdf file. +#' Convert a time BP to indexes in a netcdf file. #' #' Internal function #' #' @param time_bp vector of times BP #' @param path_to_nc path to nc file #' +#' @keywords internal time_bp_to_index <- function(time_bp, path_to_nc) { climate_nc <- ncdf4::nc_open(path_to_nc) From 66d7835f9a34a4e677a6d2bf4f9b1a793e26ace7 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Thu, 18 Aug 2022 16:51:42 +0100 Subject: [PATCH 07/83] Implement region_series and infrastructure The infrastructure should be reusable for other functions. [skip ci] --- NAMESPACE | 3 + R/check_dataset_path.R | 29 ++++++++ R/check_var_in_nc.R | 6 +- R/climate_for_location.R | 33 +++++++--- R/download_dataset.R | 18 +---- R/get_available_datasets.R | 13 ++-- R/region_series.R | 73 +++++++++++++++++++++ R/slice_region_series.R | 11 ++++ man/check_available_dataset.Rd | 4 +- man/check_dataset_path.Rd | 18 +++++ man/climate_for_locations.Rd | 35 ++-------- man/copy_example_data.Rd | 12 ++++ man/download_dataset.Rd | 6 +- man/location_slice.Rd | 40 +++++++++++ man/region_series.Rd | 33 ++++++++++ man/time_bp_to_index.Rd | 3 +- tests/testthat/test_check_dataset_path.R | 18 +++++ tests/testthat/test_climate_for_slice.R | 33 ---------- tests/testthat/test_download_dataset.R | 7 +- tests/testthat/test_get_available_dataset.R | 6 ++ tests/testthat/test_region_series.R | 32 +++++++++ 21 files changed, 327 insertions(+), 106 deletions(-) create mode 100644 R/check_dataset_path.R create mode 100644 R/region_series.R create mode 100644 R/slice_region_series.R create mode 100644 man/check_dataset_path.Rd create mode 100644 man/copy_example_data.Rd create mode 100644 man/location_slice.Rd create mode 100644 man/region_series.Rd create mode 100644 tests/testthat/test_check_dataset_path.R delete mode 100644 tests/testthat/test_climate_for_slice.R create mode 100644 tests/testthat/test_region_series.R diff --git a/NAMESPACE b/NAMESPACE index c33afdd5..4c41627b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(check_dataset_path) export(climate_for_locations) export(climate_for_time_slice) export(download_dataset) @@ -12,6 +13,8 @@ export(get_land_mask) export(get_mis_time_steps) export(get_time_steps) export(get_vars_for_dataset) +export(location_slice) +export(region_series) export(set_data_path) export(time_series_for_locations) import(terra) diff --git a/R/check_dataset_path.R b/R/check_dataset_path.R new file mode 100644 index 00000000..dc94020a --- /dev/null +++ b/R/check_dataset_path.R @@ -0,0 +1,29 @@ +#' Check dataset and path_to_nc params. +#' +#' Check that the dataset and path_to_nc parameters are valid +#' +#' @param dataset string defining the dataset to use. If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. +#' +#' @export + + +check_dataset_path <- function(dataset, path_to_nc){ + check_available_dataset(dataset = dataset, include_custom = TRUE) + + if (all(dataset=="custom",is.null(path_to_nc))){ + stop("you need to set path_to_nc if dataset='custom'") + } + # check that we are only given path_to_nc if we use a custom dataset + if (!is.null(path_to_nc)){ + if (dataset!="custom"){ + stop("path_to_nc can only be set if dataset=='custom'") + } + if (!file.exists(path_to_nc)){ + stop("path_to_nc does not point to a file") + } + } + return(TRUE) +} \ No newline at end of file diff --git a/R/check_var_in_nc.R b/R/check_var_in_nc.R index 5aebc279..219542e1 100644 --- a/R/check_var_in_nc.R +++ b/R/check_var_in_nc.R @@ -3,9 +3,9 @@ #' Internal function to test a custom nc file. #' #' @param bio_variables vector of names of variables to be extracted -#' @param path_to_nc the path to the file that contains the downloaded -#' resonstructions. -#' +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. +#' #' @keywords internal check_var_in_nc <- function(bio_variables, path_to_nc) { diff --git a/R/climate_for_location.R b/R/climate_for_location.R index 51bd58c3..35b5c6b9 100644 --- a/R/climate_for_location.R +++ b/R/climate_for_location.R @@ -1,18 +1,17 @@ -#' Extract local climate for one or more locations. +#' Extract local climate for one or more locations for a given time slice. #' -#' This function extract local climate from Beyer et al for a set of locations -#' at the appropriate times +#' This function extract local climate for a set of locations +#' at the appropriate times (selecting the closest time slice available for the +#' specific date associated with each location). #' #' @param x a 2 column matrix (with columns `longitude`, ranging #' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. #' @param time_bp vector of ages, in years before present (negative). #' @param bio_variables vector of names of variables to be extracted. #' @param dataset string defining the dataset to use (one of Beyer2020, -#' Krapp2021 or custom). -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. -#' Leave it unset if you are using the companion `pastclimData` to store -#' datasets. +#' Krapp2021, Example or custom). +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. #' @param nn_interpol boolean determining whether nearest neighbour #' interpolation is used to estimate climate for cells that lack such #' information (i.e. they are under water or ice). Interpolation is only @@ -21,7 +20,7 @@ #' #' @export -climate_for_locations <- +location_slice <- function(x, time_bp, bio_variables, @@ -120,3 +119,19 @@ climate_for_locations <- return(locations_data) } + + +#' Extract local climate for one or more locations for a given time slice. +#' +#' Deprecated version of \code{location_slice} +#' +#' @param ... arguments to be passed to \code{location_slice} +#' +#' @export + +climate_for_locations <-function(...){ + warning("DEPRECATED: use 'location_slice' instead") + location_slice(...) +} + + diff --git a/R/download_dataset.R b/R/download_dataset.R index f7b9945e..80b304c6 100644 --- a/R/download_dataset.R +++ b/R/download_dataset.R @@ -6,22 +6,10 @@ #' "Beyer" is available) #' @param bio_variables one or more variable names to be downloaded. If left #' to NULL, all variables available for this dataset will be downloaded -#' @param path_to_nc directory where the files will be saved. If not set, the -#' data will be downloaded into the storage package pastclimData; an error -#' will be returned if this package is not installed. #' #' @export -download_dataset <- function(dataset, bio_variables = NULL, path_to_nc = NULL) { - if (is.null(path_to_nc)) { - path_to_nc <- system.file("extdata", package = "pastclimData") - if (path_to_nc == "") { - stop( - "the parameter path was not set, and the package pastclimData", - "is not installed." - ) - } - } +download_dataset <- function(dataset, bio_variables = NULL) { # check the dataset exists available_datasets <- unique(files_by_dataset$dataset) @@ -62,9 +50,9 @@ download_dataset <- function(dataset, bio_variables = NULL, path_to_nc = NULL) { for (this_var in bio_variables) { file_details <- get_file_for_dataset(this_var, dataset) # only download the file if it is needed - if (!file.exists(file.path(path_to_nc, file_details$file_name))) { + if (!file.exists(file.path(get_data_path(), file_details$file_name))) { curl::curl_download(file_details$download_path, - destfile = file.path(path_to_nc, file_details$file_name), + destfile = file.path(get_data_path(), file_details$file_name), quiet = FALSE ) } diff --git a/R/get_available_datasets.R b/R/get_available_datasets.R index 9008903d..b460d5d6 100644 --- a/R/get_available_datasets.R +++ b/R/get_available_datasets.R @@ -5,7 +5,7 @@ #' @export get_available_datasets <- function() { - return(unique(files_by_dataset$dataset)) + return(unique(as.character(files_by_dataset$dataset))) } @@ -14,13 +14,18 @@ get_available_datasets <- function() { #' Internal getter function #' #' @param dataset string defining dataset +#' @param include_custom boolean whether a 'custom' dataset is allowed #' #' @keywords internal -check_available_dataset <- function(dataset) { - if (!dataset %in% get_available_datasets()) { - stop("'dataset' must be one of ", paste(get_available_datasets(), +check_available_dataset <- function(dataset, include_custom=FALSE) { + available_datasets <- get_available_datasets() + if (include_custom){ + available_datasets <- c(available_datasets, "custom") + } + if (!dataset %in% available_datasets) { + stop("'dataset' must be one of ", paste (available_datasets, collapse = ", " )) } else { diff --git a/R/region_series.R b/R/region_series.R new file mode 100644 index 00000000..2aea26b6 --- /dev/null +++ b/R/region_series.R @@ -0,0 +1,73 @@ +#' Extract a time series of climate variables for a region +#' +#' This function extracts a time series of one or more climate variables for a given +#' dataset covering a region (or the whole world). The function returns a +#' SpatRasterDataset \code{terra::sds} object, with +#' each variable as a sub-dataset. +#' +#' @param time_bp time slices in years before present (negative). The slices needs +#' to exist in the dataset. To check which slices are available, you can use +#' \code{get_time_steps}. +#' @param bio_variables vector of names of variables to be extracted +#' @param dataset string defining the dataset to use. If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. +#' +#' location_slice +#' location_series +#' region_slice +#' region_series +#' slice_region_series +#' +#' @import terra +#' @export + +region_series <- + function(time_bp, + bio_variables, + dataset, + path_to_nc = NULL) { + + check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) + + # check whether the variables exist for this dataset + if (dataset != "custom") { # if we are using standard datasets + check_var_downloaded(bio_variables, dataset, get_data_path()) + } else { # else check that the variables exist in the custom nc + check_var_in_nc(bio_variables, path_to_nc) + } + + time_index <- NULL + climate_spatrasters <- list() + + for (this_var in bio_variables) { + # get name of file that contains this variable + if (dataset != "custom") { + this_file <- get_file_for_dataset(this_var, dataset)$file_name + this_file <- file.path(get_data_path(), this_file) + this_var_nc <- get_varname(variable = this_var, dataset = dataset) + } else { + this_file <- file.path(path_to_nc) + this_var_nc <- this_var + } + # figure out the time indeces the first time we run this + if (is.null(time_index)) { + time_index <- time_bp_to_index( + time_bp = time_bp, path_to_nc = + this_file + ) + } + var_brick <- terra::rast(this_file, subds = this_var_nc) + climate_spatrasters[[this_var]] <- terra::subset(var_brick, subset = time_index) + # if (is.null(climate_spatraster)) { + # climate_spatraster <- var_slice + # } else { + # terra::add(climate_spatraster) <- var_slice + # } + } + #names(climate_spatraster) <- varnames(climate_spatraster) <- bio_variables + return(terra::sds(climate_spatrasters)) + } + + diff --git a/R/slice_region_series.R b/R/slice_region_series.R new file mode 100644 index 00000000..2d7493bc --- /dev/null +++ b/R/slice_region_series.R @@ -0,0 +1,11 @@ +slice_region_series <- function(x, time_bp){ + # check that time_bp is part of the series + + # now slice it and convert it to a Spatraster + + # + + # + + +} \ No newline at end of file diff --git a/man/check_available_dataset.Rd b/man/check_available_dataset.Rd index a8b832bb..016d76b4 100644 --- a/man/check_available_dataset.Rd +++ b/man/check_available_dataset.Rd @@ -4,10 +4,12 @@ \alias{check_available_dataset} \title{Check if dataset is available.} \usage{ -check_available_dataset(dataset) +check_available_dataset(dataset, include_custom = FALSE) } \arguments{ \item{dataset}{string defining dataset} + +\item{include_custom}{boolean whether a 'custom' dataset is allowed} } \description{ Internal getter function diff --git a/man/check_dataset_path.Rd b/man/check_dataset_path.Rd new file mode 100644 index 00000000..8aacd693 --- /dev/null +++ b/man/check_dataset_path.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check_dataset_path.R +\name{check_dataset_path} +\alias{check_dataset_path} +\title{Check dataset and path_to_nc params.} +\usage{ +check_dataset_path(dataset, path_to_nc) +} +\arguments{ +\item{dataset}{string defining the dataset to use. If set to "custom", +then a single nc file is used from "path_to_nc"} + +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in this file.} +} +\description{ +Check that the dataset and path_to_nc parameters are valid +} diff --git a/man/climate_for_locations.Rd b/man/climate_for_locations.Rd index c0e2af0a..fff8dbc0 100644 --- a/man/climate_for_locations.Rd +++ b/man/climate_for_locations.Rd @@ -2,40 +2,13 @@ % Please edit documentation in R/climate_for_location.R \name{climate_for_locations} \alias{climate_for_locations} -\title{Extract local climate for one or more locations.} +\title{Extract local climate for one or more locations for a given time slice.} \usage{ -climate_for_locations( - x, - time_bp, - bio_variables, - dataset, - path_to_nc = NULL, - nn_interpol = TRUE -) +climate_for_locations(...) } \arguments{ -\item{x}{a 2 column matrix (with columns `longitude`, ranging --180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} - -\item{time_bp}{vector of ages, in years before present (negative).} - -\item{bio_variables}{vector of names of variables to be extracted.} - -\item{dataset}{string defining the dataset to use (one of Beyer2020, -Krapp2021 or custom).} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. -Leave it unset if you are using the companion `pastclimData` to store -datasets.} - -\item{nn_interpol}{boolean determining whether nearest neighbour -interpolation is used to estimate climate for cells that lack such -information (i.e. they are under water or ice). Interpolation is only -performed from the first ring of nearest neighbours; if climate is not -available, NA will be returned for that location. Defaults to TRUE.} +\item{...}{arguments to be passed to \code{location_slice}} } \description{ -This function extract local climate from Beyer et al for a set of locations -at the appropriate times +Deprecated version of \code{location_slice} } diff --git a/man/copy_example_data.Rd b/man/copy_example_data.Rd new file mode 100644 index 00000000..0acecaa1 --- /dev/null +++ b/man/copy_example_data.Rd @@ -0,0 +1,12 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/set_data_path.R +\name{copy_example_data} +\alias{copy_example_data} +\title{Internal function to copy the example dataset when a new data path is set} +\usage{ +copy_example_data() +} +\description{ +Copy example dataset +} +\keyword{internal} diff --git a/man/download_dataset.Rd b/man/download_dataset.Rd index 7f580720..71ac787b 100644 --- a/man/download_dataset.Rd +++ b/man/download_dataset.Rd @@ -4,7 +4,7 @@ \alias{download_dataset} \title{Download paeloclimate reconstructions.} \usage{ -download_dataset(dataset, bio_variables = NULL, path_to_nc = NULL) +download_dataset(dataset, bio_variables = NULL) } \arguments{ \item{dataset}{string defining dataset to be downloaded (currently only @@ -12,10 +12,6 @@ download_dataset(dataset, bio_variables = NULL, path_to_nc = NULL) \item{bio_variables}{one or more variable names to be downloaded. If left to NULL, all variables available for this dataset will be downloaded} - -\item{path_to_nc}{directory where the files will be saved. If not set, the -data will be downloaded into the storage package pastclimData; an error -will be returned if this package is not installed.} } \description{ This function downloads paleoclimate reconstructions diff --git a/man/location_slice.Rd b/man/location_slice.Rd new file mode 100644 index 00000000..fb51c76b --- /dev/null +++ b/man/location_slice.Rd @@ -0,0 +1,40 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/climate_for_location.R +\name{location_slice} +\alias{location_slice} +\title{Extract local climate for one or more locations for a given time slice.} +\usage{ +location_slice( + x, + time_bp, + bio_variables, + dataset, + path_to_nc = NULL, + nn_interpol = TRUE +) +} +\arguments{ +\item{x}{a 2 column matrix (with columns `longitude`, ranging +-180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} + +\item{time_bp}{vector of ages, in years before present (negative).} + +\item{bio_variables}{vector of names of variables to be extracted.} + +\item{dataset}{string defining the dataset to use (one of Beyer2020, +Krapp2021, Example or custom).} + +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in this file.} + +\item{nn_interpol}{boolean determining whether nearest neighbour +interpolation is used to estimate climate for cells that lack such +information (i.e. they are under water or ice). Interpolation is only +performed from the first ring of nearest neighbours; if climate is not +available, NA will be returned for that location. Defaults to TRUE.} +} +\description{ +This function extract local climate for a set of locations +at the appropriate times (selecting the closest time slice available for the +specific date associated with each location). +} diff --git a/man/region_series.Rd b/man/region_series.Rd new file mode 100644 index 00000000..9844c306 --- /dev/null +++ b/man/region_series.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/region_series.R +\name{region_series} +\alias{region_series} +\title{Extract a time series of climate variables for a region} +\usage{ +region_series(time_bp, bio_variables, dataset, path_to_nc = NULL) +} +\arguments{ +\item{time_bp}{time slices in years before present (negative). The slices needs +to exist in the dataset. To check which slices are available, you can use +\code{get_time_steps}.} + +\item{bio_variables}{vector of names of variables to be extracted} + +\item{dataset}{string defining the dataset to use. If set to "custom", +then a single nc file is used from "path_to_nc"} + +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in this file. + +location_slice +location_series +region_slice +region_series +slice_region_series} +} +\description{ +This function extracts a time series of one or more climate variables for a given +dataset covering a region (or the whole world). The function returns a +SpatRasterDataset \code{terra::sds} object, with +each variable as a sub-dataset. +} diff --git a/man/time_bp_to_index.Rd b/man/time_bp_to_index.Rd index 56a0bf22..29feb18d 100644 --- a/man/time_bp_to_index.Rd +++ b/man/time_bp_to_index.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/time_bp_to_index.R \name{time_bp_to_index} \alias{time_bp_to_index} -\title{Convert a time BP to indeces in a netcdf file.} +\title{Convert a time BP to indexes in a netcdf file.} \usage{ time_bp_to_index(time_bp, path_to_nc) } @@ -14,3 +14,4 @@ time_bp_to_index(time_bp, path_to_nc) \description{ Internal function } +\keyword{internal} diff --git a/tests/testthat/test_check_dataset_path.R b/tests/testthat/test_check_dataset_path.R new file mode 100644 index 00000000..1d240d81 --- /dev/null +++ b/tests/testthat/test_check_dataset_path.R @@ -0,0 +1,18 @@ +test_that("check_dataset_path errors", + { + expect_true(check_dataset_path("Example", NULL)) + expect_error(check_dataset_path("foo", NULL), + "'dataset' must be one of ") + expect_error(check_dataset_path("custom", NULL), + "you need to set path_to_nc if dataset='custom'") + expect_true(check_dataset_path("custom", + file.path(get_data_path(), + "example_climate.nc"))) + expect_error(check_dataset_path("custom", + file.path(get_data_path(), + "foo.nc")), + "path_to_nc does not point to a file") + + + }) + \ No newline at end of file diff --git a/tests/testthat/test_climate_for_slice.R b/tests/testthat/test_climate_for_slice.R deleted file mode 100644 index 55415ac2..00000000 --- a/tests/testthat/test_climate_for_slice.R +++ /dev/null @@ -1,33 +0,0 @@ -test_that("climate_for_time_slice", { - # using standard dataset - path_to_example_nc <- system.file("/extdata/", package = "pastclim") - expect_true(inherits(climate_for_time_slice(-20000, c("bio01", "bio12"), - "Example", - path_to_nc = path_to_example_nc - ), "SpatRaster")) - # if we try to use a variable that does not exist - expect_error(climate_for_time_slice(-20000, c("bio01", "bio19"), "Example", - path_to_nc = path_to_example_nc - ), "bio19 not available") - # if we try to use a variable that we have not downloaded yet - expect_error(climate_for_time_slice(-20000, c("bio01", "bio19"), "Krapp2021", - path_to_nc = path_to_example_nc - ), "^variable \\(bio01, bio19\\) not yet downloaded") - - # now treat it as if it was a custom dataset - path_to_example_nc <- system.file("/extdata/example_climate.nc", - package = "pastclim" - ) - expect_true(inherits(climate_for_time_slice(-20000, c("BIO1", "BIO12"), - "custom", - path_to_nc = path_to_example_nc - ), "SpatRaster")) - # if we try to use a variable that does not exist - expect_error(climate_for_time_slice(-20000, c("BIO01", "lai"), "custom", - path_to_nc = path_to_example_nc - ), "variable \\(BIO01, lai\\) not present in the file") - # if we try to use a file that does not exist - expect_error(climate_for_time_slice(-20000, c("BIO1", "BIO12"), "custom", - path_to_nc = "./foo" - ), "file ./foo does not exist") -}) diff --git a/tests/testthat/test_download_dataset.R b/tests/testthat/test_download_dataset.R index 51e12910..ca762992 100644 --- a/tests/testthat/test_download_dataset.R +++ b/tests/testthat/test_download_dataset.R @@ -1,20 +1,19 @@ test_that("download_dataset", { path_to_example_nc <- system.file("/extdata/", package = "pastclim") expect_error( - download_dataset("Beyer", path_to_nc = path_to_example_nc), + download_dataset("Beyer"), "'dataset' must be one of Beyer2020, Krapp2021, Example" ) expect_error( download_dataset("Beyer2020", - bio_variables = "foo", - path_to_nc = path_to_example_nc + bio_variables = "foo" ), "^foo not available " ) # check that only the example climate is in the data directory expect_true(list.files(path_to_example_nc) == "example_climate.nc") # expect no error as the dataset exists - expect_error(download_dataset("Example", path_to_nc = path_to_example_nc), NA) + expect_error(download_dataset("Example"), NA) # but we should not have downloaded anything, as we already have the file expect_true(list.files(path_to_example_nc) == "example_climate.nc") }) diff --git a/tests/testthat/test_get_available_dataset.R b/tests/testthat/test_get_available_dataset.R index 212918f8..1db37dc6 100644 --- a/tests/testthat/test_get_available_dataset.R +++ b/tests/testthat/test_get_available_dataset.R @@ -9,4 +9,10 @@ testthat::test_that("get_and_check_available_datasets", { check_available_dataset("foo"), "'dataset' must be one of Beyer2020, Krapp2021, Example" ) + testthat::expect_true(check_available_dataset("custom", include_custom = TRUE)) + testthat::expect_error( + check_available_dataset("custom"), + "'dataset' must be one of Beyer2020, Krapp2021, Example" + ) }) + diff --git a/tests/testthat/test_region_series.R b/tests/testthat/test_region_series.R new file mode 100644 index 00000000..16c9c1a3 --- /dev/null +++ b/tests/testthat/test_region_series.R @@ -0,0 +1,32 @@ +test_that("climate_for_time_slice", { + # using standard dataset + path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") + + climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), + "Example") + expect_true(inherits(climate_region, "SpatRasterDataset")) + expect_true(all(names(climate_region)==c("bio01", "bio12"))) + expect_true(all(terra::nlyr(climate_region)==c(2,2))) + # do the same for a custom dataset + climate_region <- region_series(c(-20000,-10000), c("BIO1", "BIO10"), + "custom",path_to_nc = path_to_example_nc) + expect_true(inherits(climate_region, "SpatRasterDataset")) + expect_true(all(names(climate_region)==c("BIO1", "BIO10"))) + expect_true(all(terra::nlyr(climate_region)==c(2,2))) + + # if we try to use a variable that does not exist + expect_error(region_series(c(-20000,-10000), c("bio01", "bio19"), "Example"), + "bio19 not available") + expect_error(region_series(c(-20000,-10000), c("BIO1", "bio19"),"custom", + path_to_nc = path_to_example_nc), + "variable \\(bio19\\) not") + + # if we try to use a variable that we have not downloaded yet + expect_error(region_series(c(-20000,-10000), c("bio01", "bio19"), "Krapp2021" + ), "^variable \\(bio01, bio19\\) not yet downloaded") + + # if we try to use a file that does not exist + expect_error(region_series(c(-20000,-10000), c("BIO1", "BIO12"), "custom", + path_to_nc = "./foo" + ), "path_to_nc does not point to a file") +}) From 709340379ec69e4f794afc6c1b5df6bd3c40c153 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Thu, 18 Aug 2022 17:12:30 +0100 Subject: [PATCH 08/83] slice a time series [skip ci] --- NAMESPACE | 1 + R/region_series.R | 2 ++ R/slice_region_series.R | 33 ++++++++++++++++++++------ tests/testthat/test_download_dataset.R | 5 ++-- tests/testthat/test_region_series.R | 2 +- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 4c41627b..52a21d90 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -16,5 +16,6 @@ export(get_vars_for_dataset) export(location_slice) export(region_series) export(set_data_path) +export(slice_region_series) export(time_series_for_locations) import(terra) diff --git a/R/region_series.R b/R/region_series.R index 2aea26b6..cd6d5eb4 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -66,6 +66,8 @@ region_series <- # terra::add(climate_spatraster) <- var_slice # } } + climate_sds<-terra::sds(climate_spatrasters) + names(climate_sds)<-bio_variables #names(climate_spatraster) <- varnames(climate_spatraster) <- bio_variables return(terra::sds(climate_spatrasters)) } diff --git a/R/slice_region_series.R b/R/slice_region_series.R index 2d7493bc..507431f1 100644 --- a/R/slice_region_series.R +++ b/R/slice_region_series.R @@ -1,11 +1,30 @@ +#' Extract a slice for a time series of climate variables for a region +#' +#' This function extracts a time slice from time series of one or more climate +#' variables for a given dataset covering a region (or the whole world). +#' +#' @param x time series generated with \code{region_series} +#' @param time_bp time slices in years before present (negative). The slices needs +#' to exist in the dataset. To check which slices are available, you can use +#' \code{terra::time(x[[1]])} +#' +#' @export + slice_region_series <- function(x, time_bp){ # check that time_bp is part of the series - + if (!time_bp %in% time(x[[1]])){ + stop("time_bp is not a time slice within the region series x") + } + # get index + time_index <- which(time(x[[1]])==time_bp) # now slice it and convert it to a Spatraster - - # - - # - - + for (i in 1:length(x)){ + if (i==1){ + climate_spatraster <- subset(x[[i]],time_index) + } else { + terra::add(climate_spatraster) <- subset(x[[i]],time_index) + } + } + names(climate_spatraster) <- varnames(climate_spatraster) <- names(x) + return(climate_spatraster) } \ No newline at end of file diff --git a/tests/testthat/test_download_dataset.R b/tests/testthat/test_download_dataset.R index ca762992..76058c6b 100644 --- a/tests/testthat/test_download_dataset.R +++ b/tests/testthat/test_download_dataset.R @@ -1,5 +1,4 @@ test_that("download_dataset", { - path_to_example_nc <- system.file("/extdata/", package = "pastclim") expect_error( download_dataset("Beyer"), "'dataset' must be one of Beyer2020, Krapp2021, Example" @@ -11,9 +10,9 @@ test_that("download_dataset", { "^foo not available " ) # check that only the example climate is in the data directory - expect_true(list.files(path_to_example_nc) == "example_climate.nc") + expect_true(list.files(get_data_path()) == "example_climate.nc") # expect no error as the dataset exists expect_error(download_dataset("Example"), NA) # but we should not have downloaded anything, as we already have the file - expect_true(list.files(path_to_example_nc) == "example_climate.nc") + expect_true(list.files(get_data_path()) == "example_climate.nc") }) diff --git a/tests/testthat/test_region_series.R b/tests/testthat/test_region_series.R index 16c9c1a3..4ef96da8 100644 --- a/tests/testthat/test_region_series.R +++ b/tests/testthat/test_region_series.R @@ -1,6 +1,5 @@ test_that("climate_for_time_slice", { # using standard dataset - path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), "Example") @@ -8,6 +7,7 @@ test_that("climate_for_time_slice", { expect_true(all(names(climate_region)==c("bio01", "bio12"))) expect_true(all(terra::nlyr(climate_region)==c(2,2))) # do the same for a custom dataset + path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") climate_region <- region_series(c(-20000,-10000), c("BIO1", "BIO10"), "custom",path_to_nc = path_to_example_nc) expect_true(inherits(climate_region, "SpatRasterDataset")) From 0bd36d9c0b224b119d8aa942eebe76adb8afbd81 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Thu, 18 Aug 2022 17:14:01 +0100 Subject: [PATCH 09/83] man page update [skip ci] --- man/check_var_in_nc.Rd | 4 ++-- man/slice_region_series.Rd | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 man/slice_region_series.Rd diff --git a/man/check_var_in_nc.Rd b/man/check_var_in_nc.Rd index ac6aeccb..69fc7c07 100644 --- a/man/check_var_in_nc.Rd +++ b/man/check_var_in_nc.Rd @@ -9,8 +9,8 @@ check_var_in_nc(bio_variables, path_to_nc) \arguments{ \item{bio_variables}{vector of names of variables to be extracted} -\item{path_to_nc}{the path to the file that contains the downloaded -resonstructions.} +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions.} } \description{ Internal function to test a custom nc file. diff --git a/man/slice_region_series.Rd b/man/slice_region_series.Rd new file mode 100644 index 00000000..d1667918 --- /dev/null +++ b/man/slice_region_series.Rd @@ -0,0 +1,19 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/slice_region_series.R +\name{slice_region_series} +\alias{slice_region_series} +\title{Extract a slice for a time series of climate variables for a region} +\usage{ +slice_region_series(x, time_bp) +} +\arguments{ +\item{x}{time series generated with \code{region_series}} + +\item{time_bp}{time slices in years before present (negative). The slices needs +to exist in the dataset. To check which slices are available, you can use +\code{terra::time(x[[1]])}} +} +\description{ +This function extracts a time slice from time series of one or more climate +variables for a given dataset covering a region (or the whole world). +} From a5f4bacef7f6d3f07db63a4cf8ad58cdf0ddd434 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 08:28:28 +0100 Subject: [PATCH 10/83] Fix handling of custom datasets for helper functions --- R/download_dataset.R | 9 ++++++--- R/get_biome_classes.R | 18 +++++++----------- R/get_file_for_dataset.R | 7 ++++--- R/get_ice_mask.R | 13 +++++-------- R/get_land_mask.R | 13 +++++-------- R/get_mis_time_steps.R | 12 ++++++------ R/get_time_steps.R | 22 ++++++++-------------- R/get_vars_for_dataset.R | 5 +++-- man/download_dataset.Rd | 9 ++++++--- man/get_biome_classes.Rd | 11 ++++------- man/get_file_for_dataset.Rd | 5 +++-- man/get_ice_mask.Rd | 12 ++++-------- man/get_land_mask.Rd | 12 ++++-------- man/get_mis_time_steps.Rd | 12 ++++++------ man/get_time_steps.Rd | 13 ++++++------- man/get_vars_for_dataset.Rd | 5 +++-- tests/testthat/test_get_biome_classes.R | 10 +--------- tests/testthat/test_get_mis_time_steps.R | 11 ++++------- tests/testthat/test_get_time_steps.R | 16 ++++++++++------ 19 files changed, 95 insertions(+), 120 deletions(-) diff --git a/R/download_dataset.R b/R/download_dataset.R index 80b304c6..792ca430 100644 --- a/R/download_dataset.R +++ b/R/download_dataset.R @@ -1,9 +1,12 @@ #' Download paeloclimate reconstructions. #' -#' This function downloads paleoclimate reconstructions +#' This function downloads paleoclimate reconstructions. Files will be stored +#' in the data path of `pastclim`, which can be inspected with +#' \code{get_data_path} and changed with \code{set_data_path} #' -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer" is available) +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. #' @param bio_variables one or more variable names to be downloaded. If left #' to NULL, all variables available for this dataset will be downloaded #' diff --git a/R/get_biome_classes.R b/R/get_biome_classes.R index 0da03063..acb53128 100644 --- a/R/get_biome_classes.R +++ b/R/get_biome_classes.R @@ -3,23 +3,19 @@ #' Get a full list of biomes and how their id as coded in the biome variable #' for a given dataset. #' -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020", "Krapp2021" or "Example" are available) -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. #' #' @export -get_biome_classes <- function(dataset, path_to_nc = NULL) { - if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() - } +get_biome_classes <- function(dataset) { + data_path <- get_data_path() # test that we ahve the biome variable - check_var_downloaded("biome", dataset, path_to_nc = path_to_nc) + check_var_downloaded("biome", dataset) # get file name for biome this_file <- get_file_for_dataset("biome", dataset)$file_name - this_file <- file.path(path_to_nc, this_file) + this_file <- file.path(data_path, this_file) nc_in <- ncdf4::nc_open(this_file) biome_attributes <- ncdf4::ncatt_get(nc_in, "biome") # format the table diff --git a/R/get_file_for_dataset.R b/R/get_file_for_dataset.R index dba3f8cc..fc749b9a 100644 --- a/R/get_file_for_dataset.R +++ b/R/get_file_for_dataset.R @@ -1,10 +1,11 @@ -get #' Get the file details for a variable and dataset. +#' Get the file details for a variable and dataset. #' #' Internal getter function #' #' @param variable one or more variable names to be downloaded -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer" or "Krapp" are available) +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. get_file_for_dataset <- function(variable, dataset) { check_available_variable(variable, dataset) diff --git a/R/get_ice_mask.R b/R/get_ice_mask.R index fd0903de..7157756a 100644 --- a/R/get_ice_mask.R +++ b/R/get_ice_mask.R @@ -3,20 +3,17 @@ #' Get the ice mask for a dataset at a given timepoint. #' #' @param time_bp time slice in years before present (negative) -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020" or "Krapp2021" are available). Note that this function will -#' not work on an custom dataset. -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. #' #' @import terra #' @export -get_ice_mask <- function(time_bp, dataset, path_to_nc = NULL) { +get_ice_mask <- function(time_bp, dataset) { climate_slice <- climate_for_time_slice( time_bp = time_bp, bio_variables = "biome", - dataset = dataset, path_to_nc = path_to_nc + dataset = dataset ) climate_slice$ice_mask <- climate_slice[names(climate_slice)] climate_slice$ice_mask[climate_slice$ice_mask != 28] <- NA diff --git a/R/get_land_mask.R b/R/get_land_mask.R index 056adac4..a8289027 100644 --- a/R/get_land_mask.R +++ b/R/get_land_mask.R @@ -3,20 +3,17 @@ #' Get the land mask for a dataset at a given timepoint. #' #' @param time_bp time slice in years before present (negative) -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020" or "Krapp2021" are available). Note that this function will -#' not work on an custom dataset. -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. #' #' @import terra #' @export -get_land_mask <- function(time_bp, dataset, path_to_nc = NULL) { +get_land_mask <- function(time_bp, dataset) { climate_slice <- climate_for_time_slice( time_bp = time_bp, bio_variables = "biome", - dataset = dataset, path_to_nc = path_to_nc + dataset = dataset ) climate_slice$land_mask <- climate_slice[names(climate_slice)] climate_slice$land_mask[climate_slice$land_mask != 28] <- TRUE diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index 951ef8ee..d3b65cc4 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -4,15 +4,15 @@ #' #' @param mis string giving the mis; it must use the same spelling as used in #' /code{mis_boundaries} -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020" or "Krapp2021" are available) -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. #' #' @export -get_mis_time_steps <- function(mis, dataset = NULL, path_to_nc = NULL) { +get_mis_time_steps <- function(mis, dataset, path_to_nc = NULL) { if (!mis %in% mis_boundaries$mis) { stop("'mis' should be one of ", paste(mis_boundaries$mis, collapse = ",")) } diff --git a/R/get_time_steps.R b/R/get_time_steps.R index 24f02237..f9b1b7ee 100644 --- a/R/get_time_steps.R +++ b/R/get_time_steps.R @@ -2,28 +2,22 @@ #' #' Get the time steps available in a given dataset. #' -#' @param dataset string defining dataset to be downloaded (currently only -#' "Beyer2020", "Krapp2021" and "Example" are available). Leave it unset -#' if using `path_to_nc` -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. #' #' @export -get_time_steps <- function(dataset = NULL, path_to_nc = NULL) { - if (all(is.null(dataset), is.null(path_to_nc))) { - stop("Either 'dataset' or 'path_to_nc' needs to be given") - } else if (!any(is.null(dataset), is.null(path_to_nc))) { - stop("Only 'dataset' or 'path_to_nc' can be given") - } +get_time_steps <- function(dataset, path_to_nc = NULL) { + check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() # we get the first available file to get info for the dataset possible_vars <- get_vars_for_dataset(dataset) this_file <- get_file_for_dataset(possible_vars[1], dataset)$file_name - path_to_nc <- file.path(path_to_nc, this_file) + path_to_nc <- file.path(get_data_path(), this_file) } climate_nc <- ncdf4::nc_open(path_to_nc) diff --git a/R/get_vars_for_dataset.R b/R/get_vars_for_dataset.R index f83cf212..646f0747 100644 --- a/R/get_vars_for_dataset.R +++ b/R/get_vars_for_dataset.R @@ -5,8 +5,9 @@ #' publications, as `pastclim` harmonises the names of variables across #' different reconstructions. #' -#' @param dataset string defining dataset to for which variables are given. -#' It can take the value "Beyer2020", "Krapp2021" or "Example" +#' @param dataset string defining dataset to be downloaded (a list of possible +#' values can be obtained with \code{get_available_datasets}). This function +#' will not work on custom datasets. #' #' @export diff --git a/man/download_dataset.Rd b/man/download_dataset.Rd index 71ac787b..cb5c2a4c 100644 --- a/man/download_dataset.Rd +++ b/man/download_dataset.Rd @@ -7,12 +7,15 @@ download_dataset(dataset, bio_variables = NULL) } \arguments{ -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer" is available)} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} \item{bio_variables}{one or more variable names to be downloaded. If left to NULL, all variables available for this dataset will be downloaded} } \description{ -This function downloads paleoclimate reconstructions +This function downloads paleoclimate reconstructions. Files will be stored +in the data path of `pastclim`, which can be inspected with +\code{get_data_path} and changed with \code{set_data_path} } diff --git a/man/get_biome_classes.Rd b/man/get_biome_classes.Rd index f70bd97b..b97cb397 100644 --- a/man/get_biome_classes.Rd +++ b/man/get_biome_classes.Rd @@ -4,15 +4,12 @@ \alias{get_biome_classes} \title{Get the biome classes for a dataset.} \usage{ -get_biome_classes(dataset, path_to_nc = NULL) +get_biome_classes(dataset) } \arguments{ -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020", "Krapp2021" or "Example" are available)} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} } \description{ Get a full list of biomes and how their id as coded in the biome variable diff --git a/man/get_file_for_dataset.Rd b/man/get_file_for_dataset.Rd index 94834ebd..9a216d8f 100644 --- a/man/get_file_for_dataset.Rd +++ b/man/get_file_for_dataset.Rd @@ -9,8 +9,9 @@ get_file_for_dataset(variable, dataset) \arguments{ \item{variable}{one or more variable names to be downloaded} -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer" or "Krapp" are available)} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} } \description{ Internal getter function diff --git a/man/get_ice_mask.Rd b/man/get_ice_mask.Rd index 71931cbd..9a1acc29 100644 --- a/man/get_ice_mask.Rd +++ b/man/get_ice_mask.Rd @@ -4,18 +4,14 @@ \alias{get_ice_mask} \title{Get the ice mask for a dataset.} \usage{ -get_ice_mask(time_bp, dataset, path_to_nc = NULL) +get_ice_mask(time_bp, dataset) } \arguments{ \item{time_bp}{time slice in years before present (negative)} -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available). Note that this function will -not work on an custom dataset.} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} } \description{ Get the ice mask for a dataset at a given timepoint. diff --git a/man/get_land_mask.Rd b/man/get_land_mask.Rd index b7344ff8..c12f2af1 100644 --- a/man/get_land_mask.Rd +++ b/man/get_land_mask.Rd @@ -4,18 +4,14 @@ \alias{get_land_mask} \title{Get the land mask for a dataset.} \usage{ -get_land_mask(time_bp, dataset, path_to_nc = NULL) +get_land_mask(time_bp, dataset) } \arguments{ \item{time_bp}{time slice in years before present (negative)} -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available). Note that this function will -not work on an custom dataset.} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} } \description{ Get the land mask for a dataset at a given timepoint. diff --git a/man/get_mis_time_steps.Rd b/man/get_mis_time_steps.Rd index b4a92290..b1885755 100644 --- a/man/get_mis_time_steps.Rd +++ b/man/get_mis_time_steps.Rd @@ -4,18 +4,18 @@ \alias{get_mis_time_steps} \title{Get time steps for a given MIS} \usage{ -get_mis_time_steps(mis, dataset = NULL, path_to_nc = NULL) +get_mis_time_steps(mis, dataset, path_to_nc = NULL) } \arguments{ \item{mis}{string giving the mis; it must use the same spelling as used in /code{mis_boundaries}} -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020" or "Krapp2021" are available)} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). If set to "custom", +then a single nc file is used from "path_to_nc"} -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in this file.} } \description{ Get the time steps available in a given dataset for a MIS. diff --git a/man/get_time_steps.Rd b/man/get_time_steps.Rd index 9d5ddba8..5f0166e9 100644 --- a/man/get_time_steps.Rd +++ b/man/get_time_steps.Rd @@ -4,16 +4,15 @@ \alias{get_time_steps} \title{Get time steps for a given dataset} \usage{ -get_time_steps(dataset = NULL, path_to_nc = NULL) +get_time_steps(dataset, path_to_nc = NULL) } \arguments{ -\item{dataset}{string defining dataset to be downloaded (currently only -"Beyer2020", "Krapp2021" and "Example" are available). Leave it unset -if using `path_to_nc`} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). If set to "custom", +then a single nc file is used from "path_to_nc"} -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in this file.} } \description{ Get the time steps available in a given dataset. diff --git a/man/get_vars_for_dataset.Rd b/man/get_vars_for_dataset.Rd index ca2eb788..31479ca3 100644 --- a/man/get_vars_for_dataset.Rd +++ b/man/get_vars_for_dataset.Rd @@ -7,8 +7,9 @@ get_vars_for_dataset(dataset) } \arguments{ -\item{dataset}{string defining dataset to for which variables are given. -It can take the value "Beyer2020", "Krapp2021" or "Example"} +\item{dataset}{string defining dataset to be downloaded (a list of possible +values can be obtained with \code{get_available_datasets}). This function +will not work on custom datasets.} } \description{ This function lists the variables available for a given dataset. Note that diff --git a/tests/testthat/test_get_biome_classes.R b/tests/testthat/test_get_biome_classes.R index 93b872e5..3d8f503b 100644 --- a/tests/testthat/test_get_biome_classes.R +++ b/tests/testthat/test_get_biome_classes.R @@ -1,13 +1,5 @@ testthat::test_that("get biome classes", { - path_to_example_nc <- system.file("/extdata/", package = "pastclim") # check that we have 29 classes as expected - expect_true(nrow(get_biome_classes("Example", - path_to_nc = path_to_example_nc + expect_true(nrow(get_biome_classes("Example" )) == 29) - # give error if we do the same but give the wrong path - expect_error( - get_biome_classes("Example", - path_to_nc = "/foo" - ), "^variable" - ) }) diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R index e8bc1b5d..4acb0978 100644 --- a/tests/testthat/test_get_mis_time_steps.R +++ b/tests/testthat/test_get_mis_time_steps.R @@ -10,7 +10,8 @@ test_that("get_mis_time_steps for local file", { path_to_example_nc <- system.file("/extdata/", "example_climate.nc", package = "pastclim" ) - expect_equal(get_mis_time_steps(mis = "2", path_to_nc = path_to_example_nc), + expect_equal(get_mis_time_steps(mis = "2", dataset = "custom", + path_to_nc = path_to_example_nc), c(-20000, -15000), ignore_attr = TRUE ) @@ -18,12 +19,8 @@ test_that("get_mis_time_steps for local file", { test_that("get_mis_time_steps requires correct variables", { expect_error( - get_mis_time_steps(mis = "blah", path_to_nc = path_to_example_nc), + get_mis_time_steps(mis = "blah", dataset = "custom", + path_to_nc = path_to_example_nc), "'mis' should be one of" ) - expect_error(get_mis_time_steps(mis = 2), "Either 'dataset' or ") - expect_error( - get_mis_time_steps(mis = 2, dataset = "blah", path_to_nc = "blah"), - "Only 'dataset' or " - ) }) diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R index 3412635d..fd7d03b7 100644 --- a/tests/testthat/test_get_time_steps.R +++ b/tests/testthat/test_get_time_steps.R @@ -1,13 +1,15 @@ test_that("get_time_steps requires correct variables", { - expect_error(get_time_steps(), "Either 'dataset' or ") expect_error( - get_time_steps(dataset = "blah", path_to_nc = "blah"), - "Only 'dataset' or " + get_time_steps(dataset = "blah"), + "'dataset' must be one of " + ) + expect_error( + get_time_steps(dataset = "Example", path_to_nc = "blah"), + "path_to_nc can only be set if" ) }) test_that("get_time_steps for pastclimData", { - skip_if(!requireNamespace("pastclimData")) expect_equal(get_time_steps(dataset = "Example"), c(-20000, -15000, -10000, -5000, 0), ignore_attr = TRUE @@ -18,11 +20,13 @@ test_that("get_time_steps for local file", { path_to_example_nc <- system.file("/extdata/", "example_climate.nc", package = "pastclim" ) - expect_equal(get_time_steps(path_to_nc = path_to_example_nc), + expect_equal(get_time_steps(dataset = "custom", + path_to_nc = path_to_example_nc), c(-20000, -15000, -10000, -5000, 0), ignore_attr = TRUE ) - expect_equal(get_mis_time_steps(mis = "2", path_to_nc = path_to_example_nc), + expect_equal(get_mis_time_steps(mis = "2", dataset = "custom", + path_to_nc = path_to_example_nc), c(-20000, -15000), ignore_attr = TRUE ) From 5aa7322bf259ebfe2bf645e0059c3da90ce36752 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 09:53:22 +0100 Subject: [PATCH 11/83] Implement region_slice [skip_ci] --- R/region_series.R | 23 ++++++++------- R/region_slice.R | 35 +++++++++++++++++++++++ R/slice_region_series.R | 9 ++++-- README.md | 7 ++--- tests/testthat/test_get_mis_time_steps.R | 3 +- tests/testthat/test_get_time_steps.R | 2 +- tests/testthat/test_region_series.R | 9 ++++-- tests/testthat/test_region_slice.R | 20 +++++++++++++ tests/testthat/test_slice_region_series.R | 15 ++++++++++ 9 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 R/region_slice.R create mode 100644 tests/testthat/test_region_slice.R create mode 100644 tests/testthat/test_slice_region_series.R diff --git a/R/region_series.R b/R/region_series.R index cd6d5eb4..dde9a4d5 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -5,7 +5,8 @@ #' SpatRasterDataset \code{terra::sds} object, with #' each variable as a sub-dataset. #' -#' @param time_bp time slices in years before present (negative). The slices needs +#' @param time_bp time slices in years before present (negative values represent +#' time before present, positive values time in the future). The slices need #' to exist in the dataset. To check which slices are available, you can use #' \code{get_time_steps}. #' @param bio_variables vector of names of variables to be extracted @@ -53,18 +54,20 @@ region_series <- } # figure out the time indeces the first time we run this if (is.null(time_index)) { - time_index <- time_bp_to_index( - time_bp = time_bp, path_to_nc = - this_file - ) + # as we have the file name, we can us the same code for custom and + # standard datasets. + times <- get_time_steps(dataset="custom", path_to_nc = this_file) + time_index <- match(time_bp,times) + if (any(is.na(time_index))){ + stop("time_bp should only include time steps available in the dataset") + } } var_brick <- terra::rast(this_file, subds = this_var_nc) climate_spatrasters[[this_var]] <- terra::subset(var_brick, subset = time_index) - # if (is.null(climate_spatraster)) { - # climate_spatraster <- var_slice - # } else { - # terra::add(climate_spatraster) <- var_slice - # } + varnames(climate_spatrasters[[this_var]]) <- this_var + names(climate_spatrasters[[this_var]]) <- paste(this_var, + time(climate_spatrasters[[this_var]]), + sep="_") } climate_sds<-terra::sds(climate_spatrasters) names(climate_sds)<-bio_variables diff --git a/R/region_slice.R b/R/region_slice.R new file mode 100644 index 00000000..586a52ee --- /dev/null +++ b/R/region_slice.R @@ -0,0 +1,35 @@ +#' Extract a climate slice for a region +#' +#' This function extracts a slice of one or more climate variables for a given +#' dataset covering a region (or the whole world). The function returns a +#' SpatRaster \code{terra::SpatRaster} object, with +#' each variable as a layer. +#' +#' @param time_bp the time slice in years before present (negative values represent +#' time before present, positive values time in the future). The slice needs +#' to exist in the dataset. To check which slices are available, you can use +#' \code{get_time_steps}. +#' @param bio_variables vector of names of variables to be extracted +#' @param dataset string defining the dataset to use. If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in this file. +#' +#' @import terra +#' @export + +region_slice <- + function(time_bp, + bio_variables, + dataset, + path_to_nc = NULL) { + + this_series <- region_series(time_bp = time_bp, + bio_variables = bio_variables, + dataset = dataset, + path_to_nc = path_to_nc) + return(slice_region_series(x = this_series, time_bp = time_bp)) + + } + + diff --git a/R/slice_region_series.R b/R/slice_region_series.R index 507431f1..9168ec96 100644 --- a/R/slice_region_series.R +++ b/R/slice_region_series.R @@ -6,11 +6,16 @@ #' @param x time series generated with \code{region_series} #' @param time_bp time slices in years before present (negative). The slices needs #' to exist in the dataset. To check which slices are available, you can use -#' \code{terra::time(x[[1]])} +#' \code{terra::time(x[[1]])} (note that you have to use the `time` fuction +#' on the first element of the 'sds' object, i.e. on one of the 'spatRaster' +#' objects) #' #' @export slice_region_series <- function(x, time_bp){ + if (length(time_bp)!=1){ + stop("time_bp should be a single time step") + } # check that time_bp is part of the series if (!time_bp %in% time(x[[1]])){ stop("time_bp is not a time slice within the region series x") @@ -25,6 +30,6 @@ slice_region_series <- function(x, time_bp){ terra::add(climate_spatraster) <- subset(x[[i]],time_index) } } - names(climate_spatraster) <- varnames(climate_spatraster) <- names(x) + # names(climate_spatraster) <- varnames(climate_spatraster) #<- names(x) return(climate_spatraster) } \ No newline at end of file diff --git a/README.md b/README.md index f2efec05..9a920b8e 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,10 @@ devtools::install_github("EvolEcolGroup/pastclim", build_vignette = TRUE) ``` And read it directly in R with: ``` -vignette("pastclim_overview",package="pastclim") +vignette("pastclim_overview", package = "pastclim") ``` -The vignette also provides instructions on how to install the optional -companion package `pastclimData`, which simplifies the task of downloading and -storing the climate simulations. +There is also a vignette on how to format custom datasets (and ideally, how to +make a pull request to include them officially in `pastclim` for everyone to use!) --- diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R index 4acb0978..8986bbb8 100644 --- a/tests/testthat/test_get_mis_time_steps.R +++ b/tests/testthat/test_get_mis_time_steps.R @@ -1,5 +1,4 @@ -test_that("get_mis_time_steps for pastclimData", { - skip_if(!requireNamespace("pastclimData")) +test_that("get_mis_time_steps for standard dataset", { expect_equal(get_mis_time_steps(mis = "2", dataset = "Example"), c(-20000, -15000), ignore_attr = TRUE diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R index fd7d03b7..3f4886fb 100644 --- a/tests/testthat/test_get_time_steps.R +++ b/tests/testthat/test_get_time_steps.R @@ -9,7 +9,7 @@ test_that("get_time_steps requires correct variables", { ) }) -test_that("get_time_steps for pastclimData", { +test_that("get_time_steps for standard dataset", { expect_equal(get_time_steps(dataset = "Example"), c(-20000, -15000, -10000, -5000, 0), ignore_attr = TRUE diff --git a/tests/testthat/test_region_series.R b/tests/testthat/test_region_series.R index 4ef96da8..6555effc 100644 --- a/tests/testthat/test_region_series.R +++ b/tests/testthat/test_region_series.R @@ -1,11 +1,11 @@ -test_that("climate_for_time_slice", { +test_that("region series", { # using standard dataset - climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), "Example") expect_true(inherits(climate_region, "SpatRasterDataset")) expect_true(all(names(climate_region)==c("bio01", "bio12"))) expect_true(all(terra::nlyr(climate_region)==c(2,2))) + # do the same for a custom dataset path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") climate_region <- region_series(c(-20000,-10000), c("BIO1", "BIO10"), @@ -29,4 +29,9 @@ test_that("climate_for_time_slice", { expect_error(region_series(c(-20000,-10000), c("BIO1", "BIO12"), "custom", path_to_nc = "./foo" ), "path_to_nc does not point to a file") + + # if we use time steps that do not exist + expect_error(region_series(c(-19000,-10000), c("bio01", "bio12"), + "Example"), + "time_bp should only include time steps available in the dataset") }) diff --git a/tests/testthat/test_region_slice.R b/tests/testthat/test_region_slice.R new file mode 100644 index 00000000..36054901 --- /dev/null +++ b/tests/testthat/test_region_slice.R @@ -0,0 +1,20 @@ +test_that("region slice", { + # using standard dataset + climate_slice <- region_slice(c(-10000), c("bio01", "bio12"), + "Example") + expect_true(inherits(climate_slice, "SpatRaster")) + expect_true(all(varnames(climate_slice)==c("bio01", "bio12"))) + expect_true(terra::nlyr(climate_slice)==c(2)) + + # do the same for a custom dataset + path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") + climate_slice <- region_slice(c(-10000), c("BIO1", "BIO10"), + "custom",path_to_nc = path_to_example_nc) + expect_true(inherits(climate_slice, "SpatRaster")) + expect_true(all(varnames(climate_slice)==c("BIO1", "BIO10"))) + expect_true(terra::nlyr(climate_slice)==c(2)) + + expect_error(region_slice(c(-10000, -20000), c("bio01", "bio12"), + "Example"), + "time_bp should be a single time step") +}) diff --git a/tests/testthat/test_slice_region_series.R b/tests/testthat/test_slice_region_series.R new file mode 100644 index 00000000..9af91537 --- /dev/null +++ b/tests/testthat/test_slice_region_series.R @@ -0,0 +1,15 @@ +test_that("climate_for_time_slice", { + climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), + "Example") + my_slice <- slice_region_series(climate_region, time_bp = -10000) + expect_true(inherits(my_slice, "SpatRaster")) + expect_true(length(my_slice)==1) + expect_true(terra::nlyr(my_slice)==2) + expect_true(all(varnames(my_slice)==c("bio01", "bio12"))) + # use a time step that does not exist + expect_error(slice_region_series(climate_region, time_bp = -19000), + "time_bp is not a time slice within the region series x") + # use too many timesteps + expect_error(slice_region_series(climate_region, time_bp = c(-10000,-20000)), + "time_bp should be a single time step") +}) From 9649133feb4b082de550c0d099b539b2fb4d6be6 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 10:28:22 +0100 Subject: [PATCH 12/83] More fixes for datapath [skip ci] --- R/climate_for_time_slice.R | 62 ----------- R/get_downloaded_datasets.R | 20 ++-- ...limate_for_location.R => location_slice.R} | 20 ++-- R/region_series.R | 2 +- R/region_slice.R | 15 +++ ...ocation.R => _test_climate_for_location.R} | 0 tests/testthat/test_get_downloaded_datasets.R | 11 +- tests/testthat/test_location_slice.R | 101 ++++++++++++++++++ 8 files changed, 141 insertions(+), 90 deletions(-) delete mode 100644 R/climate_for_time_slice.R rename R/{climate_for_location.R => location_slice.R} (88%) rename tests/testthat/{test_climate_for_location.R => _test_climate_for_location.R} (100%) create mode 100644 tests/testthat/test_location_slice.R diff --git a/R/climate_for_time_slice.R b/R/climate_for_time_slice.R deleted file mode 100644 index 2baa84bc..00000000 --- a/R/climate_for_time_slice.R +++ /dev/null @@ -1,62 +0,0 @@ -#' Extract a raster brick for a given time slice -#' -#' This function extracts a raster brick for a given time slice. -#' -#' @param time_bp time slice in years before present (negative) -#' @param bio_variables vector of names of variables to be extracted -#' @param dataset string defining the dataset to use -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion `pastclimData` -#' to store datasets. -#' -#' @import terra -#' @export - -climate_for_time_slice <- - function(time_bp, - bio_variables, - dataset, - path_to_nc = NULL) { - - # if we are using standard datasets, check whether a variables exists - if (dataset != "custom") { - check_var_downloaded(bio_variables, dataset, path_to_nc) - } else { # else check that the variables exist in the custom nc - check_var_in_nc(bio_variables, path_to_nc) - } - - # if path_to_nc is not set, use pastclimData - if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() - } - - time_index <- NULL - climate_spatraster <- NULL - - for (this_var in bio_variables) { - # get name of file that contains this variable - if (dataset != "custom") { - this_file <- get_file_for_dataset(this_var, dataset)$file_name - this_file <- file.path(path_to_nc, this_file) - this_var_nc <- get_varname(variable = this_var, dataset = dataset) - } else { - this_file <- file.path(path_to_nc) - this_var_nc <- this_var - } - if (is.null(time_index)) { - time_index <- time_bp_to_index( - time_bp = time_bp, path_to_nc = - this_file - ) - } - var_brick <- terra::rast(this_file, subds = this_var_nc) - var_slice <- terra::subset(var_brick, subset = time_index) - if (is.null(climate_spatraster)) { - climate_spatraster <- var_slice - } else { - terra::add(climate_spatraster) <- var_slice - } - } - names(climate_spatraster) <- varnames(climate_spatraster) <- bio_variables - return(climate_spatraster) - } diff --git a/R/get_downloaded_datasets.R b/R/get_downloaded_datasets.R index 1ab96854..46f001e6 100644 --- a/R/get_downloaded_datasets.R +++ b/R/get_downloaded_datasets.R @@ -2,15 +2,15 @@ #' #' List the downloaded variable for each dataset. #' -#' @param path_to_nc path to the netcdf datasets +#' @param data_path leave it to NULL to use the default datapath #' #' @export -get_downloaded_datasets <- function(path_to_nc = NULL) { - if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() +get_downloaded_datasets <- function(data_path=NULL) { + if (is.null(data_path)){ + data_path <- get_data_path() } - all_nc_files <- list.files(path_to_nc) + all_nc_files <- list.files(data_path) files_subset <- files_by_dataset[files_by_dataset$file_name %in% all_nc_files, ] downloaded_vars <- list() @@ -28,21 +28,17 @@ get_downloaded_datasets <- function(path_to_nc = NULL) { #' #' @param variable a vector of names of the variables of interest #' @param dataset dataset of interest -#' @param path_to_nc path to the netcdf datasets #' #' @keywords internal -check_var_downloaded <- function(variable, dataset, path_to_nc = NULL) { +check_var_downloaded <- function(variable, dataset) { # first check the variable exists for that dataset check_available_variable(variable, dataset) # test if we have downloaded already - if (!all(variable %in% get_downloaded_datasets(path_to_nc = path_to_nc) + if (!all(variable %in% get_downloaded_datasets() [[dataset]])) { - missing_vars <- variable[!variable %in% get_downloaded_datasets( - path_to_nc = - path_to_nc - )[[dataset]]] + missing_vars <- variable[!variable %in% get_downloaded_datasets()[[dataset]]] stop( "variable (", paste(missing_vars, collapse = ", "), ") not yet downloaded, use `download_dataset()`" diff --git a/R/climate_for_location.R b/R/location_slice.R similarity index 88% rename from R/climate_for_location.R rename to R/location_slice.R index 35b5c6b9..7ff105b8 100644 --- a/R/climate_for_location.R +++ b/R/location_slice.R @@ -6,8 +6,9 @@ #' #' @param x a 2 column matrix (with columns `longitude`, ranging #' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. -#' @param time_bp vector of ages, in years before present (negative). -#' @param bio_variables vector of names of variables to be extracted. +#' @param time_bp the time slice in years before present (negative values represent +#' time before present, positive values time in the future). +#' #' @param bio_variables vector of names of variables to be extracted. #' @param dataset string defining the dataset to use (one of Beyer2020, #' Krapp2021, Example or custom). #' @param path_to_nc the path to the custom nc file containing the paleoclimate @@ -27,18 +28,16 @@ location_slice <- dataset, path_to_nc = NULL, nn_interpol = TRUE) { + + check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) # if we are using standard datasets, check whether a variables exists if (dataset != "custom") { - check_var_downloaded(bio_variables, dataset, path_to_nc) + check_var_downloaded(bio_variables, dataset) } else { # else check that the variables exist in the custom nc check_var_in_nc(bio_variables, path_to_nc) } - if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() - } - # reorder the inputs by time if (inherits(x, "data.frame")) { x <- x[order(time_bp), ] @@ -56,10 +55,10 @@ location_slice <- # get name of file that contains this variable if (dataset != "custom") { this_file <- get_file_for_dataset(this_var, dataset)$file_name - this_file <- file.path(path_to_nc, this_file) + this_file <- file.path(get_data_path(), this_file) this_var_nc <- get_varname(variable = this_var, dataset = dataset) } else { - this_file <- file.path(path_to_nc) + this_file <- path_to_nc this_var_nc <- this_var } if (is.null(time_indeces)) { @@ -131,6 +130,9 @@ location_slice <- climate_for_locations <-function(...){ warning("DEPRECATED: use 'location_slice' instead") + if (!is.null(path_to_nc)){ + stop("the use of pastclimData is now deprecated, use 'set_path_data' instead") + } location_slice(...) } diff --git a/R/region_series.R b/R/region_series.R index dde9a4d5..ef500e0c 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -34,7 +34,7 @@ region_series <- # check whether the variables exist for this dataset if (dataset != "custom") { # if we are using standard datasets - check_var_downloaded(bio_variables, dataset, get_data_path()) + check_var_downloaded(bio_variables, dataset) } else { # else check that the variables exist in the custom nc check_var_in_nc(bio_variables, path_to_nc) } diff --git a/R/region_slice.R b/R/region_slice.R index 586a52ee..ca3b4de3 100644 --- a/R/region_slice.R +++ b/R/region_slice.R @@ -33,3 +33,18 @@ region_slice <- } +#' Extract a climate slice for a region +#' +#' Deprecated version of \code{region_slice} +#' +#' @param ... arguments to be passed to \code{region_slice} +#' +#' @export + +climate_for_time_slice <-function(...){ + warning("DEPRECATED: use 'region_slice' instead") + if (!is.null(path_to_nc)){ + stop("the use of pastclimData is now deprecated, use 'set_path_data' instead") + } + region_slice(...) +} diff --git a/tests/testthat/test_climate_for_location.R b/tests/testthat/_test_climate_for_location.R similarity index 100% rename from tests/testthat/test_climate_for_location.R rename to tests/testthat/_test_climate_for_location.R diff --git a/tests/testthat/test_get_downloaded_datasets.R b/tests/testthat/test_get_downloaded_datasets.R index 2e0a1ade..3cbde066 100644 --- a/tests/testthat/test_get_downloaded_datasets.R +++ b/tests/testthat/test_get_downloaded_datasets.R @@ -1,21 +1,20 @@ testthat::test_that("get_downloaded_datasets", { path_to_example_nc <- system.file("/extdata/", package = "pastclim") # there is only the Example dataset available - expect_true(length(get_downloaded_datasets(path_to_nc = path_to_example_nc)) + expect_true(length(get_downloaded_datasets(data_path = path_to_example_nc)) == 1) # return an empty list if there are no files - expect_true(length(get_downloaded_datasets(path_to_nc = "./foo")) == 0) + expect_true(length(get_downloaded_datasets(data_path = "./foo")) == 0) # check that we have downloaded a variable - expect_true(check_var_downloaded("bio01", "Example", - path_to_nc = path_to_example_nc + expect_true(check_var_downloaded("bio01", "Example" )) # raise error if the variable is not available expect_error( - check_var_downloaded("npp", "Example", path_to_nc = path_to_example_nc), + check_var_downloaded("npp", "Example"), "^npp not available" ) expect_error( - check_var_downloaded("npp", "Krapp2021", path_to_nc = path_to_example_nc), + check_var_downloaded("npp", "Krapp2021"), "^variable" ) }) diff --git a/tests/testthat/test_location_slice.R b/tests/testthat/test_location_slice.R new file mode 100644 index 00000000..0352386e --- /dev/null +++ b/tests/testthat/test_location_slice.R @@ -0,0 +1,101 @@ +test_that("location_slice", { + # using standard dataset + locations <- data.frame( + longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), + time_bp = c(0, -10000, -20000, -10000) + ) + + this_climate <- location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = FALSE) + expect_false(is.na(this_climate[1, "bio01"])) + expect_true(is.na(this_climate[3, "bio01"])) + expect_true(is.na(this_climate[4, "bio01"])) + # test nn_interpolation + this_climate <- location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = TRUE + ) + expect_false(is.na(this_climate[1, "bio01"])) + expect_true(is.na(this_climate[3, "bio01"])) + expect_false(is.na(this_climate[4, "bio01"])) + + # now test if we try a variable that is not available + expect_error( + location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio19"), + dataset = "Example", nn_interpol = FALSE + ), + "bio19 not available" + ) + expect_error( + location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio19", "bio20"), + dataset = "Example", nn_interpol = FALSE + ), + "bio19, bio20 not available" + ) + + # now test if we try to use a variable that we have not downloaded yet + expect_error( + location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Krapp2021", nn_interpol = FALSE + ), + "variable \\(bio01, bio12\\) not yet downloaded" + ) + + # now test a custom dataset + path_to_example_nc <- system.file("/extdata/example_climate.nc", + package = "pastclim" + ) + this_climate <- location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO12"), + dataset = "custom", nn_interpol = FALSE, path_to_nc = path_to_example_nc + ) + expect_false(is.na(this_climate[1, "BIO1"])) + expect_true(is.na(this_climate[3, "BIO1"])) + expect_true(is.na(this_climate[4, "BIO1"])) + # if we try to use a variable that does not exist + expect_error( + location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO22"), + dataset = "custom", nn_interpol = FALSE, path_to_nc = path_to_example_nc + ), + "variable \\(BIO22\\) not present in the file" + ) + # if we try to use a file that does not exist + expect_error( + location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO22"), + dataset = "custom", nn_interpol = FALSE, path_to_nc = "/foo" + ), + "path_to_nc does not point to a file" + ) + + # now use times which are not the exact timesteps + locations_timeoff <- data.frame( + longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), + time_bp = c(0, -9750, -20375, -10475) + ) + this_climate_timeoff <- location_slice( + x = locations_timeoff[, c("longitude", "latitude")], + time_bp = locations_timeoff$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = TRUE + ) + # and compare it to the estimates from exact times (they should be the same!) + this_climate <- location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = TRUE + ) + expect_true(identical(this_climate[,-c(1:3)], this_climate_timeoff[,-c(1:3)])) +}) From 0ca3b4d9406e26b8bf3577bf5102a7c6d88733de Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 11:07:10 +0100 Subject: [PATCH 13/83] linting [skip ci] --- R/check_dataset_path.R | 17 ++-- R/check_var_in_nc.R | 2 +- R/get_available_datasets.R | 6 +- R/get_downloaded_datasets.R | 7 +- R/get_mis_time_steps.R | 7 +- R/get_time_steps.R | 7 +- R/location_slice.R | 29 +++--- R/region_series.R | 38 ++++---- R/region_slice.R | 29 +++--- R/set_data_path.R | 90 +++++++++++-------- R/slice_region_series.R | 34 +++---- R/time_bp_to_index.R | 7 +- R/zzz.R | 6 +- tests/testthat/_test_climate_for_location.R | 86 ------------------ tests/testthat/test_check_dataset_path.R | 46 ++++++---- tests/testthat/test_get_available_dataset.R | 6 +- tests/testthat/test_get_biome_classes.R | 3 +- tests/testthat/test_get_downloaded_datasets.R | 3 +- tests/testthat/test_get_mis_time_steps.R | 16 ++-- tests/testthat/test_get_set_data_path.R | 10 ++- tests/testthat/test_get_time_steps.R | 20 +++-- tests/testthat/test_location_slice.R | 10 ++- tests/testthat/test_region_series.R | 64 ++++++++----- tests/testthat/test_region_slice.R | 35 +++++--- tests/testthat/test_slice_region_series.R | 24 +++-- vignettes/pastclim_overview.Rmd | 59 +++++++----- 26 files changed, 328 insertions(+), 333 deletions(-) delete mode 100644 tests/testthat/_test_climate_for_location.R diff --git a/R/check_dataset_path.R b/R/check_dataset_path.R index dc94020a..c27b22f0 100644 --- a/R/check_dataset_path.R +++ b/R/check_dataset_path.R @@ -1,29 +1,30 @@ #' Check dataset and path_to_nc params. #' #' Check that the dataset and path_to_nc parameters are valid -#' +#' #' @param dataset string defining the dataset to use. If set to "custom", #' then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. +#' reconstructions. All the variables of interest need to be included in +#' this file. #' #' @export -check_dataset_path <- function(dataset, path_to_nc){ +check_dataset_path <- function(dataset, path_to_nc) { check_available_dataset(dataset = dataset, include_custom = TRUE) - if (all(dataset=="custom",is.null(path_to_nc))){ + if (all(dataset == "custom", is.null(path_to_nc))) { stop("you need to set path_to_nc if dataset='custom'") } # check that we are only given path_to_nc if we use a custom dataset - if (!is.null(path_to_nc)){ - if (dataset!="custom"){ + if (!is.null(path_to_nc)) { + if (dataset != "custom") { stop("path_to_nc can only be set if dataset=='custom'") } - if (!file.exists(path_to_nc)){ + if (!file.exists(path_to_nc)) { stop("path_to_nc does not point to a file") } } return(TRUE) -} \ No newline at end of file +} diff --git a/R/check_var_in_nc.R b/R/check_var_in_nc.R index 219542e1..02d46b89 100644 --- a/R/check_var_in_nc.R +++ b/R/check_var_in_nc.R @@ -5,7 +5,7 @@ #' @param bio_variables vector of names of variables to be extracted #' @param path_to_nc the path to the custom nc file containing the paleoclimate #' reconstructions. -#' +#' #' @keywords internal check_var_in_nc <- function(bio_variables, path_to_nc) { diff --git a/R/get_available_datasets.R b/R/get_available_datasets.R index b460d5d6..2133202e 100644 --- a/R/get_available_datasets.R +++ b/R/get_available_datasets.R @@ -19,13 +19,13 @@ get_available_datasets <- function() { #' @keywords internal -check_available_dataset <- function(dataset, include_custom=FALSE) { +check_available_dataset <- function(dataset, include_custom = FALSE) { available_datasets <- get_available_datasets() - if (include_custom){ + if (include_custom) { available_datasets <- c(available_datasets, "custom") } if (!dataset %in% available_datasets) { - stop("'dataset' must be one of ", paste (available_datasets, + stop("'dataset' must be one of ", paste(available_datasets, collapse = ", " )) } else { diff --git a/R/get_downloaded_datasets.R b/R/get_downloaded_datasets.R index 46f001e6..d4410a5c 100644 --- a/R/get_downloaded_datasets.R +++ b/R/get_downloaded_datasets.R @@ -6,8 +6,8 @@ #' #' @export -get_downloaded_datasets <- function(data_path=NULL) { - if (is.null(data_path)){ +get_downloaded_datasets <- function(data_path = NULL) { + if (is.null(data_path)) { data_path <- get_data_path() } all_nc_files <- list.files(data_path) @@ -38,7 +38,8 @@ check_var_downloaded <- function(variable, dataset) { # test if we have downloaded already if (!all(variable %in% get_downloaded_datasets() [[dataset]])) { - missing_vars <- variable[!variable %in% get_downloaded_datasets()[[dataset]]] + missing_vars <- variable[!variable %in% + get_downloaded_datasets()[[dataset]]] stop( "variable (", paste(missing_vars, collapse = ", "), ") not yet downloaded, use `download_dataset()`" diff --git a/R/get_mis_time_steps.R b/R/get_mis_time_steps.R index d3b65cc4..37717f13 100644 --- a/R/get_mis_time_steps.R +++ b/R/get_mis_time_steps.R @@ -5,10 +5,11 @@ #' @param mis string giving the mis; it must use the same spelling as used in #' /code{mis_boundaries} #' @param dataset string defining dataset to be downloaded (a list of possible -#' values can be obtained with \code{get_available_datasets}). If set to "custom", -#' then a single nc file is used from "path_to_nc" +#' values can be obtained with \code{get_available_datasets}). If set to +#' "custom", then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. +#' reconstructions. All the variables of interest need to be included in +#' this file. #' #' @export diff --git a/R/get_time_steps.R b/R/get_time_steps.R index f9b1b7ee..42ae9f00 100644 --- a/R/get_time_steps.R +++ b/R/get_time_steps.R @@ -3,10 +3,11 @@ #' Get the time steps available in a given dataset. #' #' @param dataset string defining dataset to be downloaded (a list of possible -#' values can be obtained with \code{get_available_datasets}). If set to "custom", -#' then a single nc file is used from "path_to_nc" +#' values can be obtained with \code{get_available_datasets}). If set to +#' "custom", then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. +#' reconstructions. All the variables of interest need to be included in +#' this file. #' #' @export diff --git a/R/location_slice.R b/R/location_slice.R index 7ff105b8..77844516 100644 --- a/R/location_slice.R +++ b/R/location_slice.R @@ -6,13 +6,14 @@ #' #' @param x a 2 column matrix (with columns `longitude`, ranging #' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. -#' @param time_bp the time slice in years before present (negative values represent -#' time before present, positive values time in the future). +#' @param time_bp the time slice in years before present (negative +#' values represent time before present, positive values time in the future). #' #' @param bio_variables vector of names of variables to be extracted. #' @param dataset string defining the dataset to use (one of Beyer2020, #' Krapp2021, Example or custom). #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. +#' reconstructions. All the variables of interest need to be included +#' in this file. #' @param nn_interpol boolean determining whether nearest neighbour #' interpolation is used to estimate climate for cells that lack such #' information (i.e. they are under water or ice). Interpolation is only @@ -28,7 +29,6 @@ location_slice <- dataset, path_to_nc = NULL, nn_interpol = TRUE) { - check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) # if we are using standard datasets, check whether a variables exists @@ -62,15 +62,15 @@ location_slice <- this_var_nc <- this_var } if (is.null(time_indeces)) { + times <- get_time_steps(dataset = "custom", path_to_nc = this_file) time_indeces <- time_bp_to_index( - time_bp = time_bp, path_to_nc = - this_file + time_bp = time_bp, time_steps = times ) unique_time_indeces <- unique(time_indeces) } climate_brick <- terra::rast(this_file, subds = this_var_nc) - # create column to store variable + # create column to store variable locations_data[this_var] <- NA for (j in unique_time_indeces) { this_slice <- terra::subset(climate_brick, j) @@ -123,17 +123,18 @@ location_slice <- #' Extract local climate for one or more locations for a given time slice. #' #' Deprecated version of \code{location_slice} -#' +#' #' @param ... arguments to be passed to \code{location_slice} -#' +#' #' @export -climate_for_locations <-function(...){ +climate_for_locations <- function(...) { warning("DEPRECATED: use 'location_slice' instead") - if (!is.null(path_to_nc)){ - stop("the use of pastclimData is now deprecated, use 'set_path_data' instead") + if (!is.null(path_to_nc)) { + stop( + "the use of pastclimData is now deprecated", + "use 'set_path_data' instead" + ) } location_slice(...) } - - diff --git a/R/region_series.R b/R/region_series.R index ef500e0c..dd4a8f05 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -1,6 +1,7 @@ #' Extract a time series of climate variables for a region #' -#' This function extracts a time series of one or more climate variables for a given +#' This function extracts a time series of one or more climate variables for +#' a given #' dataset covering a region (or the whole world). The function returns a #' SpatRasterDataset \code{terra::sds} object, with #' each variable as a sub-dataset. @@ -13,13 +14,8 @@ #' @param dataset string defining the dataset to use. If set to "custom", #' then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. -#' -#' location_slice -#' location_series -#' region_slice -#' region_series -#' slice_region_series +#' reconstructions. All the variables of interest need to be included in +#' this file. #' #' @import terra #' @export @@ -29,9 +25,8 @@ region_series <- bio_variables, dataset, path_to_nc = NULL) { - check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) - + # check whether the variables exist for this dataset if (dataset != "custom") { # if we are using standard datasets check_var_downloaded(bio_variables, dataset) @@ -54,25 +49,24 @@ region_series <- } # figure out the time indeces the first time we run this if (is.null(time_index)) { - # as we have the file name, we can us the same code for custom and + # as we have the file name, we can us the same code for custom and # standard datasets. - times <- get_time_steps(dataset="custom", path_to_nc = this_file) - time_index <- match(time_bp,times) - if (any(is.na(time_index))){ + times <- get_time_steps(dataset = "custom", path_to_nc = this_file) + time_index <- match(time_bp, times) + if (any(is.na(time_index))) { stop("time_bp should only include time steps available in the dataset") } } var_brick <- terra::rast(this_file, subds = this_var_nc) - climate_spatrasters[[this_var]] <- terra::subset(var_brick, subset = time_index) + climate_spatrasters[[this_var]] <- terra::subset(var_brick, + subset = time_index) varnames(climate_spatrasters[[this_var]]) <- this_var names(climate_spatrasters[[this_var]]) <- paste(this_var, - time(climate_spatrasters[[this_var]]), - sep="_") + time(climate_spatrasters[[this_var]]), + sep = "_" + ) } - climate_sds<-terra::sds(climate_spatrasters) - names(climate_sds)<-bio_variables - #names(climate_spatraster) <- varnames(climate_spatraster) <- bio_variables + climate_sds <- terra::sds(climate_spatrasters) + names(climate_sds) <- bio_variables return(terra::sds(climate_spatrasters)) } - - diff --git a/R/region_slice.R b/R/region_slice.R index ca3b4de3..9891eac1 100644 --- a/R/region_slice.R +++ b/R/region_slice.R @@ -5,7 +5,8 @@ #' SpatRaster \code{terra::SpatRaster} object, with #' each variable as a layer. #' -#' @param time_bp the time slice in years before present (negative values represent +#' @param time_bp the time slice in years before present (negative +#' values represent #' time before present, positive values time in the future). The slice needs #' to exist in the dataset. To check which slices are available, you can use #' \code{get_time_steps}. @@ -13,7 +14,8 @@ #' @param dataset string defining the dataset to use. If set to "custom", #' then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included in this file. +#' reconstructions. All the variables of interest need to be included +#' in this file. #' #' @import terra #' @export @@ -23,28 +25,29 @@ region_slice <- bio_variables, dataset, path_to_nc = NULL) { - - this_series <- region_series(time_bp = time_bp, - bio_variables = bio_variables, - dataset = dataset, - path_to_nc = path_to_nc) + this_series <- region_series( + time_bp = time_bp, + bio_variables = bio_variables, + dataset = dataset, + path_to_nc = path_to_nc + ) return(slice_region_series(x = this_series, time_bp = time_bp)) - } #' Extract a climate slice for a region #' #' Deprecated version of \code{region_slice} -#' +#' #' @param ... arguments to be passed to \code{region_slice} -#' +#' #' @export -climate_for_time_slice <-function(...){ +climate_for_time_slice <- function(...) { warning("DEPRECATED: use 'region_slice' instead") - if (!is.null(path_to_nc)){ - stop("the use of pastclimData is now deprecated, use 'set_path_data' instead") + if (!is.null(path_to_nc)) { + stop("the use of pastclimData is now deprecated,", + " use 'set_path_data' instead") } region_slice(...) } diff --git a/R/set_data_path.R b/R/set_data_path.R index dd051032..4e9768b9 100644 --- a/R/set_data_path.R +++ b/R/set_data_path.R @@ -1,42 +1,48 @@ #' Set the data path where climate reconstructions will be stored #' -#' This function sets the path where climate reconstructions will be stored. This +#' This function sets the path where climate reconstructions will be stored. +#' This #' information is stored in a file names "pastclim_data.txt", which is found #' in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. #' the default configuration directory for the package as set in R >= 4.0). #' #' @param path_to_nc the path to the file that contains the downloaded -#' resonstructions. If left unset, the default location returned by +#' resonstructions. If left unset, the default location returned by #' `tools::R_user_dir("pastclim","data")` will be used #' #' @export -set_data_path<-function(path_to_nc=NULL){ +set_data_path <- function(path_to_nc = NULL) { # if we don't have a config directory, create one - if (!dir.exists(tools::R_user_dir("pastclim","config"))){ - dir.create(tools::R_user_dir("pastclim","config"), recursive = TRUE) + if (!dir.exists(tools::R_user_dir("pastclim", "config"))) { + dir.create(tools::R_user_dir("pastclim", "config"), recursive = TRUE) } # use the default location - if (is.null(path_to_nc)){ - cat(tools::R_user_dir("pastclim","data"), "\n", - file = file.path(tools::R_user_dir("pastclim","config"), - "pastclim_data.txt")) + if (is.null(path_to_nc)) { + cat(tools::R_user_dir("pastclim", "data"), "\n", + file = file.path( + tools::R_user_dir("pastclim", "config"), + "pastclim_data.txt" + ) + ) # and now create it if it does not exist yet - if (!dir.exists(tools::R_user_dir("pastclim","data"))){ - dir.create(tools::R_user_dir("pastclim","data"), recursive = TRUE) + if (!dir.exists(tools::R_user_dir("pastclim", "data"))) { + dir.create(tools::R_user_dir("pastclim", "data"), recursive = TRUE) } - # update option - options(pastclim.data_path = tools::R_user_dir("pastclim","data")) + # update option + options(pastclim.data_path = tools::R_user_dir("pastclim", "data")) } else { # check that it exists - if (dir.exists(path_to_nc)){ - cat(path_to_nc, "\n",file = file.path(tools::R_user_dir("pastclim","config"), - "pastclim_data.txt")) + if (dir.exists(path_to_nc)) { + cat(path_to_nc, "\n", file = file.path( + tools::R_user_dir("pastclim", "config"), + "pastclim_data.txt" + )) } else { - stop (path_to_nc," does not exist!") + stop(path_to_nc, " does not exist!") } - # update option - options(pastclim.data_path = path_to_nc) + # update option + options(pastclim.data_path = path_to_nc) } # move the example data into the new data path copy_example_data() @@ -45,29 +51,34 @@ set_data_path<-function(path_to_nc=NULL){ #' Get the data path where climate reconstructions are stored #' -#' This function returns the path where climate reconstructions will be stored. This -#' information is stored in a file names "pastclim_data.txt", which is found -#' in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +#' This function returns the path where climate reconstructions will be stored. +#' This information is stored in a file names "pastclim_data.txt", which +#' is found in the directory returned by +#' `tools::R_user_dir("pastclim","config")` (i.e. #' the default configuration directory for the package as set in R >= 4.0). -#' +#' #' If this function is run before any path was set, it calls `set_data_path`, -#' which defaults to storing data in the directory returned by +#' which defaults to storing data in the directory returned by #' `tools::R_user_dir("pastclim","data")` -#' +#' #' @export -get_data_path<-function(){ +get_data_path <- function() { # if the package already initiliased - if (!is.null(getOption("pastclim.data_path"))){ + if (!is.null(getOption("pastclim.data_path"))) { return(getOption("pastclim.data_path")) } else { # get the info from the config file # if data path was never set before, we set it to its default location - if (!file.exists(file.path(tools::R_user_dir("pastclim","config"), - "pastclim_data.txt"))){ + if (!file.exists(file.path( + tools::R_user_dir("pastclim", "config"), + "pastclim_data.txt" + ))) { set_data_path() } - path_to_nc = utils::read.table(file.path(tools::R_user_dir("pastclim","config"), - "pastclim_data.txt"))[1,1] + path_to_nc <- utils::read.table(file.path( + tools::R_user_dir("pastclim", "config"), + "pastclim_data.txt" + ))[1, 1] return(path_to_nc) } } @@ -75,13 +86,16 @@ get_data_path<-function(){ #' Internal function to copy the example dataset when a new data path is set #' #' Copy example dataset -#' +#' #' @keywords internal -copy_example_data <-function() { - if (!file.exists(file.path(get_data_path(),"example_climate_nc"))){ - file.copy(from = system.file("/extdata/example_climate.nc", - package="pastclim"), - to= file.path(get_data_path(),"example_climate.nc")) +copy_example_data <- function() { + if (!file.exists(file.path(get_data_path(), "example_climate_nc"))) { + file.copy( + from = system.file("/extdata/example_climate.nc", + package = "pastclim" + ), + to = file.path(get_data_path(), "example_climate.nc") + ) } -} \ No newline at end of file +} diff --git a/R/slice_region_series.R b/R/slice_region_series.R index 9168ec96..cfc17c40 100644 --- a/R/slice_region_series.R +++ b/R/slice_region_series.R @@ -1,35 +1,35 @@ #' Extract a slice for a time series of climate variables for a region #' -#' This function extracts a time slice from time series of one or more climate +#' This function extracts a time slice from time series of one or more climate #' variables for a given dataset covering a region (or the whole world). -#' +#' #' @param x time series generated with \code{region_series} -#' @param time_bp time slices in years before present (negative). The slices needs -#' to exist in the dataset. To check which slices are available, you can use -#' \code{terra::time(x[[1]])} (note that you have to use the `time` fuction -#' on the first element of the 'sds' object, i.e. on one of the 'spatRaster' -#' objects) -#' +#' @param time_bp time slices in years before present (negative). The slices +#' need to exist in the dataset. To check which slices are available, you +#' can use \code{terra::time(x[[1]])} (note that you have to use +#' the `time` fuction on the first element of the 'sds' object, i.e. on one +#' of the 'spatRaster' objects) +#' #' @export -slice_region_series <- function(x, time_bp){ - if (length(time_bp)!=1){ +slice_region_series <- function(x, time_bp) { + if (length(time_bp) != 1) { stop("time_bp should be a single time step") } # check that time_bp is part of the series - if (!time_bp %in% time(x[[1]])){ + if (!time_bp %in% time(x[[1]])) { stop("time_bp is not a time slice within the region series x") } # get index - time_index <- which(time(x[[1]])==time_bp) + time_index <- which(time(x[[1]]) == time_bp) # now slice it and convert it to a Spatraster - for (i in 1:length(x)){ - if (i==1){ - climate_spatraster <- subset(x[[i]],time_index) + for (i in 1:length(x)) { + if (i == 1) { + climate_spatraster <- subset(x[[i]], time_index) } else { - terra::add(climate_spatraster) <- subset(x[[i]],time_index) + terra::add(climate_spatraster) <- subset(x[[i]], time_index) } } # names(climate_spatraster) <- varnames(climate_spatraster) #<- names(x) return(climate_spatraster) -} \ No newline at end of file +} diff --git a/R/time_bp_to_index.R b/R/time_bp_to_index.R index d2ac468c..6769516c 100644 --- a/R/time_bp_to_index.R +++ b/R/time_bp_to_index.R @@ -3,14 +3,11 @@ #' Internal function #' #' @param time_bp vector of times BP -#' @param path_to_nc path to nc file +#' @param time_steps time steps for which reconstructions are available #' #' @keywords internal -time_bp_to_index <- function(time_bp, path_to_nc) { - climate_nc <- ncdf4::nc_open(path_to_nc) - time_steps <- (climate_nc$dim$time$vals) - ncdf4::nc_close(climate_nc) +time_bp_to_index <- function(time_bp, time_steps) { time_indeces <- sapply(time_bp, function(a, b) { which.min(abs(a - b)) diff --git a/R/zzz.R b/R/zzz.R index 11e9c885..2362fcaf 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -4,7 +4,7 @@ pastclim.data_path = get_data_path() ) toset <- !(names(op.pastclim) %in% names(op)) - if(any(toset)) options(op.pastclim[toset]) - + if (any(toset)) options(op.pastclim[toset]) + invisible() -} \ No newline at end of file +} diff --git a/tests/testthat/_test_climate_for_location.R b/tests/testthat/_test_climate_for_location.R deleted file mode 100644 index cb7f5eaf..00000000 --- a/tests/testthat/_test_climate_for_location.R +++ /dev/null @@ -1,86 +0,0 @@ -test_that("climate_for_time_slice", { - # using standard dataset - path_to_example_nc <- system.file("/extdata/", package = "pastclim") - locations <- data.frame( - longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), - time_bp = c(0, -10000, -20000, -10000) - ) - - this_climate <- climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), - dataset = "Example", nn_interpol = FALSE, path_to_nc = path_to_example_nc - ) - expect_false(is.na(this_climate[1, "bio01"])) - expect_true(is.na(this_climate[3, "bio01"])) - expect_true(is.na(this_climate[4, "bio01"])) - # test nn_interpolation - this_climate <- climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), - dataset = "Example", nn_interpol = TRUE, path_to_nc = path_to_example_nc - ) - expect_false(is.na(this_climate[1, "bio01"])) - expect_true(is.na(this_climate[3, "bio01"])) - expect_false(is.na(this_climate[4, "bio01"])) - - # now test if we try a variable that is not available - expect_error( - climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("bio01", "bio19"), - dataset = "Example", nn_interpol = FALSE, path_to_nc = path_to_example_nc - ), - "bio19 not available" - ) - expect_error( - climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("bio01", "bio19", "bio20"), - dataset = "Example", nn_interpol = FALSE, path_to_nc = path_to_example_nc - ), - "bio19, bio20 not available" - ) - - # now test if we try to use a variable that we have not downloaded yet - expect_error( - climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), - dataset = "Krapp2021", nn_interpol = FALSE, - path_to_nc = path_to_example_nc - ), - "variable \\(bio01, bio12\\) not yet downloaded" - ) - - # now treat it as if it was a custom dataset - path_to_example_nc <- system.file("/extdata/example_climate.nc", - package = "pastclim" - ) - this_climate <- climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO12"), - dataset = "custom", nn_interpol = FALSE, path_to_nc = path_to_example_nc - ) - expect_false(is.na(this_climate[1, "BIO1"])) - expect_true(is.na(this_climate[3, "BIO1"])) - expect_true(is.na(this_climate[4, "BIO1"])) - # if we try to use a variable that does not exist - expect_error( - climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO22"), - dataset = "custom", nn_interpol = FALSE, path_to_nc = path_to_example_nc - ), - "variable \\(BIO22\\) not present in the file" - ) - # if we try to use a file that does not exist - expect_error( - climate_for_locations( - x = locations[, c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables = c("BIO1", "BIO22"), - dataset = "custom", nn_interpol = FALSE, path_to_nc = "/foo" - ), - "file /foo does not exist" - ) -}) diff --git a/tests/testthat/test_check_dataset_path.R b/tests/testthat/test_check_dataset_path.R index 1d240d81..60f73482 100644 --- a/tests/testthat/test_check_dataset_path.R +++ b/tests/testthat/test_check_dataset_path.R @@ -1,18 +1,28 @@ -test_that("check_dataset_path errors", - { - expect_true(check_dataset_path("Example", NULL)) - expect_error(check_dataset_path("foo", NULL), - "'dataset' must be one of ") - expect_error(check_dataset_path("custom", NULL), - "you need to set path_to_nc if dataset='custom'") - expect_true(check_dataset_path("custom", - file.path(get_data_path(), - "example_climate.nc"))) - expect_error(check_dataset_path("custom", - file.path(get_data_path(), - "foo.nc")), - "path_to_nc does not point to a file") - - - }) - \ No newline at end of file +test_that("check_dataset_path errors", { + expect_true(check_dataset_path("Example", NULL)) + expect_error( + check_dataset_path("foo", NULL), + "'dataset' must be one of " + ) + expect_error( + check_dataset_path("custom", NULL), + "you need to set path_to_nc if dataset='custom'" + ) + expect_true(check_dataset_path( + "custom", + file.path( + get_data_path(), + "example_climate.nc" + ) + )) + expect_error( + check_dataset_path( + "custom", + file.path( + get_data_path(), + "foo.nc" + ) + ), + "path_to_nc does not point to a file" + ) +}) diff --git a/tests/testthat/test_get_available_dataset.R b/tests/testthat/test_get_available_dataset.R index 1db37dc6..ca8fe8c3 100644 --- a/tests/testthat/test_get_available_dataset.R +++ b/tests/testthat/test_get_available_dataset.R @@ -9,10 +9,10 @@ testthat::test_that("get_and_check_available_datasets", { check_available_dataset("foo"), "'dataset' must be one of Beyer2020, Krapp2021, Example" ) - testthat::expect_true(check_available_dataset("custom", include_custom = TRUE)) + testthat::expect_true(check_available_dataset("custom", + include_custom = TRUE)) testthat::expect_error( check_available_dataset("custom"), "'dataset' must be one of Beyer2020, Krapp2021, Example" - ) + ) }) - diff --git a/tests/testthat/test_get_biome_classes.R b/tests/testthat/test_get_biome_classes.R index 3d8f503b..e45774f8 100644 --- a/tests/testthat/test_get_biome_classes.R +++ b/tests/testthat/test_get_biome_classes.R @@ -1,5 +1,4 @@ testthat::test_that("get biome classes", { # check that we have 29 classes as expected - expect_true(nrow(get_biome_classes("Example" - )) == 29) + expect_true(nrow(get_biome_classes("Example")) == 29) }) diff --git a/tests/testthat/test_get_downloaded_datasets.R b/tests/testthat/test_get_downloaded_datasets.R index 3cbde066..aa62d795 100644 --- a/tests/testthat/test_get_downloaded_datasets.R +++ b/tests/testthat/test_get_downloaded_datasets.R @@ -6,8 +6,7 @@ testthat::test_that("get_downloaded_datasets", { # return an empty list if there are no files expect_true(length(get_downloaded_datasets(data_path = "./foo")) == 0) # check that we have downloaded a variable - expect_true(check_var_downloaded("bio01", "Example" - )) + expect_true(check_var_downloaded("bio01", "Example")) # raise error if the variable is not available expect_error( check_var_downloaded("npp", "Example"), diff --git a/tests/testthat/test_get_mis_time_steps.R b/tests/testthat/test_get_mis_time_steps.R index 8986bbb8..d261c9dd 100644 --- a/tests/testthat/test_get_mis_time_steps.R +++ b/tests/testthat/test_get_mis_time_steps.R @@ -9,17 +9,21 @@ test_that("get_mis_time_steps for local file", { path_to_example_nc <- system.file("/extdata/", "example_climate.nc", package = "pastclim" ) - expect_equal(get_mis_time_steps(mis = "2", dataset = "custom", - path_to_nc = path_to_example_nc), - c(-20000, -15000), - ignore_attr = TRUE + expect_equal(get_mis_time_steps( + mis = "2", dataset = "custom", + path_to_nc = path_to_example_nc + ), + c(-20000, -15000), + ignore_attr = TRUE ) }) test_that("get_mis_time_steps requires correct variables", { expect_error( - get_mis_time_steps(mis = "blah", dataset = "custom", - path_to_nc = path_to_example_nc), + get_mis_time_steps( + mis = "blah", dataset = "custom", + path_to_nc = path_to_example_nc + ), "'mis' should be one of" ) }) diff --git a/tests/testthat/test_get_set_data_path.R b/tests/testthat/test_get_set_data_path.R index e2834d5f..8a7bc781 100644 --- a/tests/testthat/test_get_set_data_path.R +++ b/tests/testthat/test_get_set_data_path.R @@ -1,6 +1,8 @@ test_that("set and get data path", { set_data_path() - expect_true(file.exists(file.path(tools::R_user_dir("pastclim","config"), - "pastclim_data.txt"))) - expect_equal(get_data_path(),tools::R_user_dir("pastclim","data")) -}) \ No newline at end of file + expect_true(file.exists(file.path( + tools::R_user_dir("pastclim", "config"), + "pastclim_data.txt" + ))) + expect_equal(get_data_path(), tools::R_user_dir("pastclim", "data")) +}) diff --git a/tests/testthat/test_get_time_steps.R b/tests/testthat/test_get_time_steps.R index 3f4886fb..19375d82 100644 --- a/tests/testthat/test_get_time_steps.R +++ b/tests/testthat/test_get_time_steps.R @@ -20,14 +20,18 @@ test_that("get_time_steps for local file", { path_to_example_nc <- system.file("/extdata/", "example_climate.nc", package = "pastclim" ) - expect_equal(get_time_steps(dataset = "custom", - path_to_nc = path_to_example_nc), - c(-20000, -15000, -10000, -5000, 0), - ignore_attr = TRUE + expect_equal(get_time_steps( + dataset = "custom", + path_to_nc = path_to_example_nc + ), + c(-20000, -15000, -10000, -5000, 0), + ignore_attr = TRUE ) - expect_equal(get_mis_time_steps(mis = "2", dataset = "custom", - path_to_nc = path_to_example_nc), - c(-20000, -15000), - ignore_attr = TRUE + expect_equal(get_mis_time_steps( + mis = "2", dataset = "custom", + path_to_nc = path_to_example_nc + ), + c(-20000, -15000), + ignore_attr = TRUE ) }) diff --git a/tests/testthat/test_location_slice.R b/tests/testthat/test_location_slice.R index 0352386e..792c979e 100644 --- a/tests/testthat/test_location_slice.R +++ b/tests/testthat/test_location_slice.R @@ -8,7 +8,8 @@ test_that("location_slice", { this_climate <- location_slice( x = locations[, c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), - dataset = "Example", nn_interpol = FALSE) + dataset = "Example", nn_interpol = FALSE + ) expect_false(is.na(this_climate[1, "bio01"])) expect_true(is.na(this_climate[3, "bio01"])) expect_true(is.na(this_climate[4, "bio01"])) @@ -80,7 +81,7 @@ test_that("location_slice", { ), "path_to_nc does not point to a file" ) - + # now use times which are not the exact timesteps locations_timeoff <- data.frame( longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), @@ -90,12 +91,13 @@ test_that("location_slice", { x = locations_timeoff[, c("longitude", "latitude")], time_bp = locations_timeoff$time_bp, bio_variables = c("bio01", "bio12"), dataset = "Example", nn_interpol = TRUE - ) + ) # and compare it to the estimates from exact times (they should be the same!) this_climate <- location_slice( x = locations[, c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), dataset = "Example", nn_interpol = TRUE ) - expect_true(identical(this_climate[,-c(1:3)], this_climate_timeoff[,-c(1:3)])) + expect_true(identical(this_climate[, -c(1:3)], + this_climate_timeoff[, -c(1:3)])) }) diff --git a/tests/testthat/test_region_series.R b/tests/testthat/test_region_series.R index 6555effc..caed33d2 100644 --- a/tests/testthat/test_region_series.R +++ b/tests/testthat/test_region_series.R @@ -1,37 +1,53 @@ test_that("region series", { # using standard dataset - climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), - "Example") + climate_region <- region_series( + c(-20000, -10000), c("bio01", "bio12"), + "Example" + ) expect_true(inherits(climate_region, "SpatRasterDataset")) - expect_true(all(names(climate_region)==c("bio01", "bio12"))) - expect_true(all(terra::nlyr(climate_region)==c(2,2))) - + expect_true(all(names(climate_region) == c("bio01", "bio12"))) + expect_true(all(terra::nlyr(climate_region) == c(2, 2))) + # do the same for a custom dataset - path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") - climate_region <- region_series(c(-20000,-10000), c("BIO1", "BIO10"), - "custom",path_to_nc = path_to_example_nc) + path_to_example_nc <- system.file("/extdata/example_climate.nc", + package = "pastclim") + climate_region <- region_series(c(-20000, -10000), c("BIO1", "BIO10"), + "custom", + path_to_nc = path_to_example_nc + ) expect_true(inherits(climate_region, "SpatRasterDataset")) - expect_true(all(names(climate_region)==c("BIO1", "BIO10"))) - expect_true(all(terra::nlyr(climate_region)==c(2,2))) - + expect_true(all(names(climate_region) == c("BIO1", "BIO10"))) + expect_true(all(terra::nlyr(climate_region) == c(2, 2))) + # if we try to use a variable that does not exist - expect_error(region_series(c(-20000,-10000), c("bio01", "bio19"), "Example"), - "bio19 not available") - expect_error(region_series(c(-20000,-10000), c("BIO1", "bio19"),"custom", - path_to_nc = path_to_example_nc), - "variable \\(bio19\\) not") - + expect_error( + region_series(c(-20000, -10000), c("bio01", "bio19"), "Example"), + "bio19 not available" + ) + expect_error( + region_series(c(-20000, -10000), c("BIO1", "bio19"), "custom", + path_to_nc = path_to_example_nc + ), + "variable \\(bio19\\) not" + ) + # if we try to use a variable that we have not downloaded yet - expect_error(region_series(c(-20000,-10000), c("bio01", "bio19"), "Krapp2021" - ), "^variable \\(bio01, bio19\\) not yet downloaded") + expect_error(region_series(c(-20000, -10000), + c("bio01", "bio19"), + "Krapp2021"), + "^variable \\(bio01, bio19\\) not yet downloaded") # if we try to use a file that does not exist - expect_error(region_series(c(-20000,-10000), c("BIO1", "BIO12"), "custom", + expect_error(region_series(c(-20000, -10000), c("BIO1", "BIO12"), "custom", path_to_nc = "./foo" ), "path_to_nc does not point to a file") - + # if we use time steps that do not exist - expect_error(region_series(c(-19000,-10000), c("bio01", "bio12"), - "Example"), - "time_bp should only include time steps available in the dataset") + expect_error( + region_series( + c(-19000, -10000), c("bio01", "bio12"), + "Example" + ), + "time_bp should only include time steps available in the dataset" + ) }) diff --git a/tests/testthat/test_region_slice.R b/tests/testthat/test_region_slice.R index 36054901..44b8bf30 100644 --- a/tests/testthat/test_region_slice.R +++ b/tests/testthat/test_region_slice.R @@ -1,20 +1,29 @@ test_that("region slice", { # using standard dataset - climate_slice <- region_slice(c(-10000), c("bio01", "bio12"), - "Example") + climate_slice <- region_slice( + c(-10000), c("bio01", "bio12"), + "Example" + ) expect_true(inherits(climate_slice, "SpatRaster")) - expect_true(all(varnames(climate_slice)==c("bio01", "bio12"))) - expect_true(terra::nlyr(climate_slice)==c(2)) - + expect_true(all(varnames(climate_slice) == c("bio01", "bio12"))) + expect_true(terra::nlyr(climate_slice) == c(2)) + # do the same for a custom dataset - path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim") + path_to_example_nc <- system.file("/extdata/example_climate.nc", + package = "pastclim") climate_slice <- region_slice(c(-10000), c("BIO1", "BIO10"), - "custom",path_to_nc = path_to_example_nc) + "custom", + path_to_nc = path_to_example_nc + ) expect_true(inherits(climate_slice, "SpatRaster")) - expect_true(all(varnames(climate_slice)==c("BIO1", "BIO10"))) - expect_true(terra::nlyr(climate_slice)==c(2)) - - expect_error(region_slice(c(-10000, -20000), c("bio01", "bio12"), - "Example"), - "time_bp should be a single time step") + expect_true(all(varnames(climate_slice) == c("BIO1", "BIO10"))) + expect_true(terra::nlyr(climate_slice) == c(2)) + + expect_error( + region_slice( + c(-10000, -20000), c("bio01", "bio12"), + "Example" + ), + "time_bp should be a single time step" + ) }) diff --git a/tests/testthat/test_slice_region_series.R b/tests/testthat/test_slice_region_series.R index 9af91537..e5516e49 100644 --- a/tests/testthat/test_slice_region_series.R +++ b/tests/testthat/test_slice_region_series.R @@ -1,15 +1,21 @@ test_that("climate_for_time_slice", { - climate_region <- region_series(c(-20000,-10000), c("bio01", "bio12"), - "Example") + climate_region <- region_series( + c(-20000, -10000), c("bio01", "bio12"), + "Example" + ) my_slice <- slice_region_series(climate_region, time_bp = -10000) expect_true(inherits(my_slice, "SpatRaster")) - expect_true(length(my_slice)==1) - expect_true(terra::nlyr(my_slice)==2) - expect_true(all(varnames(my_slice)==c("bio01", "bio12"))) + expect_true(length(my_slice) == 1) + expect_true(terra::nlyr(my_slice) == 2) + expect_true(all(varnames(my_slice) == c("bio01", "bio12"))) # use a time step that does not exist - expect_error(slice_region_series(climate_region, time_bp = -19000), - "time_bp is not a time slice within the region series x") + expect_error( + slice_region_series(climate_region, time_bp = -19000), + "time_bp is not a time slice within the region series x" + ) # use too many timesteps - expect_error(slice_region_series(climate_region, time_bp = c(-10000,-20000)), - "time_bp should be a single time step") + expect_error( + slice_region_series(climate_region, time_bp = c(-10000, -20000)), + "time_bp should be a single time step" + ) }) diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index e62aa0c5..68d09f99 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -79,6 +79,7 @@ what you want to download (see below). Let us start by inspecting the *Example* dataset. We can get a list of variables available for this dataset with: + ```{r} get_vars_for_dataset(dataset="Example") ``` @@ -101,43 +102,54 @@ and get_vars_for_dataset(dataset="Krapp2021") ``` -You will not be able to get the available timesteps until you actual download the data. `pastclim` offers an interface -to download the necessary files into your data path. Let's download `bio01` and `bio05` for the *Beyer2020* dataset (this operation might take several minutes, as the datasets are large; `R` will pause until +You will not be able to get the available time steps until you download the data. `pastclim` offers an interface +to download the necessary files into your data path. + +To inspect which datasets and variables have already been downloaded in the data path, +we can use: +```{r} +get_downloaded_datasets() +``` + +Let's now download `bio01` and `bio05` for the *Beyer2020* dataset (this operation might take several minutes, as the datasets are large; `R` will pause until the download is complete): ```{r eval=FALSE} download_dataset(dataset="Beyer2020", bio_variables = c("bio01","bio05")) ``` -You can get a summary of variables already downloaded (and thus that can be used) -with: -```{r} -get_downloaded_datasets() -``` +Note that multiple variables are packed together into a single file, so the command might list more variables than the ones that we downloaded originally. -Note that for the example above, we used the directory containing the example dataset; use the path where you stored the data that you downloaded. Note that multiple variables are packed together into a single file, so the command might list more variables than the ones that we downloaded originally. +# Get climate for locations -# Get climate for location +Often we want to get the climate for specific locations. We can do so by using +the function `location_slice`. With this function, we will get slices of climate +for the times relevant to the locations of interest. -Get climate for several locations at different times: +Let us create some fictitious locations: ```{r} locations<-data.frame(longitude=c(0,90,-120,-7),latitude=c(20,45,60,37), time_bp=c(0,-10000, -20000, -10000)) +locations +``` -climate_for_locations (x= locations[,c("longitude", "latitude")], +And extract their climatic conditions for `bio01` and `bio12`: +```{r} +location_slice (x= locations[,c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), dataset="Example", nn_interpol = FALSE) ``` Note that the last two locations, for the appropriate time steps, are not available (either under water or ice), and so `pastclim` does not return a climate reconstruction. In some instances, this is due to the discretisation of space in the raster. We can interpolate climate among the nearest neighbours, thus using climate reconstructions for neighbouring pixels if the location is just off one or more land pixels: ```{r} -climate_for_locations (x= locations[,c("longitude", "latitude")], +location_slice (x= locations[,c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), dataset="Example", nn_interpol = TRUE) ``` In this case, the last location is indeed just off the coast, and so we can reconstruct some appropriate climate by interpolating. Note that `nn_interpol = TRUE` is the default for this function. -Get time series for these locations: +Sometimes, we want to get a time series of climatic reconstructions, thus allowing us to +see how climate changed over time: ```{r} locations_ts <- time_series_for_locations(x= locations[,c("longitude", "latitude")], bio_variables=c("bio01","bio12"), @@ -145,11 +157,11 @@ locations_ts <- time_series_for_locations(x= locations[,c("longitude", "latitude ``` The resulting dataframe can be subsetted to get the time series for each location -(the small example dataset only contains 5 time points): +(note that the small *Example* dataset only contains 5 time points): ```{r} subset(locations_ts,id==1) ``` -Note that the last two locations do not return information for all time steps. That's due to the change in sea level and icesheet extent, and this function (as opposed to `climate_for_locations`) does not allow for interpolation. +Also note that the last two locations do not return information for all time steps. That's due to the change in sea level and icesheet extent, and this function (as opposed to `climate_for_locations`) does not allow for interpolation. ```{r} subset(locations_ts,id==3) ``` @@ -157,22 +169,27 @@ subset(locations_ts,id==3) subset(locations_ts,id==4) ``` -# Get climate for a given time step +# Get climate for a region + +Instead of focussing on specific locations, we might want to look a whole region. +For a given time step, we can extract a slice of climate with -We can extract rasters for a given time step: ```{r} -climate_20k <- climate_for_time_slice(time_bp = -20000, +climate_20k <- region_slice(time_bp = -20000, c("bio01","bio10","bio12"), dataset="Example") ``` -We can get a summary of this object: +This returns a raster (technically a `spatRaster` object as defined in the `terra` +library, meaning that we can perform all standard `terra` raster operations +on this object). To interact with `spatRaster` objects, it is best to load the +`terra` library; we can then get a summary of this object: ```{r} library(terra) climate_20k ``` -and plot these two layers: +and plot these three variables (the layers of the raster): ```{r, fig.width=6, fig.height=5} terra::plot(climate_20k) ``` @@ -209,7 +226,7 @@ climate_20k_afr_eurasia <- terra::crop(climate_20k_afr_eurasia,afr_eurasia_vec) terra::plot(climate_20k_afr_eurasia) ``` -`pastclim` includes a number of pre-generated masks for the main continental masses, stored in the dataset `continent_outlines` in an `sf` object. Note that outlines that cross the antimeridian are split into multiple polygons (so that they can be used without projecting the rasters). `continent_outlines_union` provides the same outlines as single polygons (in case you want to use a projection). We can build the outline for Africa nad Eurasian by unioning the two individual outines: +`pastclim` includes a number of pre-generated masks for the main continental masses, stored in the dataset `continent_outlines` in an `sf` object. Note that outlines that cross the antimeridian are split into multiple polygons (so that they can be used without projecting the rasters). `continent_outlines_union` provides the same outlines as single polygons (in case you want to use a projection). We can build the outline for Africa and Eurasia by unioning the two individual outlines: ```{r} library(sf) africa_outline<- subset(region_outline, region_outline$name=="Africa") From 5faefb86be1a6a3af130c8635fc3c6e71b85db0e Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 11:35:07 +0100 Subject: [PATCH 14/83] start fixing vignette [skip ci] --- NAMESPACE | 1 + R/check_available_datasets.R | 23 +++++++++++++++++++++++ R/check_var_downloaded.R | 24 ++++++++++++++++++++++++ R/get_available_datasets.R | 25 ------------------------- R/get_downloaded_datasets.R | 27 +-------------------------- R/get_ice_mask.R | 2 +- R/get_land_mask.R | 2 +- R/location_slice.R | 1 + R/region_slice.R | 8 ++++---- man/check_available_dataset.Rd | 2 +- man/check_dataset_path.Rd | 3 ++- man/check_var_downloaded.Rd | 6 ++---- man/climate_for_locations.Rd | 2 +- man/climate_for_time_slice.Rd | 18 +++++------------- man/get_data_path.Rd | 9 +++++---- man/get_downloaded_datasets.Rd | 4 ++-- man/get_mis_time_steps.Rd | 7 ++++--- man/get_time_steps.Rd | 7 ++++--- man/location_slice.Rd | 11 ++++++----- man/region_series.Rd | 15 ++++++--------- man/region_slice.Rd | 30 ++++++++++++++++++++++++++++++ man/set_data_path.Rd | 5 +++-- man/slice_region_series.Rd | 10 ++++++---- man/time_bp_to_index.Rd | 4 ++-- vignettes/pastclim_overview.Rmd | 17 ++++++++++------- 25 files changed, 145 insertions(+), 118 deletions(-) create mode 100644 R/check_available_datasets.R create mode 100644 R/check_var_downloaded.R create mode 100644 man/region_slice.Rd diff --git a/NAMESPACE b/NAMESPACE index 52a21d90..5e6ca054 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,6 +15,7 @@ export(get_time_steps) export(get_vars_for_dataset) export(location_slice) export(region_series) +export(region_slice) export(set_data_path) export(slice_region_series) export(time_series_for_locations) diff --git a/R/check_available_datasets.R b/R/check_available_datasets.R new file mode 100644 index 00000000..732806a6 --- /dev/null +++ b/R/check_available_datasets.R @@ -0,0 +1,23 @@ +#' Check if dataset is available. +#' +#' Internal getter function +#' +#' @param dataset string defining dataset +#' @param include_custom boolean whether a 'custom' dataset is allowed +#' +#' @keywords internal + + +check_available_dataset <- function(dataset, include_custom = FALSE) { + available_datasets <- get_available_datasets() + if (include_custom) { + available_datasets <- c(available_datasets, "custom") + } + if (!dataset %in% available_datasets) { + stop("'dataset' must be one of ", paste(available_datasets, + collapse = ", " + )) + } else { + return(TRUE) + } +} diff --git a/R/check_var_downloaded.R b/R/check_var_downloaded.R new file mode 100644 index 00000000..8d8f17ff --- /dev/null +++ b/R/check_var_downloaded.R @@ -0,0 +1,24 @@ +#' Internal functions to check whether we have downloaded a given variable +#' for a dataset +#' +#' @param variable a vector of names of the variables of interest +#' @param dataset dataset of interest +#' +#' @keywords internal + +check_var_downloaded <- function(variable, dataset) { + # first check the variable exists for that dataset + check_available_variable(variable, dataset) + + # test if we have downloaded already + if (!all(variable %in% get_downloaded_datasets() + [[dataset]])) { + missing_vars <- variable[!variable %in% + get_downloaded_datasets()[[dataset]]] + stop( + "variable (", paste(missing_vars, collapse = ", "), + ") not yet downloaded, use `download_dataset()`" + ) + } + return(TRUE) +} diff --git a/R/get_available_datasets.R b/R/get_available_datasets.R index 2133202e..55f730ca 100644 --- a/R/get_available_datasets.R +++ b/R/get_available_datasets.R @@ -7,28 +7,3 @@ get_available_datasets <- function() { return(unique(as.character(files_by_dataset$dataset))) } - - -#' Check if dataset is available. -#' -#' Internal getter function -#' -#' @param dataset string defining dataset -#' @param include_custom boolean whether a 'custom' dataset is allowed -#' -#' @keywords internal - - -check_available_dataset <- function(dataset, include_custom = FALSE) { - available_datasets <- get_available_datasets() - if (include_custom) { - available_datasets <- c(available_datasets, "custom") - } - if (!dataset %in% available_datasets) { - stop("'dataset' must be one of ", paste(available_datasets, - collapse = ", " - )) - } else { - return(TRUE) - } -} diff --git a/R/get_downloaded_datasets.R b/R/get_downloaded_datasets.R index d4410a5c..7bd91aa2 100644 --- a/R/get_downloaded_datasets.R +++ b/R/get_downloaded_datasets.R @@ -21,29 +21,4 @@ get_downloaded_datasets <- function(data_path = NULL) { ] } downloaded_vars -} - -#' Internal functions to check whether we have downloaded a given variable -#' for a dataset -#' -#' @param variable a vector of names of the variables of interest -#' @param dataset dataset of interest -#' -#' @keywords internal - -check_var_downloaded <- function(variable, dataset) { - # first check the variable exists for that dataset - check_available_variable(variable, dataset) - - # test if we have downloaded already - if (!all(variable %in% get_downloaded_datasets() - [[dataset]])) { - missing_vars <- variable[!variable %in% - get_downloaded_datasets()[[dataset]]] - stop( - "variable (", paste(missing_vars, collapse = ", "), - ") not yet downloaded, use `download_dataset()`" - ) - } - return(TRUE) -} +} \ No newline at end of file diff --git a/R/get_ice_mask.R b/R/get_ice_mask.R index 7157756a..2c7cba3c 100644 --- a/R/get_ice_mask.R +++ b/R/get_ice_mask.R @@ -11,7 +11,7 @@ #' @export get_ice_mask <- function(time_bp, dataset) { - climate_slice <- climate_for_time_slice( + climate_slice <- region_slice( time_bp = time_bp, bio_variables = "biome", dataset = dataset ) diff --git a/R/get_land_mask.R b/R/get_land_mask.R index a8289027..e29b0e0f 100644 --- a/R/get_land_mask.R +++ b/R/get_land_mask.R @@ -11,7 +11,7 @@ #' @export get_land_mask <- function(time_bp, dataset) { - climate_slice <- climate_for_time_slice( + climate_slice <- region_slice( time_bp = time_bp, bio_variables = "biome", dataset = dataset ) diff --git a/R/location_slice.R b/R/location_slice.R index 77844516..ce285ecc 100644 --- a/R/location_slice.R +++ b/R/location_slice.R @@ -66,6 +66,7 @@ location_slice <- time_indeces <- time_bp_to_index( time_bp = time_bp, time_steps = times ) + locations_data$time_bp_slice <- times[time_indeces] unique_time_indeces <- unique(time_indeces) } climate_brick <- terra::rast(this_file, subds = this_var_nc) diff --git a/R/region_slice.R b/R/region_slice.R index 9891eac1..2b480e1d 100644 --- a/R/region_slice.R +++ b/R/region_slice.R @@ -45,9 +45,9 @@ region_slice <- climate_for_time_slice <- function(...) { warning("DEPRECATED: use 'region_slice' instead") - if (!is.null(path_to_nc)) { - stop("the use of pastclimData is now deprecated,", - " use 'set_path_data' instead") - } + # if (!is.null(path_to_nc)) { + # stop("the use of pastclimData is now deprecated,", + # " use 'set_path_data' instead") + # } region_slice(...) } diff --git a/man/check_available_dataset.Rd b/man/check_available_dataset.Rd index 016d76b4..b48004ad 100644 --- a/man/check_available_dataset.Rd +++ b/man/check_available_dataset.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_available_datasets.R +% Please edit documentation in R/check_available_datasets.R \name{check_available_dataset} \alias{check_available_dataset} \title{Check if dataset is available.} diff --git a/man/check_dataset_path.Rd b/man/check_dataset_path.Rd index 8aacd693..66796845 100644 --- a/man/check_dataset_path.Rd +++ b/man/check_dataset_path.Rd @@ -11,7 +11,8 @@ check_dataset_path(dataset, path_to_nc) then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included in this file.} +reconstructions. All the variables of interest need to be included in +this file.} } \description{ Check that the dataset and path_to_nc parameters are valid diff --git a/man/check_var_downloaded.Rd b/man/check_var_downloaded.Rd index 6cf53f9a..7147ccc2 100644 --- a/man/check_var_downloaded.Rd +++ b/man/check_var_downloaded.Rd @@ -1,18 +1,16 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/get_downloaded_datasets.R +% Please edit documentation in R/check_var_downloaded.R \name{check_var_downloaded} \alias{check_var_downloaded} \title{Internal functions to check whether we have downloaded a given variable for a dataset} \usage{ -check_var_downloaded(variable, dataset, path_to_nc = NULL) +check_var_downloaded(variable, dataset) } \arguments{ \item{variable}{a vector of names of the variables of interest} \item{dataset}{dataset of interest} - -\item{path_to_nc}{path to the netcdf datasets} } \description{ Internal functions to check whether we have downloaded a given variable diff --git a/man/climate_for_locations.Rd b/man/climate_for_locations.Rd index fff8dbc0..2fe3b355 100644 --- a/man/climate_for_locations.Rd +++ b/man/climate_for_locations.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/climate_for_location.R +% Please edit documentation in R/location_slice.R \name{climate_for_locations} \alias{climate_for_locations} \title{Extract local climate for one or more locations for a given time slice.} diff --git a/man/climate_for_time_slice.Rd b/man/climate_for_time_slice.Rd index a0ad60d0..701b8315 100644 --- a/man/climate_for_time_slice.Rd +++ b/man/climate_for_time_slice.Rd @@ -1,22 +1,14 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/climate_for_time_slice.R +% Please edit documentation in R/region_slice.R \name{climate_for_time_slice} \alias{climate_for_time_slice} -\title{Extract a raster brick for a given time slice} +\title{Extract a climate slice for a region} \usage{ -climate_for_time_slice(time_bp, bio_variables, dataset, path_to_nc = NULL) +climate_for_time_slice(...) } \arguments{ -\item{time_bp}{time slice in years before present (negative)} - -\item{bio_variables}{vector of names of variables to be extracted} - -\item{dataset}{string defining the dataset to use} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion `pastclimData` -to store datasets.} +\item{...}{arguments to be passed to \code{region_slice}} } \description{ -This function extracts a raster brick for a given time slice. +Deprecated version of \code{region_slice} } diff --git a/man/get_data_path.Rd b/man/get_data_path.Rd index 65baf6d7..044d64ae 100644 --- a/man/get_data_path.Rd +++ b/man/get_data_path.Rd @@ -7,13 +7,14 @@ get_data_path() } \description{ -This function returns the path where climate reconstructions will be stored. This -information is stored in a file names "pastclim_data.txt", which is found -in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. +This function returns the path where climate reconstructions will be stored. +This information is stored in a file names "pastclim_data.txt", which +is found in the directory returned by +`tools::R_user_dir("pastclim","config")` (i.e. the default configuration directory for the package as set in R >= 4.0). } \details{ If this function is run before any path was set, it calls `set_data_path`, -which defaults to storing data in the directory returned by +which defaults to storing data in the directory returned by `tools::R_user_dir("pastclim","data")` } diff --git a/man/get_downloaded_datasets.Rd b/man/get_downloaded_datasets.Rd index dac48a2a..122537e6 100644 --- a/man/get_downloaded_datasets.Rd +++ b/man/get_downloaded_datasets.Rd @@ -4,10 +4,10 @@ \alias{get_downloaded_datasets} \title{Get the variables downloaded for each dataset.} \usage{ -get_downloaded_datasets(path_to_nc = NULL) +get_downloaded_datasets(data_path = NULL) } \arguments{ -\item{path_to_nc}{path to the netcdf datasets} +\item{data_path}{leave it to NULL to use the default datapath} } \description{ List the downloaded variable for each dataset. diff --git a/man/get_mis_time_steps.Rd b/man/get_mis_time_steps.Rd index b1885755..e5d85287 100644 --- a/man/get_mis_time_steps.Rd +++ b/man/get_mis_time_steps.Rd @@ -11,11 +11,12 @@ get_mis_time_steps(mis, dataset, path_to_nc = NULL) /code{mis_boundaries}} \item{dataset}{string defining dataset to be downloaded (a list of possible -values can be obtained with \code{get_available_datasets}). If set to "custom", -then a single nc file is used from "path_to_nc"} +values can be obtained with \code{get_available_datasets}). If set to +"custom", then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included in this file.} +reconstructions. All the variables of interest need to be included in +this file.} } \description{ Get the time steps available in a given dataset for a MIS. diff --git a/man/get_time_steps.Rd b/man/get_time_steps.Rd index 5f0166e9..96665737 100644 --- a/man/get_time_steps.Rd +++ b/man/get_time_steps.Rd @@ -8,11 +8,12 @@ get_time_steps(dataset, path_to_nc = NULL) } \arguments{ \item{dataset}{string defining dataset to be downloaded (a list of possible -values can be obtained with \code{get_available_datasets}). If set to "custom", -then a single nc file is used from "path_to_nc"} +values can be obtained with \code{get_available_datasets}). If set to +"custom", then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included in this file.} +reconstructions. All the variables of interest need to be included in +this file.} } \description{ Get the time steps available in a given dataset. diff --git a/man/location_slice.Rd b/man/location_slice.Rd index fb51c76b..9c819884 100644 --- a/man/location_slice.Rd +++ b/man/location_slice.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/climate_for_location.R +% Please edit documentation in R/location_slice.R \name{location_slice} \alias{location_slice} \title{Extract local climate for one or more locations for a given time slice.} @@ -17,15 +17,16 @@ location_slice( \item{x}{a 2 column matrix (with columns `longitude`, ranging -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} -\item{time_bp}{vector of ages, in years before present (negative).} - -\item{bio_variables}{vector of names of variables to be extracted.} +\item{time_bp}{the time slice in years before present (negative +values represent time before present, positive values time in the future). +#' @param bio_variables vector of names of variables to be extracted.} \item{dataset}{string defining the dataset to use (one of Beyer2020, Krapp2021, Example or custom).} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included in this file.} +reconstructions. All the variables of interest need to be included + in this file.} \item{nn_interpol}{boolean determining whether nearest neighbour interpolation is used to estimate climate for cells that lack such diff --git a/man/region_series.Rd b/man/region_series.Rd index 9844c306..3020fe62 100644 --- a/man/region_series.Rd +++ b/man/region_series.Rd @@ -7,7 +7,8 @@ region_series(time_bp, bio_variables, dataset, path_to_nc = NULL) } \arguments{ -\item{time_bp}{time slices in years before present (negative). The slices needs +\item{time_bp}{time slices in years before present (negative values represent +time before present, positive values time in the future). The slices need to exist in the dataset. To check which slices are available, you can use \code{get_time_steps}.} @@ -17,16 +18,12 @@ to exist in the dataset. To check which slices are available, you can use then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included in this file. - -location_slice -location_series -region_slice -region_series -slice_region_series} +reconstructions. All the variables of interest need to be included in +this file.} } \description{ -This function extracts a time series of one or more climate variables for a given +This function extracts a time series of one or more climate variables for +a given dataset covering a region (or the whole world). The function returns a SpatRasterDataset \code{terra::sds} object, with each variable as a sub-dataset. diff --git a/man/region_slice.Rd b/man/region_slice.Rd new file mode 100644 index 00000000..00973ed4 --- /dev/null +++ b/man/region_slice.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/region_slice.R +\name{region_slice} +\alias{region_slice} +\title{Extract a climate slice for a region} +\usage{ +region_slice(time_bp, bio_variables, dataset, path_to_nc = NULL) +} +\arguments{ +\item{time_bp}{the time slice in years before present (negative +values represent +time before present, positive values time in the future). The slice needs +to exist in the dataset. To check which slices are available, you can use +\code{get_time_steps}.} + +\item{bio_variables}{vector of names of variables to be extracted} + +\item{dataset}{string defining the dataset to use. If set to "custom", +then a single nc file is used from "path_to_nc"} + +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included +in this file.} +} +\description{ +This function extracts a slice of one or more climate variables for a given +dataset covering a region (or the whole world). The function returns a +SpatRaster \code{terra::SpatRaster} object, with +each variable as a layer. +} diff --git a/man/set_data_path.Rd b/man/set_data_path.Rd index 0fd08c2c..113240a6 100644 --- a/man/set_data_path.Rd +++ b/man/set_data_path.Rd @@ -8,11 +8,12 @@ set_data_path(path_to_nc = NULL) } \arguments{ \item{path_to_nc}{the path to the file that contains the downloaded -resonstructions. If left unset, the default location returned by +resonstructions. If left unset, the default location returned by `tools::R_user_dir("pastclim","data")` will be used} } \description{ -This function sets the path where climate reconstructions will be stored. This +This function sets the path where climate reconstructions will be stored. +This information is stored in a file names "pastclim_data.txt", which is found in the directory returned by `tools::R_user_dir("pastclim","config")` (i.e. the default configuration directory for the package as set in R >= 4.0). diff --git a/man/slice_region_series.Rd b/man/slice_region_series.Rd index d1667918..206db29c 100644 --- a/man/slice_region_series.Rd +++ b/man/slice_region_series.Rd @@ -9,11 +9,13 @@ slice_region_series(x, time_bp) \arguments{ \item{x}{time series generated with \code{region_series}} -\item{time_bp}{time slices in years before present (negative). The slices needs -to exist in the dataset. To check which slices are available, you can use -\code{terra::time(x[[1]])}} +\item{time_bp}{time slices in years before present (negative). The slices +need to exist in the dataset. To check which slices are available, you +can use \code{terra::time(x[[1]])} (note that you have to use +the `time` fuction on the first element of the 'sds' object, i.e. on one +of the 'spatRaster' objects)} } \description{ -This function extracts a time slice from time series of one or more climate +This function extracts a time slice from time series of one or more climate variables for a given dataset covering a region (or the whole world). } diff --git a/man/time_bp_to_index.Rd b/man/time_bp_to_index.Rd index 29feb18d..a960330d 100644 --- a/man/time_bp_to_index.Rd +++ b/man/time_bp_to_index.Rd @@ -4,12 +4,12 @@ \alias{time_bp_to_index} \title{Convert a time BP to indexes in a netcdf file.} \usage{ -time_bp_to_index(time_bp, path_to_nc) +time_bp_to_index(time_bp, time_steps) } \arguments{ \item{time_bp}{vector of times BP} -\item{path_to_nc}{path to nc file} +\item{time_steps}{time steps for which reconstructions are available} } \description{ Internal function diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index 68d09f99..1e4d78c5 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -130,7 +130,7 @@ Let us create some fictitious locations: ```{r} locations<-data.frame(longitude=c(0,90,-120,-7),latitude=c(20,45,60,37), - time_bp=c(0,-10000, -20000, -10000)) + time_bp=c(0,-9753, -18738, -11849)) locations ``` @@ -140,6 +140,9 @@ location_slice (x= locations[,c("longitude", "latitude")], time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), dataset="Example", nn_interpol = FALSE) ``` +`pastclim` finds the closest time steps (slices) available for a given date, and ouputs +the slice used in column `time_bp_slice` (the *Example* dataset that we use in this vignette has a temporal resolution of only 5k years). + Note that the last two locations, for the appropriate time steps, are not available (either under water or ice), and so `pastclim` does not return a climate reconstruction. In some instances, this is due to the discretisation of space in the raster. We can interpolate climate among the nearest neighbours, thus using climate reconstructions for neighbouring pixels if the location is just off one or more land pixels: ```{r} location_slice (x= locations[,c("longitude", "latitude")], @@ -245,10 +248,10 @@ get_biome_classes("Example") To plot it, we extract the biome and then subset it to just that class ```{r, fig.width=6, fig.height=5} -climate_20k <- climate_for_time_slice(-20000, +climate_20k <- region_slice(-20000, c("bio01","bio10","biome"), dataset = "Example") -climate_20k$ice<-climate_20k$biome +climate_20k$ice<-climate_20k$`biome_-20000` climate_20k$ice[climate_20k$ice!=28]<-FALSE climate_20k$ice[climate_20k$ice==28]<-TRUE terra::plot(climate_20k) @@ -256,7 +259,7 @@ terra::plot(climate_20k) Or more simply, we use functions designed to get ice and land masks. ```{r, fig.width=6, fig.height=5} -climate_20k <- climate_for_time_slice(-20000, +climate_20k <- region_slice(-20000, c("bio01","bio10"), dataset="Example") climate_20k$ice_mask<-get_ice_mask(-20000, dataset="Example") @@ -268,7 +271,7 @@ terra::plot(climate_20k) We can visualise the environment for this time step with a PCA: ```{r, fig.width=4, fig.height=4} -climate_10k <- climate_for_time_slice(-10000, +climate_10k <- region_slice(-10000, c("bio01","bio10","bio12"), dataset="Example") climate_values_10k <- values(climate_10k) @@ -281,7 +284,7 @@ plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", We can now get the climate variables for the locations at this time step, compute the PCA scores and plots the locations on top of the background climate: ```{r, fig.width=4, fig.height=4} locations_10k <- locations[locations$time_bp==-10000,] -locations_10k_climate<- climate_for_locations (x= locations_10k[,c("longitude", "latitude")], +locations_10k_climate<- location_slice (x= locations_10k[,c("longitude", "latitude")], time_bp = locations_10k$time_bp, bio_variables=c("bio01","bio10","bio12"), dataset="Example") locations_10k_pca_scores <- predict(climate_10k_pca,newdata= locations_10k_climate[,-c(1:2)]) @@ -303,7 +306,7 @@ And now cycle over those steps. First extract the climate into a list: ```{r} mis_climate_list<-list() for (this_step in mis_time_steps){ - this_step_climate <- climate_for_time_slice(this_step ,c("bio01","bio10","bio12"), + this_step_climate <- region_slice(this_step ,c("bio01","bio10","bio12"), dataset="Example") this_step_climate <- values(this_step_climate) this_step_climate <- this_step_climate[!is.nan(this_step_climate[,1]),] From 2ba2a451a5645f963720d5db3a40b9e53a0ed91a Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 14:50:56 +0100 Subject: [PATCH 15/83] Full update for all functions --- NAMESPACE | 2 +- R/location_series.R | 115 ++++++++++++++++++ R/location_slice.R | 22 ++-- R/region_series.R | 23 ++-- R/slice_region_series.R | 2 +- R/time_bp_series.R | 34 ++++++ R/time_series_for_location.R | 67 ---------- man/climate_for_locations.Rd | 8 +- man/location_series.Rd | 33 +++++ man/location_slice.Rd | 13 +- man/region_series.Rd | 9 +- man/time_bp_series.Rd | 17 +++ man/time_series_for_locations.Rd | 24 ---- ..._for_location.R => test_location_series.R} | 47 ++++--- tests/testthat/test_region_series.R | 13 ++ tests/testthat/test_time_bp_series.R | 30 +++++ vignettes/pastclim_overview.Rmd | 68 ++++++----- 17 files changed, 357 insertions(+), 170 deletions(-) create mode 100644 R/location_series.R create mode 100644 R/time_bp_series.R delete mode 100644 R/time_series_for_location.R create mode 100644 man/location_series.Rd create mode 100644 man/time_bp_series.Rd delete mode 100644 man/time_series_for_locations.Rd rename tests/testthat/{test_time_series_for_location.R => test_location_series.R} (67%) create mode 100644 tests/testthat/test_time_bp_series.R diff --git a/NAMESPACE b/NAMESPACE index 5e6ca054..06db3d2e 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,10 +13,10 @@ export(get_land_mask) export(get_mis_time_steps) export(get_time_steps) export(get_vars_for_dataset) +export(location_series) export(location_slice) export(region_series) export(region_slice) export(set_data_path) export(slice_region_series) -export(time_series_for_locations) import(terra) diff --git a/R/location_series.R b/R/location_series.R new file mode 100644 index 00000000..3e54e2e8 --- /dev/null +++ b/R/location_series.R @@ -0,0 +1,115 @@ +#' Extract a time series of bioclimatic variables for one or more locations. +#' +#' This function extract a time series of local climate for +#' a set of locations +#' +#' @param x a 2 column matrix (with columns `longitude`, ranging +#' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. +#' @param time_bp time slices in years before present (negative values represent +#' time before present, positive values time in the future). This parameter can +#' be a vector of times (the slices need +#' to exist in the dataset), a list with a min and max element setting the +#' range of values, or left to NULL to retrieve all time steps. +#' To check which slices are available, you can use +#' \code{get_time_steps}. +#' @param bio_variables vector of names of variables to be extracted. +#' @param dataset string defining the dataset to use. If set to "custom", +#' then a single nc file is used from "path_to_nc" +#' @param path_to_nc the path to the custom nc file containing the paleoclimate +#' reconstructions. All the variables of interest need to be included in +#' this file. +#' +#' @export + +location_series <- + function(x, + time_bp = NULL, + bio_variables, + dataset, + path_to_nc = NULL) { + + check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) + + # if we are using standard datasets, check whether a variables exists + if (dataset != "custom") { + check_var_downloaded(bio_variables, dataset) + } else { # else check that the variables exist in the custom nc + check_var_in_nc(bio_variables, path_to_nc) + } + + # reorder the inputs by time + if (inherits(x, "data.frame")) { + locations_data <- x + } else { + locations_data <- data.frame(cell_number = x) + } + + time_series_df <- locations_data + time_series_df$id <- seq_len(nrow(time_series_df)) + time_index <- NULL + for (this_var in bio_variables) { + # get name of file that contains this variable + if (dataset != "custom") { + this_file <- get_file_for_dataset(this_var, dataset)$file_name + this_file <- file.path(get_data_path(), this_file) + this_var_nc <- get_varname(variable = this_var, dataset = dataset) + } else { + this_file <- file.path(path_to_nc) + this_var_nc <- this_var + } + + # figure out the time indeces the first time we run this + if (is.null(time_index)) { + # as we have the file name, we can us the same code for custom and + # standard datasets. + times <- get_time_steps(dataset = "custom", path_to_nc = this_file) + time_index <- time_bp_series(time_bp = time_bp, + time_steps = times) + } + + climate_brick_temp <- terra::rast(this_file, subds = this_var_nc) + if (!is.null(time_bp)){ + climate_brick <- terra::subset(climate_brick_temp, + subset = time_index) + } else { + climate_brick <- climate_brick_temp + } + # add time var if it doesn't exist yet + if (!("time" %in% names(time_series_df))) { + n_time_steps <- length(time(climate_brick)) + n_locations <- nrow(time_series_df) + time_series_df <- time_series_df[rep( + seq_len(nrow(time_series_df)), + n_time_steps + ), ] + time_series_df$time <- rep(time(climate_brick), each = n_locations) + } + this_var_ts <- terra::extract(climate_brick, x) + names(this_var_ts)[-1] <- terra::time(climate_brick) + time_series_df[this_var] <- utils::stack(this_var_ts, select = -ID)$values + } + return(time_series_df) + } + + + +#' Extract a time series of bioclimatic variables for one or more locations. +#' +#' Deprecated version of \code{location_series} +#' +#' @param ... arguments to be passed to \code{series} +#' +#' @export + +climate_for_locations <- function(...) { + warning("DEPRECATED: use 'location_slice' instead") + # if (!is.null(path_to_nc)) { + # stop( + # "the use of pastclimData is now deprecated", + # "use 'set_path_data' instead" + # ) + # } + location_slice(...) +} + + diff --git a/R/location_slice.R b/R/location_slice.R index ce285ecc..9c48930c 100644 --- a/R/location_slice.R +++ b/R/location_slice.R @@ -8,12 +8,12 @@ #' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. #' @param time_bp the time slice in years before present (negative #' values represent time before present, positive values time in the future). -#' #' @param bio_variables vector of names of variables to be extracted. -#' @param dataset string defining the dataset to use (one of Beyer2020, -#' Krapp2021, Example or custom). +#' @param bio_variables vector of names of variables to be extracted. +#' @param dataset string defining the dataset to use. If set to "custom", +#' then a single nc file is used from "path_to_nc" #' @param path_to_nc the path to the custom nc file containing the paleoclimate -#' reconstructions. All the variables of interest need to be included -#' in this file. +#' reconstructions. All the variables of interest need to be included in +#' this file. #' @param nn_interpol boolean determining whether nearest neighbour #' interpolation is used to estimate climate for cells that lack such #' information (i.e. they are under water or ice). Interpolation is only @@ -131,11 +131,11 @@ location_slice <- climate_for_locations <- function(...) { warning("DEPRECATED: use 'location_slice' instead") - if (!is.null(path_to_nc)) { - stop( - "the use of pastclimData is now deprecated", - "use 'set_path_data' instead" - ) - } + # if (!is.null(path_to_nc)) { + # stop( + # "the use of pastclimData is now deprecated", + # "use 'set_path_data' instead" + # ) + # } location_slice(...) } diff --git a/R/region_series.R b/R/region_series.R index dd4a8f05..61c0fd4c 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -7,8 +7,11 @@ #' each variable as a sub-dataset. #' #' @param time_bp time slices in years before present (negative values represent -#' time before present, positive values time in the future). The slices need -#' to exist in the dataset. To check which slices are available, you can use +#' time before present, positive values time in the future). This parameter can +#' be a vector of times (the slices need +#' to exist in the dataset), a list with a min and max element setting the +#' range of values, or left to NULL to retrieve all time steps. +#' To check which slices are available, you can use #' \code{get_time_steps}. #' @param bio_variables vector of names of variables to be extracted #' @param dataset string defining the dataset to use. If set to "custom", @@ -21,7 +24,7 @@ #' @export region_series <- - function(time_bp, + function(time_bp = NULL, bio_variables, dataset, path_to_nc = NULL) { @@ -52,14 +55,16 @@ region_series <- # as we have the file name, we can us the same code for custom and # standard datasets. times <- get_time_steps(dataset = "custom", path_to_nc = this_file) - time_index <- match(time_bp, times) - if (any(is.na(time_index))) { - stop("time_bp should only include time steps available in the dataset") - } + time_index <- time_bp_series(time_bp = time_bp, + time_steps = times) } var_brick <- terra::rast(this_file, subds = this_var_nc) - climate_spatrasters[[this_var]] <- terra::subset(var_brick, - subset = time_index) + if (!is.null(time_bp)){ + climate_spatrasters[[this_var]] <- terra::subset(var_brick, + subset = time_index) + } else { + climate_spatrasters[[this_var]] <- var_brick + } varnames(climate_spatrasters[[this_var]]) <- this_var names(climate_spatrasters[[this_var]]) <- paste(this_var, time(climate_spatrasters[[this_var]]), diff --git a/R/slice_region_series.R b/R/slice_region_series.R index cfc17c40..42fe2ad3 100644 --- a/R/slice_region_series.R +++ b/R/slice_region_series.R @@ -30,6 +30,6 @@ slice_region_series <- function(x, time_bp) { terra::add(climate_spatraster) <- subset(x[[i]], time_index) } } - # names(climate_spatraster) <- varnames(climate_spatraster) #<- names(x) + names(climate_spatraster) <- varnames(climate_spatraster) #<- names(x) return(climate_spatraster) } diff --git a/R/time_bp_series.R b/R/time_bp_series.R new file mode 100644 index 00000000..70f92038 --- /dev/null +++ b/R/time_bp_series.R @@ -0,0 +1,34 @@ +#' Convert a time BP to indexes for a series +#' +#' Internal function +#' +#' @param time_bp vector of times BP +#' @param time_steps time steps for which reconstructions are available +#' +#' @keywords internal + +time_bp_series<-function(time_bp, time_steps){ + # if this is a vector, just use the values + if (is.numeric(time_bp)){ + time_index <- match(time_bp, time_steps) + if (any(is.na(time_index))) { + stop("time_bp should only include time steps available in the dataset") + } + # if this is a list + } else if (inherits(time_bp,"list")){ + if (!all(names(time_bp)==c("min","max"))){ + stop("time_bp should be a list with min and max elements") + } + if (time_bp$min>time_bp$max){ + stop("in time_bp, min should be less than max") + } + time_bp <- time_steps[time_steps>=time_bp$min & time_steps<=time_bp$max] + time_index <- match(time_bp, time_steps) + # finally give an error if this is not null + } else if (is.null(time_bp)){ + time_index <- NULL + } else { + stop ("time_bp can only be NULL, a numeric vector, or a list with min and max values") + } + return(time_index) +} \ No newline at end of file diff --git a/R/time_series_for_location.R b/R/time_series_for_location.R deleted file mode 100644 index 0ddc7919..00000000 --- a/R/time_series_for_location.R +++ /dev/null @@ -1,67 +0,0 @@ -#' Extract a time series of bioclimatic variables for one or more locations. -#' -#' This function extract a time series of local climate for -#' a set of locations -#' -#' @param x a 2 column matrix (with columns `longitude`, ranging -#' -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers. -#' @param bio_variables vector of names of variables to be extracted. -#' @param dataset string defining the dataset to use. -#' @param path_to_nc the path to the directory containing the downloaded -#' reconstructions. Leave it unset if you are using the companion -#' `pastclimData` to store datasets. -#' -#' @export - -time_series_for_locations <- - function(x, - bio_variables, - dataset, - path_to_nc = NULL) { - - # if we are using standard datasets, check whether a variables exists - if (dataset != "custom") { - check_var_downloaded(bio_variables, dataset, path_to_nc) - } else { # else check that the variables exist in the custom nc - check_var_in_nc(bio_variables, path_to_nc) - } - - if (is.null(path_to_nc)) { - path_to_nc <- get_data_path() - } - - # reorder the inputs by time - if (inherits(x, "data.frame")) { - locations_data <- x - } else { - locations_data <- data.frame(cell_number = x) - } - time_series_df <- locations_data - time_series_df$id <- seq_len(nrow(time_series_df)) - for (this_var in bio_variables) { - # get name of file that contains this variable - if (dataset != "custom") { - this_file <- get_file_for_dataset(this_var, dataset)$file_name - this_file <- file.path(path_to_nc, this_file) - this_var_nc <- get_varname(variable = this_var, dataset = dataset) - } else { - this_file <- file.path(path_to_nc) - this_var_nc <- this_var - } - climate_brick <- terra::rast(this_file, subds = this_var_nc) - # add time var if it doesn't exist yet - if (!("time" %in% names(time_series_df))) { - n_time_steps <- length(time(climate_brick)) - n_locations <- nrow(time_series_df) - time_series_df <- time_series_df[rep( - seq_len(nrow(time_series_df)), - n_time_steps - ), ] - time_series_df$time <- rep(time(climate_brick), each = n_locations) - } - this_var_ts <- terra::extract(climate_brick, x) - names(this_var_ts)[-1] <- terra::time(climate_brick) - time_series_df[this_var] <- utils::stack(this_var_ts, select = -ID)$values - } - return(time_series_df) - } diff --git a/man/climate_for_locations.Rd b/man/climate_for_locations.Rd index 2fe3b355..f1e348a5 100644 --- a/man/climate_for_locations.Rd +++ b/man/climate_for_locations.Rd @@ -1,14 +1,18 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/location_slice.R +% Please edit documentation in R/location_series.R, R/location_slice.R \name{climate_for_locations} \alias{climate_for_locations} -\title{Extract local climate for one or more locations for a given time slice.} +\title{Extract a time series of bioclimatic variables for one or more locations.} \usage{ +climate_for_locations(...) + climate_for_locations(...) } \arguments{ \item{...}{arguments to be passed to \code{location_slice}} } \description{ +Deprecated version of \code{location_series} + Deprecated version of \code{location_slice} } diff --git a/man/location_series.Rd b/man/location_series.Rd new file mode 100644 index 00000000..f79c1079 --- /dev/null +++ b/man/location_series.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/location_series.R +\name{location_series} +\alias{location_series} +\title{Extract a time series of bioclimatic variables for one or more locations.} +\usage{ +location_series(x, time_bp = NULL, bio_variables, dataset, path_to_nc = NULL) +} +\arguments{ +\item{x}{a 2 column matrix (with columns `longitude`, ranging +-180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} + +\item{time_bp}{time slices in years before present (negative values represent +time before present, positive values time in the future). This parameter can +be a vector of times (the slices need +to exist in the dataset), a list with a min and max element setting the +range of values, or left to NULL to retrieve all time steps. +To check which slices are available, you can use +\code{get_time_steps}.} + +\item{bio_variables}{vector of names of variables to be extracted.} + +\item{dataset}{string defining the dataset to use. If set to "custom", +then a single nc file is used from "path_to_nc"} + +\item{path_to_nc}{the path to the custom nc file containing the paleoclimate +reconstructions. All the variables of interest need to be included in +this file.} +} +\description{ +This function extract a time series of local climate for + a set of locations +} diff --git a/man/location_slice.Rd b/man/location_slice.Rd index 9c819884..1662231a 100644 --- a/man/location_slice.Rd +++ b/man/location_slice.Rd @@ -18,15 +18,16 @@ location_slice( -180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} \item{time_bp}{the time slice in years before present (negative -values represent time before present, positive values time in the future). -#' @param bio_variables vector of names of variables to be extracted.} +values represent time before present, positive values time in the future).} -\item{dataset}{string defining the dataset to use (one of Beyer2020, -Krapp2021, Example or custom).} +\item{bio_variables}{vector of names of variables to be extracted.} + +\item{dataset}{string defining the dataset to use. If set to "custom", +then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate -reconstructions. All the variables of interest need to be included - in this file.} +reconstructions. All the variables of interest need to be included in +this file.} \item{nn_interpol}{boolean determining whether nearest neighbour interpolation is used to estimate climate for cells that lack such diff --git a/man/region_series.Rd b/man/region_series.Rd index 3020fe62..58b88e0b 100644 --- a/man/region_series.Rd +++ b/man/region_series.Rd @@ -4,12 +4,15 @@ \alias{region_series} \title{Extract a time series of climate variables for a region} \usage{ -region_series(time_bp, bio_variables, dataset, path_to_nc = NULL) +region_series(time_bp = NULL, bio_variables, dataset, path_to_nc = NULL) } \arguments{ \item{time_bp}{time slices in years before present (negative values represent -time before present, positive values time in the future). The slices need -to exist in the dataset. To check which slices are available, you can use +time before present, positive values time in the future). This parameter can +be a vector of times (the slices need +to exist in the dataset), a list with a min and max element setting the +range of values, or left to NULL to retrieve all time steps. +To check which slices are available, you can use \code{get_time_steps}.} \item{bio_variables}{vector of names of variables to be extracted} diff --git a/man/time_bp_series.Rd b/man/time_bp_series.Rd new file mode 100644 index 00000000..93a49836 --- /dev/null +++ b/man/time_bp_series.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/time_bp_series.R +\name{time_bp_series} +\alias{time_bp_series} +\title{Convert a time BP to indexes for a series} +\usage{ +time_bp_series(time_bp, time_steps) +} +\arguments{ +\item{time_bp}{vector of times BP} + +\item{time_steps}{time steps for which reconstructions are available} +} +\description{ +Internal function +} +\keyword{internal} diff --git a/man/time_series_for_locations.Rd b/man/time_series_for_locations.Rd deleted file mode 100644 index 5530a656..00000000 --- a/man/time_series_for_locations.Rd +++ /dev/null @@ -1,24 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/time_series_for_location.R -\name{time_series_for_locations} -\alias{time_series_for_locations} -\title{Extract a time series of bioclimatic variables for one or more locations.} -\usage{ -time_series_for_locations(x, bio_variables, dataset, path_to_nc = NULL) -} -\arguments{ -\item{x}{a 2 column matrix (with columns `longitude`, ranging --180 to 180, and `latitude`, from -90 to 90), or a vector of cell numbers.} - -\item{bio_variables}{vector of names of variables to be extracted.} - -\item{dataset}{string defining the dataset to use.} - -\item{path_to_nc}{the path to the directory containing the downloaded -reconstructions. Leave it unset if you are using the companion -`pastclimData` to store datasets.} -} -\description{ -This function extract a time series of local climate for - a set of locations -} diff --git a/tests/testthat/test_time_series_for_location.R b/tests/testthat/test_location_series.R similarity index 67% rename from tests/testthat/test_time_series_for_location.R rename to tests/testthat/test_location_series.R index 520eee9c..d779758c 100644 --- a/tests/testthat/test_time_series_for_location.R +++ b/tests/testthat/test_location_series.R @@ -1,50 +1,65 @@ test_that("time_series_for_location", { # using standard dataset - path_to_example_nc <- system.file("/extdata/", package = "pastclim") locations <- data.frame( - longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), - time_bp = c(0, -10000, -20000, -10000) + longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37) ) - locations_ts <- time_series_for_locations( + locations_ts <- location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("bio01", "bio12"), - dataset = "Example", path_to_nc = path_to_example_nc + dataset = "Example" ) expect_true(nrow(locations_ts) == 20) + + locations_ts <- location_series( + x = locations[, c("longitude", "latitude")], + time_bp = c(-20000,-10000,-5000), + bio_variables = c("bio01", "bio12"), + dataset = "Example" + ) + expect_true(nrow(locations_ts) == 12) + + locations_ts <- location_series( + x = locations[, c("longitude", "latitude")], + time_bp = list(min = -10000, max = -5000), + bio_variables = c("bio01", "bio12"), + dataset = "Example" + ) + expect_true(nrow(locations_ts) == 8) + # now test if we try a variable that is not available expect_error( - time_series_for_locations( + location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("bio01", "bio19"), - dataset = "Example", path_to_nc = path_to_example_nc + dataset = "Example" ), "bio19 not available" ) expect_error( - time_series_for_locations( + location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("bio01", "bio19", "bio20"), - dataset = "Example", path_to_nc = path_to_example_nc + dataset = "Example" ), "bio19, bio20 not available" ) # now test if we try to use a variable that we have not downloaded yet expect_error( - time_series_for_locations( + location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("bio01", "bio12"), - dataset = "Krapp2021", path_to_nc = path_to_example_nc + dataset = "Krapp2021" ), "variable \\(bio01, bio12\\) not yet downloaded" ) - # now treat it as if it was a custom dataset + # now use a custom dataset path_to_example_nc <- system.file("/extdata/example_climate.nc", package = "pastclim" ) - locations_ts <- time_series_for_locations( + locations_ts <- location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("BIO1", "BIO12"), dataset = "custom", path_to_nc = path_to_example_nc @@ -52,7 +67,7 @@ test_that("time_series_for_location", { expect_true(nrow(locations_ts) == 20) # if we try to use a variable that does not exist expect_error( - time_series_for_locations( + location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("BIO1", "BIO22"), dataset = "custom", path_to_nc = path_to_example_nc @@ -61,11 +76,11 @@ test_that("time_series_for_location", { ) # if we try to use a file that does not exist expect_error( - time_series_for_locations( + location_series( x = locations[, c("longitude", "latitude")], bio_variables = c("BIO1", "BIO22"), dataset = "custom", path_to_nc = "/foo" ), - "file /foo does not exist" + "path_to_nc does not point to a file" ) }) diff --git a/tests/testthat/test_region_series.R b/tests/testthat/test_region_series.R index caed33d2..1befe871 100644 --- a/tests/testthat/test_region_series.R +++ b/tests/testthat/test_region_series.R @@ -50,4 +50,17 @@ test_that("region series", { ), "time_bp should only include time steps available in the dataset" ) + + # get all values + climate_region <- region_series(bio_variables = c("bio01", "bio12"), + dataset = "Example" + ) + expect_true(all(terra::nlyr(climate_region) == c(5, 5))) + + # get all values + climate_region <- region_series( + time_bp = list(min=-13000,max=0), c("bio01", "bio12"), + "Example" + ) + expect_true(all(terra::nlyr(climate_region) == c(3, 3))) }) diff --git a/tests/testthat/test_time_bp_series.R b/tests/testthat/test_time_bp_series.R new file mode 100644 index 00000000..f69e40cb --- /dev/null +++ b/tests/testthat/test_time_bp_series.R @@ -0,0 +1,30 @@ +test_that("time_bp_series returns correct values", { + time_bp <- c(-10000,-20000) + time_steps <- sort(-seq(0,20000,by = 5000)) + ##################################################### + # if time_bp is a vector + ##################################################### + # correctly working + expect_true(all(time_bp_series(time_bp,time_steps)==c(3,1))) + # incorrect values + time_bp <- c(-9000,-20000) + expect_error(time_bp_series(time_bp,time_steps), + "time_bp should only include time steps available in the dataset") + ##################################################### + # if time_bp is a list + ##################################################### + time_bp <- list(min=-15000, max=-3000) + expect_true(all(time_bp_series(time_bp,time_steps)==c(2,3,4))) + time_bp <- list(min=-3000, max=-15000) + expect_error(time_bp_series(time_bp,time_steps), + "in time_bp, min should be less than max") + ##################################################### + # if time_bp is null + ##################################################### + expect_true(is.null(time_bp_series(NULL,time_steps))) + ##################################################### + # if time_bp is nonsense + ##################################################### + expect_error(time_bp_series("blah",time_steps), + "time_bp can only be") +}) \ No newline at end of file diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index 1e4d78c5..bf845933 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -154,7 +154,7 @@ In this case, the last location is indeed just off the coast, and so we can reco Sometimes, we want to get a time series of climatic reconstructions, thus allowing us to see how climate changed over time: ```{r} -locations_ts <- time_series_for_locations(x= locations[,c("longitude", "latitude")], +locations_ts <- location_series(x= locations[,c("longitude", "latitude")], bio_variables=c("bio01","bio12"), dataset="Example") ``` @@ -251,7 +251,7 @@ To plot it, we extract the biome and then subset it to just that class climate_20k <- region_slice(-20000, c("bio01","bio10","biome"), dataset = "Example") -climate_20k$ice<-climate_20k$`biome_-20000` +climate_20k$ice<-climate_20k$biome climate_20k$ice[climate_20k$ice!=28]<-FALSE climate_20k$ice[climate_20k$ice==28]<-TRUE terra::plot(climate_20k) @@ -259,11 +259,11 @@ terra::plot(climate_20k) Or more simply, we use functions designed to get ice and land masks. ```{r, fig.width=6, fig.height=5} -climate_20k <- region_slice(-20000, - c("bio01","bio10"), - dataset="Example") -climate_20k$ice_mask<-get_ice_mask(-20000, dataset="Example") -climate_20k$land_mask <- get_land_mask(-20000, dataset="Example") +climate_20k <- region_slice(time_bp = -20000, + c("bio01","bio10"), + dataset = "Example") +climate_20k$ice_mask<-get_ice_mask(-20000, dataset = "Example") +climate_20k$land_mask <- get_land_mask(-20000, dataset = "Example") terra::plot(climate_20k) ``` @@ -272,8 +272,8 @@ terra::plot(climate_20k) We can visualise the environment for this time step with a PCA: ```{r, fig.width=4, fig.height=4} climate_10k <- region_slice(-10000, - c("bio01","bio10","bio12"), - dataset="Example") + c("bio01","bio10","bio12"), + dataset="Example") climate_values_10k <- values(climate_10k) climate_values_10k <- climate_values_10k[!is.nan(climate_values_10k[,1]),] climate_10k_pca<-prcomp(climate_values_10k,scale=TRUE, center=TRUE) @@ -283,11 +283,14 @@ plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", We can now get the climate variables for the locations at this time step, compute the PCA scores and plots the locations on top of the background climate: ```{r, fig.width=4, fig.height=4} -locations_10k <- locations[locations$time_bp==-10000,] +locations_10k <-location_slice (x= locations[,c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), + dataset="Example") +locations_10k <- locations_10k[locations_10k$time_bp_slice == -10000,] locations_10k_climate<- location_slice (x= locations_10k[,c("longitude", "latitude")], time_bp = locations_10k$time_bp, bio_variables=c("bio01","bio10","bio12"), dataset="Example") -locations_10k_pca_scores <- predict(climate_10k_pca,newdata= locations_10k_climate[,-c(1:2)]) +locations_10k_pca_scores <- predict(climate_10k_pca,newdata= locations_10k_climate[,-c(1:4)]) plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", xlab="PC1",ylab="PC2") points(locations_10k_pca_scores,pch=20,col="red") @@ -304,13 +307,16 @@ mis_time_steps And now cycle over those steps. First extract the climate into a list: ```{r} -mis_climate_list<-list() -for (this_step in mis_time_steps){ - this_step_climate <- region_slice(this_step ,c("bio01","bio10","bio12"), - dataset="Example") -this_step_climate <- values(this_step_climate) -this_step_climate <- this_step_climate[!is.nan(this_step_climate[,1]),] - mis_climate_list[[as.character(this_step)]]<- this_step_climate +mis_climate_list <- list() +for (this_step in mis_time_steps) { + this_step_climate <- + region_slice(time_bp = this_step , + bio_variables = c("bio01", "bio10", "bio12"), + dataset = "Example") + this_step_climate <- values(this_step_climate) + this_step_climate <- + this_step_climate[!is.nan(this_step_climate[, 1]), ] + mis_climate_list[[as.character(this_step)]] <- this_step_climate } ``` @@ -332,27 +338,29 @@ plot(mis_pca$x[,2]~mis_pca$x[,1],pch=20,col="lightgray", For a number of species distribution models, we need to subsample background points to our presences. We can simply do that by generating the raster for the time slice of interest, and using the appropriate function from `terra`: ```{r} -climate_20k <- climate_for_time_slice(-20000,c("bio01","bio10"), - dataset="Example") -this_sample<-terra::spatSample(climate_20k, 100,na.rm=TRUE,cells=TRUE,xy=TRUE) +climate_20k <- region_slice(time_bp = -20000, + bio_variables = c("bio01", "bio10"), + dataset = "Example") +this_sample<-terra::spatSample(climate_20k, 100, na.rm = TRUE, + cells = TRUE,xy = TRUE) head(this_sample) ``` If we needed to get other variables for those coordinates, we could simply: ```{r} -additional_var <- climate_for_locations ( - x= data.frame(longitude = this_sample$x, latitude = this_sample$y), - time_bp = rep(-20000,nrow(this_sample)), - bio_variables=c("bio12"), - dataset="Example") +additional_var <- location_slice ( + x = data.frame(longitude = this_sample$x, latitude = this_sample$y), + time_bp = rep(-20000, nrow(this_sample)), + bio_variables = c("bio12"), + dataset = "Example") head(additional_var) ``` Alternatively, we could use the cell number: ```{r} -additional_var <- climate_for_locations (x= this_sample$cell, - time_bp = rep(-20000,nrow(this_sample)), - bio_variables=c("bio12"), - dataset="Example") +additional_var <- location_slice (x = this_sample$cell, + time_bp = rep(-20000, nrow(this_sample)), + bio_variables = c("bio12"), + dataset = "Example") head(additional_var) ``` From 34caa7d666500f75728bf44ef5ff358b6135d138 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 14:54:48 +0100 Subject: [PATCH 16/83] Minor edits to vignette [skip ci] --- vignettes/pastclim_overview.Rmd | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index bf845933..acc18454 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -248,12 +248,12 @@ get_biome_classes("Example") To plot it, we extract the biome and then subset it to just that class ```{r, fig.width=6, fig.height=5} -climate_20k <- region_slice(-20000, - c("bio01","bio10","biome"), - dataset = "Example") +climate_20k <- region_slice(time_bp = -20000, + bio_variables = c("bio01","bio10","biome"), + dataset = "Example") climate_20k$ice<-climate_20k$biome -climate_20k$ice[climate_20k$ice!=28]<-FALSE -climate_20k$ice[climate_20k$ice==28]<-TRUE +climate_20k$ice[climate_20k$ice!=28] <- FALSE +climate_20k$ice[climate_20k$ice==28] <- TRUE terra::plot(climate_20k) ``` From 36335689272b7f8901a2c2032b100705c258b3cb Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 15:03:42 +0100 Subject: [PATCH 17/83] Style the vignette --- vignettes/pastclim_overview.Rmd | 191 +++++++++++++++++++------------- 1 file changed, 112 insertions(+), 79 deletions(-) diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index acc18454..27db303e 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -32,7 +32,7 @@ devtools::install_github("EvolEcolGroup/pastclim", build_vignettes = TRUE) ``` And read it directly in R with: ```{r vignette, eval=FALSE} -vignette("pastclim_overview",package="pastclim") +vignette("pastclim_overview", package = "pastclim") ``` Depending on the operating system you use, you might need additional packages to build a vignette. @@ -81,25 +81,25 @@ Let us start by inspecting the *Example* dataset. We can get a list of variables available for this dataset with: ```{r} -get_vars_for_dataset(dataset="Example") +get_vars_for_dataset(dataset = "Example") ``` and the available time steps available can be obtained with: ```{r} -get_time_steps(dataset="Example") +get_time_steps(dataset = "Example") ``` For "Beyer2020" and "Krapp2021", you can get a list of available variables for each dataset with: ```{r} -get_vars_for_dataset(dataset="Beyer2020") +get_vars_for_dataset(dataset = "Beyer2020") ``` and ```{r} -get_vars_for_dataset(dataset="Krapp2021") +get_vars_for_dataset(dataset = "Krapp2021") ``` You will not be able to get the available time steps until you download the data. `pastclim` offers an interface @@ -115,7 +115,7 @@ Let's now download `bio01` and `bio05` for the *Beyer2020* dataset (this operati the download is complete): ```{r eval=FALSE} -download_dataset(dataset="Beyer2020", bio_variables = c("bio01","bio05")) +download_dataset(dataset = "Beyer2020", bio_variables = c("bio01", "bio05")) ``` Note that multiple variables are packed together into a single file, so the command might list more variables than the ones that we downloaded originally. @@ -129,47 +129,55 @@ for the times relevant to the locations of interest. Let us create some fictitious locations: ```{r} -locations<-data.frame(longitude=c(0,90,-120,-7),latitude=c(20,45,60,37), - time_bp=c(0,-9753, -18738, -11849)) +locations <- data.frame( + longitude = c(0, 90, -120, -7), latitude = c(20, 45, 60, 37), + time_bp = c(0, -9753, -18738, -11849) +) locations ``` And extract their climatic conditions for `bio01` and `bio12`: ```{r} -location_slice (x= locations[,c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), - dataset="Example", nn_interpol = FALSE) +location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = FALSE +) ``` `pastclim` finds the closest time steps (slices) available for a given date, and ouputs the slice used in column `time_bp_slice` (the *Example* dataset that we use in this vignette has a temporal resolution of only 5k years). Note that the last two locations, for the appropriate time steps, are not available (either under water or ice), and so `pastclim` does not return a climate reconstruction. In some instances, this is due to the discretisation of space in the raster. We can interpolate climate among the nearest neighbours, thus using climate reconstructions for neighbouring pixels if the location is just off one or more land pixels: ```{r} -location_slice (x= locations[,c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), - dataset="Example", nn_interpol = TRUE) +location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example", nn_interpol = TRUE +) ``` In this case, the last location is indeed just off the coast, and so we can reconstruct some appropriate climate by interpolating. Note that `nn_interpol = TRUE` is the default for this function. Sometimes, we want to get a time series of climatic reconstructions, thus allowing us to see how climate changed over time: ```{r} -locations_ts <- location_series(x= locations[,c("longitude", "latitude")], - bio_variables=c("bio01","bio12"), - dataset="Example") +locations_ts <- location_series( + x = locations[, c("longitude", "latitude")], + bio_variables = c("bio01", "bio12"), + dataset = "Example" +) ``` The resulting dataframe can be subsetted to get the time series for each location (note that the small *Example* dataset only contains 5 time points): ```{r} -subset(locations_ts,id==1) +subset(locations_ts, id == 1) ``` Also note that the last two locations do not return information for all time steps. That's due to the change in sea level and icesheet extent, and this function (as opposed to `climate_for_locations`) does not allow for interpolation. ```{r} -subset(locations_ts,id==3) +subset(locations_ts, id == 3) ``` ```{r} -subset(locations_ts,id==4) +subset(locations_ts, id == 4) ``` # Get climate for a region @@ -178,9 +186,11 @@ Instead of focussing on specific locations, we might want to look a whole region For a given time step, we can extract a slice of climate with ```{r} -climate_20k <- region_slice(time_bp = -20000, - c("bio01","bio10","bio12"), - dataset="Example") +climate_20k <- region_slice( + time_bp = -20000, + c("bio01", "bio10", "bio12"), + dataset = "Example" +) ``` This returns a raster (technically a `spatRaster` object as defined in the `terra` @@ -204,7 +214,7 @@ region_extent ``` We can crop the raster to Europe: ```{r, fig.width=6, fig.height=5} -europe_climate_20k <- terra::crop(climate_20k,terra::ext(region_extent$Europe)) +europe_climate_20k <- terra::crop(climate_20k, terra::ext(region_extent$Europe)) terra::plot(europe_climate_20k) ``` @@ -220,21 +230,21 @@ copy over the `crs` from the raster to the polygon). It is also helpful to crop the extent of the raster to that of the polygon (to avoid having a large amount of blank space). ```{r, fig.width=6, fig.height=5} -afr_eurasia_vec<- terra::vect("POLYGON ((0 70, 25 70, 50 80, 170 80, 170 10, +afr_eurasia_vec <- terra::vect("POLYGON ((0 70, 25 70, 50 80, 170 80, 170 10, 119 2.4, 119 0.8, 116 -7.6, 114 -12, 100 -40, -25 -40, -25 64, 0 70))") -terra::crs(afr_eurasia_vec)<-terra::crs(climate_20k) +terra::crs(afr_eurasia_vec) <- terra::crs(climate_20k) climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) -climate_20k_afr_eurasia <- terra::crop(climate_20k_afr_eurasia,afr_eurasia_vec) +climate_20k_afr_eurasia <- terra::crop(climate_20k_afr_eurasia, afr_eurasia_vec) terra::plot(climate_20k_afr_eurasia) ``` `pastclim` includes a number of pre-generated masks for the main continental masses, stored in the dataset `continent_outlines` in an `sf` object. Note that outlines that cross the antimeridian are split into multiple polygons (so that they can be used without projecting the rasters). `continent_outlines_union` provides the same outlines as single polygons (in case you want to use a projection). We can build the outline for Africa and Eurasia by unioning the two individual outlines: ```{r} library(sf) -africa_outline<- subset(region_outline, region_outline$name=="Africa") -eurasia_outline<- subset(region_outline, region_outline$name=="Eurasia") -afr_eurasia_vec<-terra::vect(sf::st_union(africa_outline, eurasia_outline)) +africa_outline <- subset(region_outline, region_outline$name == "Africa") +eurasia_outline <- subset(region_outline, region_outline$name == "Eurasia") +afr_eurasia_vec <- terra::vect(sf::st_union(africa_outline, eurasia_outline)) climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) terra::plot(climate_20k_afr_eurasia) ``` @@ -248,21 +258,25 @@ get_biome_classes("Example") To plot it, we extract the biome and then subset it to just that class ```{r, fig.width=6, fig.height=5} -climate_20k <- region_slice(time_bp = -20000, - bio_variables = c("bio01","bio10","biome"), - dataset = "Example") -climate_20k$ice<-climate_20k$biome -climate_20k$ice[climate_20k$ice!=28] <- FALSE -climate_20k$ice[climate_20k$ice==28] <- TRUE +climate_20k <- region_slice( + time_bp = -20000, + bio_variables = c("bio01", "bio10", "biome"), + dataset = "Example" +) +climate_20k$ice <- climate_20k$biome +climate_20k$ice[climate_20k$ice != 28] <- FALSE +climate_20k$ice[climate_20k$ice == 28] <- TRUE terra::plot(climate_20k) ``` Or more simply, we use functions designed to get ice and land masks. ```{r, fig.width=6, fig.height=5} -climate_20k <- region_slice(time_bp = -20000, - c("bio01","bio10"), - dataset = "Example") -climate_20k$ice_mask<-get_ice_mask(-20000, dataset = "Example") +climate_20k <- region_slice( + time_bp = -20000, + c("bio01", "bio10"), + dataset = "Example" +) +climate_20k$ice_mask <- get_ice_mask(-20000, dataset = "Example") climate_20k$land_mask <- get_land_mask(-20000, dataset = "Example") terra::plot(climate_20k) ``` @@ -272,36 +286,44 @@ terra::plot(climate_20k) We can visualise the environment for this time step with a PCA: ```{r, fig.width=4, fig.height=4} climate_10k <- region_slice(-10000, - c("bio01","bio10","bio12"), - dataset="Example") + c("bio01", "bio10", "bio12"), + dataset = "Example" +) climate_values_10k <- values(climate_10k) -climate_values_10k <- climate_values_10k[!is.nan(climate_values_10k[,1]),] -climate_10k_pca<-prcomp(climate_values_10k,scale=TRUE, center=TRUE) -plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", - xlab="PC1",ylab="PC2") +climate_values_10k <- climate_values_10k[!is.nan(climate_values_10k[, 1]), ] +climate_10k_pca <- prcomp(climate_values_10k, scale = TRUE, center = TRUE) +plot(climate_10k_pca$x[, 2] ~ climate_10k_pca$x[, 1], + pch = 20, col = "lightgray", + xlab = "PC1", ylab = "PC2" +) ``` We can now get the climate variables for the locations at this time step, compute the PCA scores and plots the locations on top of the background climate: ```{r, fig.width=4, fig.height=4} -locations_10k <-location_slice (x= locations[,c("longitude", "latitude")], - time_bp = locations$time_bp, bio_variables=c("bio01","bio12"), - dataset="Example") -locations_10k <- locations_10k[locations_10k$time_bp_slice == -10000,] -locations_10k_climate<- location_slice (x= locations_10k[,c("longitude", "latitude")], - time_bp = locations_10k$time_bp, bio_variables=c("bio01","bio10","bio12"), - dataset="Example") -locations_10k_pca_scores <- predict(climate_10k_pca,newdata= locations_10k_climate[,-c(1:4)]) -plot(climate_10k_pca$x[,2]~climate_10k_pca$x[,1],pch=20,col="lightgray", - xlab="PC1",ylab="PC2") -points(locations_10k_pca_scores,pch=20,col="red") - +locations_10k <- location_slice( + x = locations[, c("longitude", "latitude")], + time_bp = locations$time_bp, bio_variables = c("bio01", "bio12"), + dataset = "Example" +) +locations_10k <- locations_10k[locations_10k$time_bp_slice == -10000, ] +locations_10k_climate <- location_slice( + x = locations_10k[, c("longitude", "latitude")], + time_bp = locations_10k$time_bp, bio_variables = c("bio01", "bio10", "bio12"), + dataset = "Example" +) +locations_10k_pca_scores <- predict(climate_10k_pca, newdata = locations_10k_climate[, -c(1:4)]) +plot(climate_10k_pca$x[, 2] ~ climate_10k_pca$x[, 1], + pch = 20, col = "lightgray", + xlab = "PC1", ylab = "PC2" +) +points(locations_10k_pca_scores, pch = 20, col = "red") ``` ## Working with MIS Sometimes we want to work with multiple time steps to represent a given MIS. We can get a list of time steps available for a given mis with: ```{r} -mis_time_steps <- get_mis_time_steps(1,"Example") +mis_time_steps <- get_mis_time_steps(1, "Example") mis_time_steps ``` @@ -310,9 +332,11 @@ And now cycle over those steps. First extract the climate into a list: mis_climate_list <- list() for (this_step in mis_time_steps) { this_step_climate <- - region_slice(time_bp = this_step , - bio_variables = c("bio01", "bio10", "bio12"), - dataset = "Example") + region_slice( + time_bp = this_step, + bio_variables = c("bio01", "bio10", "bio12"), + dataset = "Example" + ) this_step_climate <- values(this_step_climate) this_step_climate <- this_step_climate[!is.nan(this_step_climate[, 1]), ] @@ -327,9 +351,11 @@ mis_climate <- do.call(rbind, mis_climate_list) And finally plot it: ```{r, fig.width=4, fig.height=4} -mis_pca<-prcomp(mis_climate,scale=TRUE, center=TRUE) -plot(mis_pca$x[,2]~mis_pca$x[,1],pch=20,col="lightgray", - xlab="PC1",ylab="PC2") +mis_pca <- prcomp(mis_climate, scale = TRUE, center = TRUE) +plot(mis_pca$x[, 2] ~ mis_pca$x[, 1], + pch = 20, col = "lightgray", + xlab = "PC1", ylab = "PC2" +) ``` @@ -338,29 +364,36 @@ plot(mis_pca$x[,2]~mis_pca$x[,1],pch=20,col="lightgray", For a number of species distribution models, we need to subsample background points to our presences. We can simply do that by generating the raster for the time slice of interest, and using the appropriate function from `terra`: ```{r} -climate_20k <- region_slice(time_bp = -20000, - bio_variables = c("bio01", "bio10"), - dataset = "Example") -this_sample<-terra::spatSample(climate_20k, 100, na.rm = TRUE, - cells = TRUE,xy = TRUE) +climate_20k <- region_slice( + time_bp = -20000, + bio_variables = c("bio01", "bio10"), + dataset = "Example" +) +this_sample <- terra::spatSample(climate_20k, 100, + na.rm = TRUE, + cells = TRUE, xy = TRUE +) head(this_sample) ``` If we needed to get other variables for those coordinates, we could simply: ```{r} -additional_var <- location_slice ( - x = data.frame(longitude = this_sample$x, latitude = this_sample$y), - time_bp = rep(-20000, nrow(this_sample)), - bio_variables = c("bio12"), - dataset = "Example") +additional_var <- location_slice( + x = data.frame(longitude = this_sample$x, latitude = this_sample$y), + time_bp = rep(-20000, nrow(this_sample)), + bio_variables = c("bio12"), + dataset = "Example" +) head(additional_var) ``` Alternatively, we could use the cell number: ```{r} -additional_var <- location_slice (x = this_sample$cell, - time_bp = rep(-20000, nrow(this_sample)), - bio_variables = c("bio12"), - dataset = "Example") +additional_var <- location_slice( + x = this_sample$cell, + time_bp = rep(-20000, nrow(this_sample)), + bio_variables = c("bio12"), + dataset = "Example" +) head(additional_var) ``` From b847e73c34e2747f1184f7d8609d0c894b8e085c Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 15:28:35 +0100 Subject: [PATCH 18/83] Expand vignette --- vignettes/pastclim_overview.Rmd | 38 +++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index 27db303e..e2658acb 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -204,7 +204,7 @@ climate_20k and plot these three variables (the layers of the raster): ```{r, fig.width=6, fig.height=5} -terra::plot(climate_20k) +terra::plot(climate_20k, main = names(climate_20k)) ``` Often we want to focus a given region. There are a number of preset extents in @@ -215,7 +215,8 @@ region_extent We can crop the raster to Europe: ```{r, fig.width=6, fig.height=5} europe_climate_20k <- terra::crop(climate_20k, terra::ext(region_extent$Europe)) -terra::plot(europe_climate_20k) +terra::plot(europe_climate_20k, + main = names(europe_climate_20k)) ``` We might want to use a more complex shape (i.e. a polygon, as a `terra::vect` @@ -236,7 +237,8 @@ afr_eurasia_vec <- terra::vect("POLYGON ((0 70, 25 70, 50 80, 170 80, 170 10, terra::crs(afr_eurasia_vec) <- terra::crs(climate_20k) climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) climate_20k_afr_eurasia <- terra::crop(climate_20k_afr_eurasia, afr_eurasia_vec) -terra::plot(climate_20k_afr_eurasia) +terra::plot(climate_20k_afr_eurasia, + main = names(climate_20k_afr_eurasia)) ``` `pastclim` includes a number of pre-generated masks for the main continental masses, stored in the dataset `continent_outlines` in an `sf` object. Note that outlines that cross the antimeridian are split into multiple polygons (so that they can be used without projecting the rasters). `continent_outlines_union` provides the same outlines as single polygons (in case you want to use a projection). We can build the outline for Africa and Eurasia by unioning the two individual outlines: @@ -246,10 +248,38 @@ africa_outline <- subset(region_outline, region_outline$name == "Africa") eurasia_outline <- subset(region_outline, region_outline$name == "Eurasia") afr_eurasia_vec <- terra::vect(sf::st_union(africa_outline, eurasia_outline)) climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) -terra::plot(climate_20k_afr_eurasia) +terra::plot(climate_20k_afr_eurasia, + main = names(climate_20k_afr_eurasia)) ``` Note that the Eurasian outline is intersected by the antimeridian, and so we have a small amount of data on the left hand side of the plot (corresponding to the eastern end of Siberia). +It is possible to also load a time series of rasters with the function `region_series`. In this case, the function returns a `spatRasterDataset`, with each variable as a sub-dataset: + +```{r} +climate_region <- region_series( + time_bp = list(min=-15000,max=0), c("bio01", "bio10", "bio12"), + "Example" +) +climate_region +``` + +Each of these sub-dataset is a `spatRaster`, with time steps as layers: + +```{r} +climate_region$bio01 +``` + +We can then plot the time series of a given variable: +```{r} +terra::plot(climate_region$bio01) +``` + +To plot all climate variables for a given time step, we can slice the time series: +```{r} +slice_10k <- slice_region_series(climate_region, time_bp = -10000) +terra::plot(slice_10k, main = names(slice_10k)) +``` + # Working with biomes and icesheets The climate reconstructions do not show areas under permanent ice. Ice sheets are stored as class 28 in the "biome" variable: ```{r} From 4cfd9966b1aed0c9487899b4d39fbca22b472c56 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 15:41:31 +0100 Subject: [PATCH 19/83] Edits of vignette [skip ci] --- vignettes/pastclim_overview.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index e2658acb..adfd9639 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -52,7 +52,7 @@ of this issue can be found on [stack**overflow**](https://stackoverflow.com/ques # Download the data You will need to download climatic reconstructions before being able to do any work with `pastclim`. Currently the library contains two datasets: -"Beyer2020" which covers the last 120k years; and, for project that go further back in time, "Krapp2021" which goes back to 800kya. For these two datasets, there are functions that help you download the data +*Beyer2020* which covers the last 120k years; and, for project that go further back in time, *Krapp2021* which goes back to 800kya. For these two datasets, there are functions that help you download the data and choose the variables. It is possible to add additional, custom datasets, but you will need some familiarity with handling `netcdf` files (see the vignette on 'Formatting data to use in pastclim'). @@ -90,7 +90,7 @@ and the available time steps available can be obtained with: get_time_steps(dataset = "Example") ``` -For "Beyer2020" and "Krapp2021", you can get a list +For *Beyer2020* and *Krapp2021*, you can get a list of available variables for each dataset with: ```{r} get_vars_for_dataset(dataset = "Beyer2020") @@ -111,7 +111,7 @@ we can use: get_downloaded_datasets() ``` -Let's now download `bio01` and `bio05` for the *Beyer2020* dataset (this operation might take several minutes, as the datasets are large; `R` will pause until +Let's now download *bio01* and *bio05* for the *Beyer2020* dataset (this operation might take several minutes, as the datasets are large; `R` will pause until the download is complete): ```{r eval=FALSE} @@ -136,7 +136,7 @@ locations <- data.frame( locations ``` -And extract their climatic conditions for `bio01` and `bio12`: +And extract their climatic conditions for *bio01* and *bio12*: ```{r} location_slice( x = locations[, c("longitude", "latitude")], @@ -296,7 +296,7 @@ climate_20k <- region_slice( climate_20k$ice <- climate_20k$biome climate_20k$ice[climate_20k$ice != 28] <- FALSE climate_20k$ice[climate_20k$ice == 28] <- TRUE -terra::plot(climate_20k) +terra::plot(climate_20k, main = names(climate_20k)) ``` Or more simply, we use functions designed to get ice and land masks. @@ -308,7 +308,7 @@ climate_20k <- region_slice( ) climate_20k$ice_mask <- get_ice_mask(-20000, dataset = "Example") climate_20k$land_mask <- get_land_mask(-20000, dataset = "Example") -terra::plot(climate_20k) +terra::plot(climate_20k, main = names(climate_20k)) ``` # Set the samples within the background From 1f3c5843871d40c7d238b9fa57ae54e84baba5e5 Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 17:23:43 +0100 Subject: [PATCH 20/83] cropping --- R/region_series.R | 32 ++++++++++++++++++++- R/region_slice.R | 16 +++++++++-- man/region_series.Rd | 16 ++++++++++- man/region_slice.Rd | 16 ++++++++++- vignettes/pastclim_overview.Rmd | 49 +++++++++++++++++++++++---------- 5 files changed, 109 insertions(+), 20 deletions(-) diff --git a/R/region_series.R b/R/region_series.R index 61c0fd4c..fe483583 100644 --- a/R/region_series.R +++ b/R/region_series.R @@ -19,6 +19,11 @@ #' @param path_to_nc the path to the custom nc file containing the paleoclimate #' reconstructions. All the variables of interest need to be included in #' this file. +#' @param ext an extent, coded as a \code{terra::SpatExtent} object. If NULL, +#' the full extent of the reconstruction is given. +#' @param crop a polygon used to crop the reconstructions (e.g. the outline +#' of a continental mass). A \code{terra::SpatVector} object is used to +#' define the polygon #' #' @import terra #' @export @@ -27,9 +32,23 @@ region_series <- function(time_bp = NULL, bio_variables, dataset, - path_to_nc = NULL) { + path_to_nc = NULL, + ext = NULL, + crop = NULL) { + check_dataset_path(dataset = dataset, path_to_nc = path_to_nc) + if (!is.null(ext)){ + if(!inherits(ext,"SpatExtent")){ + stop ("extent should be a terra::SpatExtent object created terra::ext") + } + } + if (!is.null(crop)){ + if(!inherits(crop,"SpatVector")){ + stop ("extent should be a terra::SpatVector object created terra::vect") + } + } + # check whether the variables exist for this dataset if (dataset != "custom") { # if we are using standard datasets check_var_downloaded(bio_variables, dataset) @@ -59,6 +78,17 @@ region_series <- time_steps = times) } var_brick <- terra::rast(this_file, subds = this_var_nc) + # subset extent + if (!is.null(ext)){ + var_brick <- terra::crop(var_brick, ext) + } + # subset to crop + if (!is.null(crop)){ + terra::crs(crop) <- terra::crs(var_brick) + var_brick <- terra::mask(var_brick, crop) + var_brick <- terra::crop(var_brick, crop) + } + if (!is.null(time_bp)){ climate_spatrasters[[this_var]] <- terra::subset(var_brick, subset = time_index) diff --git a/R/region_slice.R b/R/region_slice.R index 2b480e1d..496c1cfc 100644 --- a/R/region_slice.R +++ b/R/region_slice.R @@ -16,7 +16,12 @@ #' @param path_to_nc the path to the custom nc file containing the paleoclimate #' reconstructions. All the variables of interest need to be included #' in this file. -#' +#' @param ext an extent, coded as a \code{terra::SpatExtent} object. If NULL, +#' the full extent of the reconstruction is given. +#' @param crop a polygon used to crop the reconstructions (e.g. the outline +#' of a continental mass). A \code{terra::SpatVector} object is used to +#' define the polygon. +#' #' @import terra #' @export @@ -24,12 +29,17 @@ region_slice <- function(time_bp, bio_variables, dataset, - path_to_nc = NULL) { + path_to_nc = NULL, + ext = NULL, + crop = NULL) { + this_series <- region_series( time_bp = time_bp, bio_variables = bio_variables, dataset = dataset, - path_to_nc = path_to_nc + path_to_nc = path_to_nc, + ext = ext, + crop = crop ) return(slice_region_series(x = this_series, time_bp = time_bp)) } diff --git a/man/region_series.Rd b/man/region_series.Rd index 58b88e0b..77dd9899 100644 --- a/man/region_series.Rd +++ b/man/region_series.Rd @@ -4,7 +4,14 @@ \alias{region_series} \title{Extract a time series of climate variables for a region} \usage{ -region_series(time_bp = NULL, bio_variables, dataset, path_to_nc = NULL) +region_series( + time_bp = NULL, + bio_variables, + dataset, + path_to_nc = NULL, + ext = NULL, + crop = NULL +) } \arguments{ \item{time_bp}{time slices in years before present (negative values represent @@ -23,6 +30,13 @@ then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate reconstructions. All the variables of interest need to be included in this file.} + +\item{ext}{an extent, coded as a \code{terra::SpatExtent} object. If NULL, +the full extent of the reconstruction is given.} + +\item{crop}{a polygon used to crop the reconstructions (e.g. the outline +of a continental mass). A \code{terra::SpatVector} object is used to +define the polygon} } \description{ This function extracts a time series of one or more climate variables for diff --git a/man/region_slice.Rd b/man/region_slice.Rd index 00973ed4..085abcd9 100644 --- a/man/region_slice.Rd +++ b/man/region_slice.Rd @@ -4,7 +4,14 @@ \alias{region_slice} \title{Extract a climate slice for a region} \usage{ -region_slice(time_bp, bio_variables, dataset, path_to_nc = NULL) +region_slice( + time_bp, + bio_variables, + dataset, + path_to_nc = NULL, + ext = NULL, + crop = NULL +) } \arguments{ \item{time_bp}{the time slice in years before present (negative @@ -21,6 +28,13 @@ then a single nc file is used from "path_to_nc"} \item{path_to_nc}{the path to the custom nc file containing the paleoclimate reconstructions. All the variables of interest need to be included in this file.} + +\item{ext}{an extent, coded as a \code{terra::SpatExtent} object. If NULL, +the full extent of the reconstruction is given.} + +\item{crop}{a polygon used to crop the reconstructions (e.g. the outline +of a continental mass). A \code{terra::SpatVector} object is used to +define the polygon.} } \description{ This function extracts a slice of one or more climate variables for a given diff --git a/vignettes/pastclim_overview.Rmd b/vignettes/pastclim_overview.Rmd index adfd9639..2507d447 100644 --- a/vignettes/pastclim_overview.Rmd +++ b/vignettes/pastclim_overview.Rmd @@ -1,7 +1,7 @@ --- title: "pastclim overview" -# output: rmarkdown::pdf_document -output: rmarkdown::html_vignette +output: rmarkdown::pdf_document +# output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{pastclim overview} %\VignetteEngine{knitr::rmarkdown} @@ -212,31 +212,44 @@ Often we want to focus a given region. There are a number of preset extents in ```{r} region_extent ``` -We can crop the raster to Europe: + +We can extract climate only for Europe; we need to pass an `terra::SpatExtent` +object to `region_slice`: + ```{r, fig.width=6, fig.height=5} -europe_climate_20k <- terra::crop(climate_20k, terra::ext(region_extent$Europe)) +europe_climate_20k <- region_slice( + time_bp = -20000, + c("bio01", "bio10", "bio12"), + dataset = "Example", + ext = terra::ext(region_extent$Europe) +) terra::plot(europe_climate_20k, main = names(europe_climate_20k)) ``` +Alternatively, we can crop an already extracted raster: +```{r, fig.width=6, fig.height=5} +europe_climate_20k_alt <- terra::crop(climate_20k, terra::ext(region_extent$Europe)) +terra::plot(europe_climate_20k_alt, + main = names(europe_climate_20k_alt)) +``` + We might want to use a more complex shape (i.e. a polygon, as a `terra::vect` object) as a mask to limit the area covered by the raster. For example, we might want to focus the 3 continents Africa, Europe and Asia, but exclude the Americas and Oceania. We need to create a polygon with the appropriate vertices (note that you need to -reuse the first vertex as the last vertex, to close the polygon). For the masking -operation to work correctly, the polygon will need to have the same projection -as the raster (all the dataset have a simple `lon/lat` projection, so we can -copy over the `crs` from the raster to the polygon). It is also helpful to crop -the extent of the raster to that of the polygon (to avoid having a large amount -of blank space). +reuse the first vertex as the last vertex, to close the polygon). + ```{r, fig.width=6, fig.height=5} afr_eurasia_vec <- terra::vect("POLYGON ((0 70, 25 70, 50 80, 170 80, 170 10, 119 2.4, 119 0.8, 116 -7.6, 114 -12, 100 -40, -25 -40, -25 64, 0 70))") -terra::crs(afr_eurasia_vec) <- terra::crs(climate_20k) -climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) -climate_20k_afr_eurasia <- terra::crop(climate_20k_afr_eurasia, afr_eurasia_vec) +climate_20k_afr_eurasia <- region_slice( + time_bp = -20000, + c("bio01", "bio10", "bio12"), + dataset = "Example", + crop = afr_eurasia_vec) terra::plot(climate_20k_afr_eurasia, main = names(climate_20k_afr_eurasia)) ``` @@ -247,10 +260,15 @@ library(sf) africa_outline <- subset(region_outline, region_outline$name == "Africa") eurasia_outline <- subset(region_outline, region_outline$name == "Eurasia") afr_eurasia_vec <- terra::vect(sf::st_union(africa_outline, eurasia_outline)) -climate_20k_afr_eurasia <- terra::mask(climate_20k, afr_eurasia_vec) +climate_20k_afr_eurasia <- region_slice( + time_bp = -20000, + c("bio01", "bio10", "bio12"), + dataset = "Example", + crop = afr_eurasia_vec) terra::plot(climate_20k_afr_eurasia, main = names(climate_20k_afr_eurasia)) ``` + Note that the Eurasian outline is intersected by the antimeridian, and so we have a small amount of data on the left hand side of the plot (corresponding to the eastern end of Siberia). It is possible to also load a time series of rasters with the function `region_series`. In this case, the function returns a `spatRasterDataset`, with each variable as a sub-dataset: @@ -280,6 +298,9 @@ slice_10k <- slice_region_series(climate_region, time_bp = -10000) terra::plot(slice_10k, main = names(slice_10k)) ``` +`region_series` takes the same `ext` and `crop` options as `region_slice` to limit +the extent of the climatic reconstrutions. + # Working with biomes and icesheets The climate reconstructions do not show areas under permanent ice. Ice sheets are stored as class 28 in the "biome" variable: ```{r} From b1878e22887033b6301addaa4ec9d3b2e2e04f5d Mon Sep 17 00:00:00 2001 From: Andrea Manica Date: Fri, 19 Aug 2022 21:30:35 +0100 Subject: [PATCH 21/83] vignette for custom data --- .../CHELSA_TraCE21k_bio01_-1_V1.0.small.tif | Bin 0 -> 251136 bytes .../CHELSA_TraCE21k_bio01_-2_V1.0.small.tif | Bin 0 -> 251136 bytes .../CHELSA_TraCE21k_bio01_-3_V1.0.small.tif | Bin 0 -> 251136 bytes .../rawdata_scripts/create_files_by_dataset.R | 2 +- vignettes/custom_datasets.Rmd | 119 ++++++++++++++++++ 5 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 inst/extdata/CHELSA_bio01/CHELSA_TraCE21k_bio01_-1_V1.0.small.tif create mode 100644 inst/extdata/CHELSA_bio01/CHELSA_TraCE21k_bio01_-2_V1.0.small.tif create mode 100644 inst/extdata/CHELSA_bio01/CHELSA_TraCE21k_bio01_-3_V1.0.small.tif create mode 100644 vignettes/custom_datasets.Rmd diff --git a/inst/extdata/CHELSA_bio01/CHELSA_TraCE21k_bio01_-1_V1.0.small.tif b/inst/extdata/CHELSA_bio01/CHELSA_TraCE21k_bio01_-1_V1.0.small.tif new file mode 100644 index 0000000000000000000000000000000000000000..a1b5fa927c8d4ed8f072c837b29da648301e1dea GIT binary patch literal 251136 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

k9s=(R+U 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