diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c2aa02f1..d9c25265 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -14,8 +14,6 @@ jobs: strategy: matrix: version: - - '1.3' - - '1.4' - '1.5' os: - ubuntu-latest diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml index 8ce95e8a..6b44ede0 100644 --- a/.github/workflows/CompatHelper.yml +++ b/.github/workflows/CompatHelper.yml @@ -9,7 +9,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - julia-version: [1.4.0] + julia-version: [1.5.0] julia-arch: [x86] os: [ubuntu-latest] steps: diff --git a/Project.toml b/Project.toml index aadf98bc..da73c24a 100644 --- a/Project.toml +++ b/Project.toml @@ -1,11 +1,11 @@ name = "SimpleSDMLayers" uuid = "2c645270-77db-11e9-22c3-0f302a89c64c" authors = ["Timothée Poisot ", "Gabriel Dansereau "] -version = "0.4.10" +version = "0.5.0" [deps] ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3" -HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" Requires = "ae029012-a4dd-5104-9daa-d747884805df" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" @@ -13,8 +13,7 @@ ZipFile = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" [compat] ArchGDAL = "0.4, 0.5, 0.6" -HTTP = "0.8, 0.9" RecipesBase = "0.7, 0.8, 1.0" Requires = "1.0" ZipFile = "0.8, 0.9" -julia = "1.3" +julia = "1.5" diff --git a/README.md b/README.md index dda40506..5e246b0e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Simple Layers for Species Distributions Modelling -This package offers very simple types and functions to interact with -bioclimatic data and the output of species distribution models. +This package offers very simple types and functions to interact with bioclimatic +data and the output of species distribution models. [![d_stable](https://img.shields.io/badge/Doc-stable-green?style=flat-square)](https://ecojulia.github.io/SimpleSDMLayers.jl/stable/) [![d_latest](https://img.shields.io/badge/Doc-latest-blue?style=flat-square)](https://ecojulia.github.io/SimpleSDMLayers.jl/latest/) @@ -19,8 +19,9 @@ bioclimatic data and the output of species distribution models. title = "SimpleSDMLayers.jl & GBIF.jl example">

-Curious to know more? Have a look at our [paper in Journal of Open Source Software](https://doi.org/10.21105/joss.02872), our [JuliaCon poster](https://github.com/gabrieldansereau/juliacon-2020-poster/blob/master/juliacon-poster.pdf), our [NextJournal demo notebook](https://nextjournal.com/gabrieldansereau/SimpleSDMLayers-JuliaCon2020-demo/), and our [extended documentation](https://ecojulia.github.io/SimpleSDMLayers.jl/stable/), or keep reading for a quick overview. +Curious to know more? Have a look at our [paper in Journal of Open Source Software][joss], our [JuliaCon poster](https://github.com/gabrieldansereau/juliacon-2020-poster/blob/master/juliacon-poster.pdf), our [NextJournal demo notebook](https://nextjournal.com/gabrieldansereau/SimpleSDMLayers-JuliaCon2020-demo/), and our [extended documentation](https://ecojulia.github.io/SimpleSDMLayers.jl/stable/), or keep reading for a quick overview. +[joss]: https://doi.org/10.21105/joss.02872 ### Installation The currently released version of the package can be installed with: @@ -68,19 +69,22 @@ are immutable. ### Bioclimatic data -#### WorldClim 2.1 - -The `worldclim` function will get a range, or an array of indices, and return -the corresponding bioclim 2.1 layers at the specified `resolution`. For -example, to get the annual temperature, and annual precipitation: - -~~~ julia -temperature, precipitation = worldclim([1,12]) -~~~ - -By default, the function will return the layers for the entire globe, and they -can be cropped later. The layers are returned as `SimpleSDMPredictor` objects. - +| Data provider | Dataset | Layers | Future models | Future scenarios | +| -------------------------------- | ---------------------- | ------ | ------------- | ------------------------------------ | +| `EarthEnv` | `Landcover` | 12 | | | +| `EarthEnv` | `HabitatHeterogeneity` | 14 | | | +| [`WorldClim`][worldclim-current] | `BioClim` | 19 | `CMIP6` | `SharedSocioeconomicPathway` | +| [`CHELSA`][chelsa-bioclim] | `BioClim` | 12 | `CMIP5` | `RepresentativeConcentrationPathway` | + +[earthenv-landcover]: http://www.earthenv.org/landcover +[earthenv-texture]: http://www.earthenv.org/texture +[worldclim-current]: https://www.worldclim.org/data/worldclim21.html +[chelsa-bioclim]: http://chelsa-climate.org/ + +When downloaded (using `SimpleSDMPredictor`), the layers are stored either in an +`assets` subfolder of the current project (strongly advised against), or at the +location determined by the `SDMLAYERS_PATH` environment variable. The datasets/providers +with future models and scenarios also accept years. ### Plotting @@ -89,6 +93,7 @@ Using the `Plots` package, one can call the `heatmap`, `contour`, `density` `heatmap`. ~~~ julia +temperature = SimpleSDMPredictor(WorldClim, BioClim, 1) plot(temperature) ~~~ diff --git a/docs/make.jl b/docs/make.jl index 7081745b..7c186037 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -17,15 +17,15 @@ makedocs( ], "Examples" => [ "Temperature data" => "examples/temperature.md", - "GBIF integration" => "examples/gbif.md", "DataFrames integration" => "examples/dataframes.md", - "Importing raster data" => "examples/import.md", "Sliding window analysis" => "examples/slidingwindow.md", "Landcover data" => "examples/landcover.md", "Landcover consensus" => "examples/consensus.md" ], "Building SDMs" => [ - "BIOCLIM from scratch" => "sdm/bioclim.md" + "GBIF integration" => "sdm/gbif.md", + "BIOCLIM from scratch" => "sdm/bioclim.md", + "Future data" => "sdm/future.md" ] ] ) diff --git a/docs/src/examples/consensus.md b/docs/src/examples/consensus.md index 0acc83af..d56e1cc7 100644 --- a/docs/src/examples/consensus.md +++ b/docs/src/examples/consensus.md @@ -12,72 +12,34 @@ using Plots bbox = (left=8.25, right=10.0, bottom=41.2, top=43.2) ``` -We will then do two things. First, get the first layer of landcover (see the -help of `landcover` for a list of the layers), and then create a datacube, -organized around dimensions of latitude, longitude, and layer value - we will -only focus on the 11 first variables, since we do not want the information on -open water (layer 12): +First, we will download all values for our layers: ```@example cons -lc = landcover(1; full=false, bbox...) -use = fill(NaN32, size(lc)..., 11) +lc = convert.(Float32, SimpleSDMPredictor(EarthEnv, LandCover, 1:12; full=false, bbox...)) ``` -At this point, we will simply fill in the first "slice" of our datacube with -values from the layer: +To perform the actual analysis, we will define a `shannon` function, which will +return the entropy of the land use categories: ```@example cons -for (i,e) in enumerate(lc.grid) - coord = (CartesianIndices(size(lc.grid))[i].I..., 1) - if !isnothing(e) - use[coord...] = e - end -end -``` - -The next step is to repeat this process for all other layers, filling the -appropriate data cube slice: - -```@example cons -for layer in 2:11 - lc = landcover(layer; full=false, bbox...) - for (i,e) in enumerate(lc.grid) - coord = (CartesianIndices(size(lc.grid))[i].I..., layer) - if !isnothing(e) - use[coord...] = e - end - end -end -``` - -To perform the actual analysis, we will define a `get_most_common_landuse` function, which will return the index of the layer with the highest score: - -```@example cons -function get_most_common_landuse(f) - f[isnan.(f)] .= 0.0 - sum(f) == 0 && return NaN - return last(findmax(f)) -end - function shannon(x) - v = filter(!isnan, x) + v = filter(n -> n>zero(eltype(x)), x) length(v) == 0 && return NaN v = v ./ sum(v) return -sum(v.*log2.(v)) end ``` -```@example cons -consensus = mapslices(get_most_common_landuse, use; dims=3)[:,:,1] -entropy = mapslices(shannon, use; dims=3)[:,:,1] +We can then apply these functions using the `mosaic` method: -consensus = SimpleSDMResponse(consensus, lc) -entropy = SimpleSDMResponse(entropy, lc) +```@example cons +consensus = mosaic(x -> last(findmax(x)), lc) +entropy = mosaic(shannon, lc) ``` ```@example cons -p1 = plot(consensus, c=cgrad(:Set3_11, categorical=true), frame=:none) +p1 = plot(consensus, c=:terrain, frame=:none) p2 = plot(entropy, c=:bamako, frame=:none) -plot(p1, p2, size=(900, 400), dpi=600) +plot(p1, p2) ``` diff --git a/docs/src/examples/landcover.md b/docs/src/examples/landcover.md index 9ccd9ae6..8c6b2d6b 100644 --- a/docs/src/examples/landcover.md +++ b/docs/src/examples/landcover.md @@ -8,7 +8,7 @@ limits of a bounding box. ```@example urban using SimpleSDMLayers -urban = landcover(9; left=-11.0, right=31.1, bottom=29.0, top=71.1) +urban = SimpleSDMPredictor(EarthEnv, LandCover, 9; left=-11.0, right=31.1, bottom=29.0, top=71.1) ``` This dataset is returning data as `UInt8` (as it represents a proportion of the @@ -16,26 +16,18 @@ pixel occupied by the type), but this is not something that can be plotted efficiently. So in the next step, we will manipulate this object a little bit to have something more workable. -Let's start by preparing a new grid, with the same dimensions, but a friendlier -type, and then we can then fill these values using a simple rule of using either -`NaN` or the converted value: - ```@example urban -n_urban_grid = zeros(Float32, size(urban)); -for (i,e) in enumerate(urban.grid) - n_urban_grid[i] = isnothing(e) ? NaN32 : Float32(e) -end +urban = convert(Float32, urban) ``` -We can now overwrite our `urban` object as a layer: +We will replace the values of 0 by `nothing`, to only see the pixels with some +urban cover: ```@example urban -urban = SimpleSDMPredictor(n_urban_grid, urban) +replace!(urban, zero(eltype(urban)) => nothing) ``` -Note that the previous instruction uses a shortcut where the bounding box from a -new `SimpleSDMLayer` is drawn from the bounding box for an existing layer. With -this done, we can show the results: +With this done, we can plot the results: ```@example urban using Plots diff --git a/docs/src/examples/slidingwindow.md b/docs/src/examples/slidingwindow.md index 53ac5b2a..91289b95 100644 --- a/docs/src/examples/slidingwindow.md +++ b/docs/src/examples/slidingwindow.md @@ -9,7 +9,7 @@ using SimpleSDMLayers using Plots using Statistics -precipitation = worldclim(12; left=-80.0, right=-56.0, bottom=44.0, top=62.0) +precipitation = SimpleSDMPredictor(WorldClim, BioClim, 12; left=-80.0, right=-56.0, bottom=44.0, top=62.0) ``` The sliding window works by taking all pixels *within a given radius* (expressed diff --git a/docs/src/examples/temperature.md b/docs/src/examples/temperature.md index 32b8b00d..bf1b4667 100644 --- a/docs/src/examples/temperature.md +++ b/docs/src/examples/temperature.md @@ -7,7 +7,7 @@ for each layers are in the function documentation): ```@example temp using SimpleSDMLayers -temperature = worldclim(1) +temperature = SimpleSDMPredictor(WorldClim, BioClim, 1) ``` Thanks to the integration with Plots and StatsPlots, we can very rapidly diff --git a/docs/src/man/data.md b/docs/src/man/data.md index 1998f802..aa062f34 100644 --- a/docs/src/man/data.md +++ b/docs/src/man/data.md @@ -4,26 +4,60 @@ The package offers access to bioclimatic and other datasets - they are downloaded, saved to the disk, and then read locally. Please note that some of them require a lot of memory, so make sure your machine can handle them. -## Worldclim 2.1 +By default, the layers are stored in the `assets` subfolder of the current +project. This being said, the prefered solution is to define a `SDMLAYERS_PATH` +environment variable pointing to a specific path, where the layers will live. +This will ensure that they are re-used between projects. + +## General interface + +All layers are returned as `SimpleSDMPredictor`, and therefore constructed by +calling the `SimpleSDMPredictor` function on a `LayerProvider` and a +`LayerDataset`, possibly with a future climate model and scenario. In all cases, +the method accepts either a single layer, or an array of layers. + +| Data provider | Dataset | Layers | Future models | Future scenarios | +| -------------------------------- | ---------------------- | ------ | ------------- | ------------------------------------ | +| `EarthEnv` | `Landcover` | 12 | | | +| `EarthEnv` | `HabitatHeterogeneity` | 14 | | | +| [`WorldClim`][worldclim-current] | `BioClim` | 19 | `CMIP6` | `SharedSocioeconomicPathway` | +| [`CHELSA`][chelsa-bioclim] | `BioClim` | 12 | `CMIP5` | `RepresentativeConcentrationPathway` | + +[earthenv-landcover]: http://www.earthenv.org/landcover +[earthenv-texture]: http://www.earthenv.org/texture +[worldclim-current]: https://www.worldclim.org/data/worldclim21.html +[chelsa-bioclim]: http://chelsa-climate.org/ + +## Later providers ```@docs -worldclim +SimpleSDMLayers.LayerProvider +WorldClim +CHELSA +EarthEnv ``` -## CHELSA V1 +## Layer datasets ```@docs -bioclim +SimpleSDMLayers.LayerDataset +BioClim +LandCover +HabitatHeterogeneity ``` -## EarthEnv landcover +## Future climate models ```@docs -landcover +SharedSocioeconomicPathway +RepresentativeConcentrationPathway +CMIP5 +CMIP6 ``` -## ASCII files +## File reading and writing ```@docs SimpleSDMLayers.ascii +geotiff ``` \ No newline at end of file diff --git a/docs/src/sdm/bioclim.md b/docs/src/sdm/bioclim.md index c6d4484a..f61ecbe0 100644 --- a/docs/src/sdm/bioclim.md +++ b/docs/src/sdm/bioclim.md @@ -40,7 +40,7 @@ example, we will take all worldclim data, at the default 10 arc minute resolution: ```@example bioclim -predictors = worldclim(1:19; left=left, right=right, bottom=bottom, top=top); +predictors = SimpleSDMPredictor(WorldClim, BioClim, 1:19; left=left, right=right, bottom=bottom, top=top); first(predictors) ``` diff --git a/docs/src/sdm/future.md b/docs/src/sdm/future.md new file mode 100644 index 00000000..f5f58061 --- /dev/null +++ b/docs/src/sdm/future.md @@ -0,0 +1,80 @@ +# Future climate data + +For some dta providers and datasets, `SimpleSDMLayers` offers access to future +climate data. Future climates are usually specified by a model, and a scenario. +For example, WorldClim 2.1 offers the full suite of BioClim variable under four +SSPs and a number of CMIP6 models. + +We can use this to look at, for example, the temperature difference between the +current and future climate. To illustrate this, we will do a simple example +where we contrast the "historical" climate (*i.e.* what is assumed to be the +current data) to the projected data under SSP585 in the 2041-2060 period. + +```@example future +using SimpleSDMLayers +using Plots +using Statistics +``` + +We will start by getting the contemporary data: + +```@example future +baseline = SimpleSDMPredictor(WorldClim, BioClim, 1; left=60.0, right=95.0, bottom=0.0, top=40.0) +plot(baseline, frame=:box, c=:heat) +``` + +To get a future dataset, we need to specify the model: + +```@example future +instances(CMIP6) +``` + +And we need to check the names of the SSP we want to use: + +```@example future +instances(SharedSocioeconomicPathway) +``` + +We can now get our future temperature layer (and plot it): + +```@example future +future = SimpleSDMPredictor(WorldClim, BioClim, CanESM5, SSP585, 1; year="2041-2060", left=60.0, right=95.0, bottom=0.0, top=40.0) +plot(future, frame=:box, c=:heat) +``` + +Note that the call to get the future data is almost the same as the historical +one - the exception is the addition of the model and scenario, and the +specification of the years. + +With this layer, we can now measure the difference in mean annual temperature: + +```@example future +plot(future - baseline, c=:lapaz, frame=:box) +``` + +We might actually be interested in averaging multiple models. Because we know +the variety of models worldclim has (`instances(CMIP6)`), we can do this fairly +easily. One of the model has no predictions for SSP585 (which we would learn in +the form of an error message), so we will filter it out directly. + +```@example future +ensemble = [ + SimpleSDMPredictor( + WorldClim, BioClim, model, SSP585, 1; + year="2041-2060", left=60.0, right=95.0, bottom=0.0, top=40.0 + ) for model in instances(CMIP6) if model != GFDLESM4 +]; +``` + +We will measure the difference of each layer to the baseline: + +```@example future +differences = [component - baseline for component in ensemble] +plot(plot.(differences, c=:lapaz)..., frame=:box) +``` + +From this, we can look at the average difference (across multiple models): + +```@example future +plot(mosaic(mean, differences), c=:lapaz, frame=:box) +``` \ No newline at end of file diff --git a/docs/src/examples/gbif.md b/docs/src/sdm/gbif.md similarity index 97% rename from docs/src/examples/gbif.md rename to docs/src/sdm/gbif.md index 8aaf43d9..6b39efda 100644 --- a/docs/src/examples/gbif.md +++ b/docs/src/sdm/gbif.md @@ -10,7 +10,7 @@ using SimpleSDMLayers using GBIF using Plots using Statistics -temperature, precipitation = worldclim([1,12]) +temperature, precipitation = SimpleSDMPredictor(WorldClim, BioClim, [1,12]) ``` We can get some occurrences for the taxon of interest: diff --git a/src/SimpleSDMLayers.jl b/src/SimpleSDMLayers.jl index 32e2ac2d..8f23a29b 100644 --- a/src/SimpleSDMLayers.jl +++ b/src/SimpleSDMLayers.jl @@ -1,7 +1,7 @@ module SimpleSDMLayers using ArchGDAL -using HTTP +using Downloads using RecipesBase using ZipFile using Requires @@ -19,19 +19,37 @@ export latitudes, longitudes include(joinpath("lib", "iteration.jl")) -include(joinpath("datasets", "sources.jl")) -include(joinpath("datasets", "download_layer.jl")) -export EarthEnv, WorldClim, BioClim - -include(joinpath("datasets", "raster.jl")) include(joinpath("datasets", "ascii.jl")) include(joinpath("datasets", "geotiff.jl")) export geotiff -include(joinpath("datasets", "worldclim.jl")) -include(joinpath("datasets", "chelsa.jl")) -include(joinpath("datasets", "landcover.jl")) -export worldclim, bioclim, landcover +include(joinpath("datasets", "types.jl")) +export WorldClim, CHELSA, EarthEnv +export BioClim, LandCover, HabitatHeterogeneity +export CMIP6, SharedSocioeconomicPathway +export CMIP5, RepresentativeConcentrationPathway +for s in instances(CMIP5) + @eval export $(Symbol(s)) +end +for s in instances(CMIP6) + @eval export $(Symbol(s)) +end +for s in instances(RepresentativeConcentrationPathway) + @eval export $(Symbol(s)) +end +for s in instances(SharedSocioeconomicPathway) + @eval export $(Symbol(s)) +end + +include(joinpath("datasets", "chelsa", "download.jl")) +include(joinpath("datasets", "chelsa", "bioclim.jl")) + +include(joinpath("datasets", "worldclim", "download.jl")) +include(joinpath("datasets", "worldclim", "bioclim.jl")) + +include(joinpath("datasets", "earthenv", "download.jl")) +include(joinpath("datasets", "earthenv", "landcover.jl")) +include(joinpath("datasets", "earthenv", "habitatheterogeneity.jl")) include(joinpath("operations", "coarsen.jl")) include(joinpath("operations", "sliding.jl")) @@ -44,11 +62,8 @@ include(joinpath("recipes", "recipes.jl")) # This next bit is about being able to change the path for raster assets # globally, which avoids duplication this argument across multiple functions. -_layers_assets_path = "assets" -function assets_path() - isdir(SimpleSDMLayers._layers_assets_path) || mkdir(SimpleSDMLayers._layers_assets_path) - return SimpleSDMLayers._layers_assets_path -end +_layers_assets_path = get(ENV, "SDMLAYERS_PATH", "assets") +isdir(_layers_assets_path) || mkpath(_layers_assets_path) # Fixes the export of clip when GBIF or others are loaded clip(::T) where {T <: SimpleSDMLayer} = nothing diff --git a/src/datasets/ascii.jl b/src/datasets/ascii.jl index c792f229..fb54a406 100644 --- a/src/datasets/ascii.jl +++ b/src/datasets/ascii.jl @@ -9,7 +9,7 @@ end Reads the content of a grid file to a `SimpleSDMPredictor`, the type of which is given by the `datatype` argument. """ -function ascii(file::AbstractString, datatype::Type{T}=Float64) where {T <: Number} +function ascii(::Type{LT}, file::AbstractString, datatype::Type{T}=Float64) where {LT <: SimpleSDMLayer, T <: Number} lines = lowercase.(readlines(file)) # Get the information ncols, ncols_line = _get_asc_field(lines, "ncols", Int64) @@ -32,7 +32,11 @@ function ascii(file::AbstractString, datatype::Type{T}=Float64) where {T <: Numb grid[i] = nothing end end - return SimpleSDMPredictor(grid, xl, xl+cs*ncols, yl, yl+cs*nrows) + return LT(grid, xl, xl+cs*ncols, yl, yl+cs*nrows) +end + +function ascii(file::AbstractString, datatype::Type{T}=Float64) where {T <: Number} + return ascii(SimpleSDMPredictor, file, datatype) end """ @@ -40,8 +44,8 @@ end Writes a `layer` to a grid file, with a given `nodata` value. The layer must store numbers. """ -function ascii(layer::SimpleSDMPredictor{T}, file::AbstractString; nodata::T=convert(T, -9999)) where {T <: Number} - if !(stride(layer)[1] ≈ stride(layer)[2]) +function ascii(file::AbstractString, layer::SimpleSDMPredictor{T}; nodata::T=convert(T, -9999)) where {T <: Number} + if !(stride(layer,1) ≈ stride(layer,2)) throw(DimensionMismatch("The cells of the layer to write must be square (i.e. both values of stride must be equal)")) end open(file, "w") do io diff --git a/src/datasets/chelsa.jl b/src/datasets/chelsa.jl deleted file mode 100644 index 75df9832..00000000 --- a/src/datasets/chelsa.jl +++ /dev/null @@ -1,49 +0,0 @@ -""" - bioclim(layer::Integer; left=nothing, right=nothing, bottom=nothing, top=nothing) - -Download and prepare bioclim layers from the CHELSA database, and returns -them as an array of `SimpleSDMPredictor`s. Layers are called by their number, -from 1 to 19. The list of available layers is given in a table below. - -The keyword argument is `path`, which refers to the path where the function -will look for the geotiff files. - -Note that these files are *large* due the fine resolution of the data, and for -this reason this function will return the *integer* version of the layers. Also -note that the bioclim data are only available for the V1 of CHELSA, and are not -from the V2. - -It is recommended to *keep* the content of the `path` folder, as it will -eliminate the need to download the tiff files (which are quite large). For -example, calling `bioclim(1:19)` will download and everything, and future -calls will be much faster. - -| Variable | Description | -| ------ | ------ | -| 1 | Annual Mean Temperature | -| 2 | Mean Diurnal Range (Mean of monthly (max temp - min temp)) | -| 3 | Isothermality (BIO2/BIO7) (* 100) | -| 4 | Temperature Seasonality (standard deviation *100) | -| 5 | Max Temperature of Warmest Month | -| 6 | Min Temperature of Coldest Month | -| 7 | Temperature Annual Range (BIO5-BIO6) | -| 8 | Mean Temperature of Wettest Quarter | -| 9 | Mean Temperature of Driest Quarter | -| 10 | Mean Temperature of Warmest Quarter | -| 11 | Mean Temperature of Coldest Quarter | -| 12 | Annual Precipitation | -| 13 | Precipitation of Wettest Month | -| 14 | Precipitation of Driest Month | -| 15 | Precipitation Seasonality (Coefficient of Variation) | -| 16 | Precipitation of Wettest Quarter | -| 17 | Precipitation of Driest Quarter | -| 18 | Precipitation of Warmest Quarter | -| 19 | Precipitation of Coldest Quarter | - -""" -function bioclim(layer::Integer; left=nothing, right=nothing, bottom=nothing, top=nothing) - return raster(SimpleSDMPredictor, BioClim(), layer=layer, left=left, right=right, bottom=bottom, top=top) -end - -bioclim(layers::Vector{T}; args...) where {T <: Integer} = [bioclim(l; args...) for l in layers] -bioclim(layers::UnitRange{T}; args...) where {T <: Integer} = [bioclim(l; args...) for l in layers] diff --git a/src/datasets/chelsa/bioclim.jl b/src/datasets/chelsa/bioclim.jl new file mode 100644 index 00000000..b88587a3 --- /dev/null +++ b/src/datasets/chelsa/bioclim.jl @@ -0,0 +1,46 @@ +""" + SimpleSDMPredictor(CHELSA, BioClim, layer::Integer; left=nothing, right=nothing, bottom=nothing, top=nothing) + +Download and prepare bioclim layers from the CHELSA database, and returns +them as an array of `SimpleSDMPredictor`s. Layers are called by their number, +from 1 to 19. The list of available layers is given in a table below. + +The keyword argument is `path`, which refers to the path where the function +will look for the geotiff files. + +Note that these files are *large* due the fine resolution of the data, and for +this reason this function will return the *integer* version of the layers. Also +note that the bioclim data are only available for the V1 of CHELSA, and are not +from the V2. + +It is recommended to *keep* the content of the `path` folder, as it will +eliminate the need to download the tiff files (which are quite large). For +example, calling `bioclim(1:19)` will download and everything, and future +calls will be much faster. +""" +function SimpleSDMPredictor(::Type{CHELSA}, ::Type{BioClim}, layer::Integer=1; kwargs...) + file = _get_raster(CHELSA, BioClim, layer) + return geotiff(SimpleSDMPredictor, file; kwargs...) +end + +function SimpleSDMPredictor(::Type{CHELSA}, ::Type{BioClim}, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(CHELSA, BioClim, l; kwargs...) for l in layers] +end + +""" + SimpleSDMPredictor(CHELSA, BioClim, mod::CMIP5, fut::RepresentativeConcentrationPathway, layer::Integer=1; year="2041-2060", kwargs...) + +Returns a *future* layer for a given RCP and model, at a given year (either +2041-2060 or 2061-2080), from the bioclim future climate. +""" +function SimpleSDMPredictor(::Type{CHELSA}, ::Type{BioClim}, mod::CMIP5, fut::RepresentativeConcentrationPathway, layer::Integer=1; year="2041-2060", kwargs...) + @assert year in ["2041-2060", "2061-2080"] + file = _get_raster(CHELSA, BioClim, mod, fut, layer, year) + return geotiff(SimpleSDMPredictor, file; kwargs...) +end + +function SimpleSDMPredictor(::Type{CHELSA}, ::Type{BioClim}, mod::CMIP5, fut::RepresentativeConcentrationPathway, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(CHELSA, BioClim, mod, fut, l; kwargs...) for l in layers] +end diff --git a/src/datasets/chelsa/download.jl b/src/datasets/chelsa/download.jl new file mode 100644 index 00000000..0e863678 --- /dev/null +++ b/src/datasets/chelsa/download.jl @@ -0,0 +1,33 @@ +function _get_raster(::Type{CHELSA}, ::Type{BioClim}, layer::Integer) + 1 ≤ layer ≤ 19 || throw(ArgumentError("The layer must be between 1 and 19")) + + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(CHELSA), _rasterpath(BioClim)) + isdir(path) || mkpath(path) + + layer = lpad(layer, 2, "0") + filename = "CHELSA_bio10_$(layer).tif" + url_root = "https://envicloud.os.zhdk.cloud.switch.ch/chelsa/chelsa_V1/climatologies/bio/" + + filepath = joinpath(path, filename) + if !(isfile(filepath)) + Downloads.download(url_root * filename, filepath) + end + return filepath +end + +function _get_raster(::Type{CHELSA}, ::Type{BioClim}, mod::CMIP5, fut::RepresentativeConcentrationPathway, layer::Integer, year="2041-2060") + 1 ≤ layer ≤ 19 || throw(ArgumentError("The layer must be between 1 and 19")) + + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(CHELSA), _rasterpath(BioClim), _rasterpath(mod), _rasterpath(fut), year) + isdir(path) || mkpath(path) + + root = "https://os.zhdk.cloud.switch.ch/envicloud/chelsa/chelsa_V1/cmip5/$(year)/bio/" + filename = "CHELSA_bio_mon_$(_rasterpath(mod))_$(_rasterpath(fut))_r1i1p1_g025.nc_$(layer)_$(year)_V1.2.tif" + + filepath = joinpath(path, filename) + if !(isfile(filepath)) + Downloads.download(root * filename, filepath) + end + + return filepath +end \ No newline at end of file diff --git a/src/datasets/download_layer.jl b/src/datasets/download_layer.jl deleted file mode 100644 index b9927814..00000000 --- a/src/datasets/download_layer.jl +++ /dev/null @@ -1,69 +0,0 @@ -function download_layer(w::WorldClim, layer::Integer) - 1 ≤ layer ≤ 19 || throw(ArgumentError("The layer must be between 1 and 19")) - - path = SimpleSDMLayers.assets_path() - - res = Dict(2.5 => "2.5", 5.0 => "5", 10.0 => "10") - - output_file = joinpath(path, "wc2.1_$(res[w.resolution])m_bio_$(layer).tif") - zip_file = joinpath(path, "bioclim_2.1_$(res[w.resolution])m.zip") - - if !isfile(path) - if !isfile(zip_file) - root = "https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/" - stem = "wc2.1_$(res[w.resolution])m_bio.zip" - r = HTTP.request("GET", root * stem) - open(zip_file, "w") do f - write(f, String(r.body)) - end - end - zf = ZipFile.Reader(zip_file) - file_to_read = - first(filter(f -> joinpath(path, f.name) == output_file, zf.files)) - - if !isfile(joinpath(path, file_to_read.name)) - write(joinpath(path, file_to_read.name), read(file_to_read)) - end - close(zf) - end - - return joinpath(path, file_to_read.name) -end - -function download_layer(l::EarthEnv, layer::Integer) - 1 ≤ layer ≤ 12 || throw(ArgumentError("The layer must be between 1 and 12")) - - path = SimpleSDMLayers.assets_path() - - root = "https://data.earthenv.org/consensus_landcover/" - stem = l.full ? "with_DISCover/consensus_full_class_$(layer).tif" : - "without_DISCover/Consensus_reduced_class_$(layer).tif" - filetype = l.full ? "complete" : "partial" - filename = "landcover_$(filetype)_$(layer).tif" - - if !isfile(joinpath(path, filename)) - layerrequest = HTTP.request("GET", root * stem) - open(joinpath(path, filename), "w") do layerfile - write(layerfile, String(layerrequest.body)) - end - end - - return joinpath(path, filename) -end - -function download_layer(::BioClim, layer::Integer) - 1 ≤ layer ≤ 19 || throw(ArgumentError("The layer must be between 1 and 19")) - path = SimpleSDMLayers.assets_path() - layer = lpad(layer, 2, "0") - filename = "CHELSA_bio10_$(layer).tif" - url_root = "https://envicloud.os.zhdk.cloud.switch.ch/chelsa/chelsa_V1/climatologies/bio/" - - filepath = joinpath(path, filename) - if !(isfile(filepath)) - res = HTTP.request("GET", url_root * filename) - open(filepath, "w") do f - write(f, String(res.body)) - end - end - return filepath -end diff --git a/src/datasets/earthenv/download.jl b/src/datasets/earthenv/download.jl new file mode 100644 index 00000000..672acd98 --- /dev/null +++ b/src/datasets/earthenv/download.jl @@ -0,0 +1,56 @@ +function _get_raster(::Type{EarthEnv}, ::Type{LandCover}, layer::Integer, full::Bool=false) + 1 ≤ layer ≤ 12 || throw(ArgumentError("The layer must be between 1 and 12")) + + filetype = full ? "complete" : "partial" + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(EarthEnv), _rasterpath(LandCover), filetype) + isdir(path) || mkpath(path) + + root = "https://data.earthenv.org/consensus_landcover/" + stem = full ? "with_DISCover/consensus_full_class_$(layer).tif" : + "without_DISCover/Consensus_reduced_class_$(layer).tif" + filename = "landcover_$(filetype)_$(layer).tif" + + if !isfile(joinpath(path, filename)) + Downloads.download(root * stem, joinpath(path, filename)) + end + + return joinpath(path, filename) +end + +function _get_raster(::Type{EarthEnv}, ::Type{HabitatHeterogeneity}, layer::Integer, resolution::Int64=25) + 1 ≤ layer ≤ 14 || throw(ArgumentError("The layer must be between 1 and 14")) + + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(EarthEnv), _rasterpath(HabitatHeterogeneity), string(resolution)) + isdir(path) || mkpath(path) + + # Layer names + layernames = ["cv", "evenness", "range", "shannon", "simpson", "std", "Contrast", "Correlation", "Dissimilarity", "Entropy", "Homogeneity", "Maximum", "Uniformity", "Variance"] + + # Get the correct value for the numerical precision + res = 16 + if resolution == 1 + if layer in [7, 9, 14] + res = 32 + end + end + if resolution == 5 + if layer in [1, 7, 9, 14] + res = 32 + end + end + if resolution == 25 + if layer in [7, 9, 10, 14] + res = 32 + end + end + + root = "https://data.earthenv.org/habitat_heterogeneity/" + stem = "$(resolution)km/$(layernames[layer])_01_05_$(resolution)km_uint$(res).tif" + filename = "$(layernames[layer])_$(resolution)km.tif" + + if !isfile(joinpath(path, filename)) + Downloads.download(root * stem, joinpath(path, filename)) + end + + return joinpath(path, filename) +end \ No newline at end of file diff --git a/src/datasets/earthenv/habitatheterogeneity.jl b/src/datasets/earthenv/habitatheterogeneity.jl new file mode 100644 index 00000000..f0aebb9e --- /dev/null +++ b/src/datasets/earthenv/habitatheterogeneity.jl @@ -0,0 +1,32 @@ +""" + SimpleSDMPredictor(::Type{EarthEnv}, ::Type{HabitatHeterogeneity}, layer::Integer=1; resolution::Int64=25, kwargs...) + +| Variable | Explanation | Measure | Value range | Relationship to heterogeneity | +| -------- | ------------------------ | ---------------------------------------------------------------- | ----------------------------------- | ----------------------------- | +| 1 | Coefficient of variation | Normalized dispersion of EVI | >=0 | Positive | +| 2 | Evenness | Evenness of EVI | >=0; <=1 | Positive | +| 3 | Range | Range of EVI | >=0 | Positive | +| 4 | Shannon | Diversity of EVI | >=0; <=ln(max # of different EVI) | Positive | +| 5 | Simpson | Diversity of EVI | >=0; <=1-1/(max # of different EVI) | Positive | +| 6 | Standard deviation | Dispersion of EVI | >=0 | Positive | +| 7 | Contrast | Exponentially weighted difference in EVI between adjacent pixels | >=0 | Positive | +| 8 | Correlation | Linear dependency of EVI on adjacent pixels | >=-1; <=1 | Nonlinear | +| 9 | Dissimilarity | Difference in EVI between adjacent pixels | >=0 | Positive | +| 10 | Entropy | Disorderliness of EVI | >=0 | Positive | +| 11 | Homogeneity | Similarity of EVI between adjacent pixels | >=0; <=1 | Negative | +| 12 | Maximum | Dominance of EVI combinations between adjacent pixels | >=0; <=1 | Negative | +| 13 | Uniformity | Orderliness of EVI | >=0; <=1 | Negative | +| 14 | Variance | Dispersion of EVI combinations between adjacent pixels | >=0 | Positive | + +These data are released under a CC-BY-NC license to Tuanmu & Jetz. +""" +function SimpleSDMPredictor(::Type{EarthEnv}, ::Type{HabitatHeterogeneity}, layer::Integer=1; resolution::Int64=25, kwargs...) + @assert resolution in [1, 5, 25] + file = _get_raster(EarthEnv, HabitatHeterogeneity, layer, resolution) + return geotiff(SimpleSDMPredictor, file; kwargs...) +end + +function SimpleSDMPredictor(::Type{EarthEnv}, ::Type{HabitatHeterogeneity}, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(EarthEnv, HabitatHeterogeneity, l; kwargs...) for l in layers] +end diff --git a/src/datasets/landcover.jl b/src/datasets/earthenv/landcover.jl similarity index 78% rename from src/datasets/landcover.jl rename to src/datasets/earthenv/landcover.jl index c0787ddd..49d5d7d2 100644 --- a/src/datasets/landcover.jl +++ b/src/datasets/earthenv/landcover.jl @@ -1,6 +1,6 @@ """ - landcover(layers::Vector{T}; full::Bool=false, path::AbstractString="assets") where {T <: Integer} + SimpleSDMPredictor(::Type{EarthEnv}, ::Type{LandCover}, layer::Integer=1; full::Bool=false, kwargs...) Download and prepare the EarthEnv consensus landcover data, and returns them as an array of `SimpleSDMPredictor`s. Layers are called by their number, from 1 to @@ -41,9 +41,12 @@ keeping the models stored is particularly important. These data are released under a CC-BY-NC license to Tuanmu & Jetz. """ -function landcover(layer::Integer; full::Bool=false, left=nothing, right=nothing, bottom=nothing, top=nothing) - return raster(SimpleSDMPredictor, EarthEnv(full), layer=layer, left=left, right=right, bottom=bottom, top=top) +function SimpleSDMPredictor(::Type{EarthEnv}, ::Type{LandCover}, layer::Integer=1; full::Bool=false, kwargs...) + file = _get_raster(EarthEnv, LandCover, layer, full) + return geotiff(SimpleSDMPredictor, file; kwargs...) end -landcover(layers::Vector{T}; args...) where {T <: Integer} = [landcover(l; args...) for l in layers] -landcover(layers::UnitRange{T}; args...) where {T <: Integer} = [landcover(l; args...) for l in layers] +function SimpleSDMPredictor(::Type{EarthEnv}, ::Type{LandCover}, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(EarthEnv, LandCover, l; kwargs...) for l in layers] +end diff --git a/src/datasets/geotiff.jl b/src/datasets/geotiff.jl index 7108531e..cc388042 100644 --- a/src/datasets/geotiff.jl +++ b/src/datasets/geotiff.jl @@ -8,7 +8,7 @@ function _find_span(n, m, M, pos) end """ - geotiff(::Type{LT}, tiff_file; left=nothing, right=nothing, bottom=nothing, top=nothing) where {LT <: SimpleSDMLayer} + geotiff(::Type{LT}, file, bandnumber::Integer=1; left=nothing, right=nothing, bottom=nothing, top=nothing) where {LT <: SimpleSDMLayer} The geotiff function reads a geotiff file, and returns it as a matrix of the correct type. The optional arguments `left`, `right`, `bottom`, and `left` are @@ -19,7 +19,8 @@ The first argument is the type of the `SimpleSDMLayer` to be returned. """ function geotiff( ::Type{LT}, - file::AbstractString; + file::AbstractString, + bandnumber::Integer=1; left = -180.0, right = 180.0, bottom = -90.0, @@ -36,11 +37,12 @@ function geotiff( wkt = ArchGDAL.getproj(dataset) # The data we need is pretty much always going to be stored in the first - # band, so this is what we will get for now. Note that this is not - # reading the data yet, just retrieving the metadata. - band = ArchGDAL.getband(dataset, 1) + # band, but this is not the case for the future WorldClim data. + band = ArchGDAL.getband(dataset, bandnumber) T = ArchGDAL.pixeltype(band) - nodata = convert(T, ArchGDAL.getnodatavalue(band)) + + # The nodata is not always correclty identified, so if it is not found, we assumed it is the smallest value in the band + nodata = isnothing(ArchGDAL.getnodatavalue(band)) ? convert(T, ArchGDAL.minimum(band)) : convert(T, ArchGDAL.getnodatavalue(band)) # Get the correct latitudes minlon = transform[1] @@ -71,7 +73,7 @@ function geotiff( # We are now ready to initialize a matrix of the correct type. buffer = Matrix{T}(undef, length(min_width:max_width), length(min_height:max_height)) - ArchGDAL.read!(dataset, buffer, 1, min_height:max_height, min_width:max_width) + ArchGDAL.read!(dataset, buffer, bandnumber, min_height:max_height, min_width:max_width) buffer = convert(Matrix{Union{Nothing,eltype(buffer)}}, rotl90(buffer)) replace!(buffer, nodata => nothing) LT(buffer, left_pos-0.5lon_stride, right_pos+0.5lon_stride, bottom_pos-0.5lat_stride, top_pos+0.5lat_stride) @@ -81,7 +83,13 @@ function geotiff( end -function geotiff(layer::SimpleSDMPredictor{T}, file::AbstractString; nodata::T=convert(T, -9999)) where {T <: Number} +""" + geotiff(file::AbstractString, layer::SimpleSDMPredictor{T}; nodata::T=convert(T, -9999)) where {T <: Number} + +Write a single `layer` to a `file`, where the `nodata` field is set to an +arbitrary value. +""" +function geotiff(file::AbstractString, layer::SimpleSDMPredictor{T}; nodata::T=convert(T, -9999)) where {T <: Number} array = layer.grid replace!(array, nothing => NaN) array = convert(Matrix{T}, array) @@ -119,8 +127,79 @@ function geotiff(layer::SimpleSDMPredictor{T}, file::AbstractString; nodata::T=c # Write ! ArchGDAL.write(dataset, file, driver=ArchGDAL.getdriver("GTiff"), options=["COMPRESS=LZW"]) end + return file +end + +function _prepare_layer_for_burnin(layer::T) where {T <: SimpleSDMLayer} + @assert eltype(layer) <: Number + array = layer.grid + replace!(array, nothing => NaN) + array = convert(Matrix{eltype(layer)}, array) + dtype = eltype(array) + array_t = reverse(permutedims(array, [2, 1]); dims=2) + return array_t end -function geotiff(layer::SimpleSDMResponse{T}, file::AbstractString; nodata::T=convert(T, -9999)) where {T <: Number} - geotiff(convert(SimpleSDMPredictor, layer), file; nodata=nodata) +""" + geotiff(file::AbstractString, layers::Vector{SimpleSDMPredictor{T}}; nodata::T=convert(T, -9999)) where {T <: Number} + +Stores a series of `layers` in a `file`, where every layer in a band. See +`geotiff` for other options. +""" +function geotiff(file::AbstractString, layers::Vector{SimpleSDMPredictor{T}}; nodata::T=convert(T, -9999)) where {T <: Number} + bands = 1:length(layers) + _layers_are_compatible(layers) + width, height = size(_prepare_layer_for_burnin(layers[1])) + + # Geotransform + gt = zeros(Float64, 6) + gt[1] = layers[1].left + gt[2] = 2stride(layers[1], 1) + gt[3] = 0.0 + gt[4] = layers[1].top + gt[5] = 0.0 + gt[6] = -2stride(layers[1], 2) + + # Write + prefix = first(split(last(splitpath(file)), '.')) + ArchGDAL.create(prefix, + driver=ArchGDAL.getdriver("MEM"), + width=width, height=height, + nbands=length(layers), dtype=T, + options=["COMPRESS=LZW"]) do dataset + + for i in 1:length(bands) + band = ArchGDAL.getband(dataset, i) + + # Write data to band + ArchGDAL.write!(band, _prepare_layer_for_burnin(layers[i])) + + # Write nodata and projection info + ArchGDAL.setnodatavalue!(band, nodata) + end + ArchGDAL.setgeotransform!(dataset, gt) + ArchGDAL.setproj!(dataset, "EPSG:4326") + + # Write ! + ArchGDAL.write(dataset, file, driver=ArchGDAL.getdriver("GTiff"), options=["COMPRESS=LZW"]) + end + return file +end + +""" + geotiff(file::AbstractString, layer::SimpleSDMResponse{T}; nodata::T=convert(T, -9999)) where {T <: Number} + +Write a single `SimpleSDMResponse` layer to a file. +""" +function geotiff(file::AbstractString, layer::SimpleSDMResponse{T}; nodata::T=convert(T, -9999)) where {T <: Number} + return geotiff(file, convert(SimpleSDMPredictor, layer); nodata=nodata) +end + +""" + geotiff(file::AbstractString, layers::Vector{SimpleSDMResponse{T}}; nodata::T=convert(T, -9999)) where {T <: Number} + +Write a vector of `SimpleSDMResponse` layers to bands in a file. +""" +function geotiff(file::AbstractString, layers::Vector{SimpleSDMResponse{T}}; nodata::T=convert(T, -9999)) where {T <: Number} + return geotiff(file, convert.(SimpleSDMPredictor, layers); nodata=nodata) end \ No newline at end of file diff --git a/src/datasets/raster.jl b/src/datasets/raster.jl deleted file mode 100644 index 040d4be9..00000000 --- a/src/datasets/raster.jl +++ /dev/null @@ -1,16 +0,0 @@ -function raster(::Type{IT}, source::ST; layer::Integer=1, left=nothing, right=nothing, bottom=nothing, top=nothing) where {IT <: SimpleSDMLayer, ST <: SimpleSDMSource} - file = download_layer(source, layer) - left = isnothing(left) ? minimum(longitudes(ST)) : left - right = isnothing(right) ? maximum(longitudes(ST)) : right - bottom = isnothing(bottom) ? minimum(latitudes(ST)) : bottom - top = isnothing(top) ? maximum(latitudes(ST)) : top - return geotiff(IT, ST, file; left=left, right=right, bottom=bottom, top=top) -end - -function raster(::Type{IT}, source::ST, file; left=nothing, right=nothing, bottom=nothing, top=nothing) where {IT <: SimpleSDMLayer, ST <: SimpleSDMSource} - left = isnothing(left) ? minimum(longitudes(ST)) : left - right = isnothing(right) ? maximum(longitudes(ST)) : right - bottom = isnothing(bottom) ? minimum(latitudes(ST)) : bottom - top = isnothing(top) ? maximum(latitudes(ST)) : top - return geotiff(IT, ST, file; left=left, right=right, bottom=bottom, top=top) -end diff --git a/src/datasets/sources.jl b/src/datasets/sources.jl deleted file mode 100644 index 54ef31f4..00000000 --- a/src/datasets/sources.jl +++ /dev/null @@ -1,26 +0,0 @@ -abstract type SimpleSDMSource end - -latitudes(::Type{T}) where {T <: SimpleSDMSource} = (-90.0, 90.0) -longitudes(::Type{T}) where {T <: SimpleSDMSource} = (-180.0, 180.0) - -struct WorldClim <: SimpleSDMSource - resolution::AbstractFloat - function WorldClim(resolution::AbstractFloat) - resolution ∈ [2.5, 5.0, 10.0] || throw(ArgumentError("The resolution argument ($(resolution)) must be 2.5, 5, or 10")) - return new(resolution) - end -end - -WorldClim() = WorldClim(10.0) - -struct BioClim <: SimpleSDMSource end -longitudes(::Type{BioClim}) = (-180.0001388888, 179.9998611111) -latitudes(::Type{BioClim}) = (-90.0001388888, 83.9998611111) - -struct EarthEnv <: SimpleSDMSource - full::Bool -end - -EarthEnv() = EarthEnv(false) -latitudes(::Type{EarthEnv}) = (-56.0, 90.0) -longitudes(::Type{EarthEnv}) = (-180.0, 180.0) diff --git a/src/datasets/types.jl b/src/datasets/types.jl new file mode 100644 index 00000000..e2901dca --- /dev/null +++ b/src/datasets/types.jl @@ -0,0 +1,200 @@ + +""" + LayerProvider + +A `LayerProvider` is an abstract type used to dispatch the correct call of +`SimpleSDMPredictor` to a specific dataset. A dataset is specified by a +`LayerProvider` and a `LayerDataset`, as well as optionally one or multiple +layers, and future climate information. +""" +abstract type LayerProvider end + +""" + LayerDataset + +A `LayerDataset` is a specific set of rasters provided by a `LayerProvider`. +""" +abstract type LayerDataset end + +""" + WorldClim + +TODO WorldClim + +This provider currently offers `BioClim` data, both historical and future under +`CMIP6`. +""" +struct WorldClim <: LayerProvider end + +""" + CHELSA + +TODO CHELSA + +This provider currently offers `BioClim` data, both historical and future under +`CMIP5`. +""" +struct CHELSA <: LayerProvider end + +""" + EarthEnv + +Data from the earthenv project, all released under a CC-BY-NC license to Tuanmu +& Jetz. This provider currently offers `LandCover` and `HabitatHeterogeneity` +rasters. +""" +struct EarthEnv <: LayerProvider end + +""" + BioClim + +A list of 19 bioclimatic variables derived from the monthly temperature and +precipitation data. This dataset is provided by `WorldClim` and `CHELSA`, both +of which offer future versions under `CMIP5` and `CMIP6` models. + +| Variable | Description | +| ------ | ------ | +| 1 | Annual Mean Temperature | +| 2 | Mean Diurnal Range (Mean of monthly (max temp - min temp)) | +| 3 | Isothermality (BIO2/BIO7) (* 100) | +| 4 | Temperature Seasonality (standard deviation *100) | +| 5 | Max Temperature of Warmest Month | +| 6 | Min Temperature of Coldest Month | +| 7 | Temperature Annual Range (BIO5-BIO6) | +| 8 | Mean Temperature of Wettest Quarter | +| 9 | Mean Temperature of Driest Quarter | +| 10 | Mean Temperature of Warmest Quarter | +| 11 | Mean Temperature of Coldest Quarter | +| 12 | Annual Precipitation | +| 13 | Precipitation of Wettest Month | +| 14 | Precipitation of Driest Month | +| 15 | Precipitation Seasonality (Coefficient of Variation) | +| 16 | Precipitation of Wettest Quarter | +| 17 | Precipitation of Driest Quarter | +| 18 | Precipitation of Warmest Quarter | +| 19 | Precipitation of Coldest Quarter | +""" +struct BioClim <: LayerDataset end + +""" + LandCover + +Information on land cover, currently only provided by `EarthEnv`. +""" +struct LandCover <: LayerDataset end + +""" + HabitatHeterogeneity + +Information on habitat heterogeneity, currently only provided by `EarthEnv`. +""" +struct HabitatHeterogeneity <: LayerDataset end + +""" + SharedSocioeconomicPathway + +Enumeration of the four SSPs, which can be listed with +`instances(SharedSocioeconomicPathway)`. These are meant to be used with `CMIP6` +models. +""" +@enum SharedSocioeconomicPathway SSP126 SSP245 SSP370 SSP585 + +""" + RepresentativeConcentrationPathway + +Enumeration of the four RCPs, which can be listed with +`instances(RepresentativeConcentrationPathway)`. These are meant to be used with +`CMIP5` models. +""" +@enum RepresentativeConcentrationPathway RCP26 RCP45 RCP60 RCP85 + +""" + CMIP6 + +Enumeration of the models from CMIP6, which can be listed with +`instances(CMIP6)`. +""" +@enum CMIP6 BCCCSM2MR CNRMCMS61 CNRMESM21 CanESM5 GFDLESM4 IPSLCM6ALR MIROCES2L MIROC6 MRIESM2 + +""" + CMIP5 + +Enumeration of the models from CMIP5, which can be listed with +`instances(CMIP5)`. +""" +@enum CMIP5 ACCESS10 BNUESM CCSM4 CESM1BGC CESM1CAM5 CMCCCMS CMCCCM CNRMCM5 CSIROMK360 CanESM2 FGOALSG2 FIOESM GFDLCM3 GFDLESM2G GFDLESM2M GISSE2HCC GISSE2H GISSE2RCC GISSE2R HADGEM2AO HADGEM2CC IPSLCM5ALR IPSLCM5AMR MIROCESMCHEM MIROCESM MIROC5 MPIESMLR MPIESMMR MRICGCM3 MRIESM1 NORESM1M BCCCSM11 INMCM4 + + +# Provider paths +_rasterpath(::Type{WorldClim}) = "WorldClim" +_rasterpath(::Type{CHELSA}) = "CHELSA" +_rasterpath(::Type{EarthEnv}) = "EarthEnv" + +# Dataset paths +_rasterpath(::Type{BioClim}) = "BioClim" +_rasterpath(::Type{LandCover}) = "LandCover" +_rasterpath(::Type{HabitatHeterogeneity}) = "HabitatHeterogeneity" + +# Future paths +_rasterpath(model::CMIP6) = _rasterpath(Val{model}) +_rasterpath(model::CMIP5) = _rasterpath(Val{model}) +_rasterpath(ssp::SharedSocioeconomicPathway) = _rasterpath(Val{ssp}) +_rasterpath(rcp::RepresentativeConcentrationPathway) = _rasterpath(Val{rcp}) + +# SSP path +_rasterpath(::Type{Val{SSP126}}) = "ssp126" +_rasterpath(::Type{Val{SSP245}}) = "ssp245" +_rasterpath(::Type{Val{SSP370}}) = "ssp370" +_rasterpath(::Type{Val{SSP585}}) = "ssp585" + +# RCP path +_rasterpath(::Type{Val{RCP26}}) = "rcp26" +_rasterpath(::Type{Val{RCP45}}) = "rcp45" +_rasterpath(::Type{Val{RCP60}}) = "rcp60" +_rasterpath(::Type{Val{RCP85}}) = "rcp85" + +# CMIP6 path +_rasterpath(::Type{Val{BCCCSM2MR}}) = "BCC-CSM2-MR" +_rasterpath(::Type{Val{CNRMCMS61}}) = "CNRM-CM6-1" +_rasterpath(::Type{Val{CNRMESM21}}) = "CNRM-ESM2-1" +_rasterpath(::Type{Val{CanESM5}}) = "CanESM5" +_rasterpath(::Type{Val{GFDLESM4}}) = "GFDL-ESM4" +_rasterpath(::Type{Val{IPSLCM6ALR}}) = "IPSL-CM6A-LR" +_rasterpath(::Type{Val{MIROCES2L}}) = "MIROC-ES2L" +_rasterpath(::Type{Val{MIROC6}}) = "MIROC6" +_rasterpath(::Type{Val{MRIESM2}}) = "MRI-ESM2-0" + +# CMIP5 path +_rasterpath(::Type{Val{ACCESS10}}) = "ACCESS1-0" +_rasterpath(::Type{Val{BNUESM}}) = "BNU-ESM" +_rasterpath(::Type{Val{CCSM4}}) = "CCSM4" +_rasterpath(::Type{Val{CESM1BGC}}) = "CESM1-BGC" +_rasterpath(::Type{Val{CESM1CAM5}}) = "CESM1-CAM5" +_rasterpath(::Type{Val{CMCCCMS}}) = "CMCC-CMS" +_rasterpath(::Type{Val{CMCCCM}}) = "CMCC-CM" +_rasterpath(::Type{Val{CNRMCM5}}) = "CNRM-CM5" +_rasterpath(::Type{Val{CSIROMK360}}) = "CSIRO-Mk3-6-0" +_rasterpath(::Type{Val{CanESM2}}) = "CanESM2" +_rasterpath(::Type{Val{FGOALSG2}}) = "FGOALS-g2" +_rasterpath(::Type{Val{FIOESM}}) = "FIO-ESM" +_rasterpath(::Type{Val{GFDLCM3}}) = "GFDL-CM3" +_rasterpath(::Type{Val{GFDLESM2G}}) = "GFDL-ESM2G" +_rasterpath(::Type{Val{GFDLESM2M}}) = "GFDL-ESM2M" +_rasterpath(::Type{Val{GISSE2HCC}}) = "GISS-E2-H-CC" +_rasterpath(::Type{Val{GISSE2H}}) = "GISS-E2-H" +_rasterpath(::Type{Val{GISSE2RCC}}) = "GISS-E2-R-CC" +_rasterpath(::Type{Val{GISSE2R}}) = "GISS-E2-R" +_rasterpath(::Type{Val{HADGEM2AO}}) = "HadGEM2-AO" +_rasterpath(::Type{Val{HADGEM2CC}}) = "HadGEM2-CC" +_rasterpath(::Type{Val{IPSLCM5ALR}}) = "IPSL-CM5A-LR" +_rasterpath(::Type{Val{IPSLCM5AMR}}) = "IPSL-CM5A-MR" +_rasterpath(::Type{Val{MIROCESMCHEM}}) = "MIROC-ESM-CHEM" +_rasterpath(::Type{Val{MIROCESM}}) = "MIROC-ESM" +_rasterpath(::Type{Val{MIROC5}}) = "MIROC5" +_rasterpath(::Type{Val{MPIESMLR}}) = "MPI-ESM-LR" +_rasterpath(::Type{Val{MPIESMMR}}) = "MPI-ESM-MR" +_rasterpath(::Type{Val{MRICGCM3}}) = "MRI-CGCM3" +_rasterpath(::Type{Val{MRIESM1}}) = "MRI-ESM1" +_rasterpath(::Type{Val{NORESM1M}}) = "NorESM1-M" +_rasterpath(::Type{Val{BCCCSM11}}) = "bcc-csm1-1" +_rasterpath(::Type{Val{INMCM4}}) = "inmcm4" diff --git a/src/datasets/worldclim.jl b/src/datasets/worldclim/bioclim.jl similarity index 59% rename from src/datasets/worldclim.jl rename to src/datasets/worldclim/bioclim.jl index acddcddc..2274e5ee 100644 --- a/src/datasets/worldclim.jl +++ b/src/datasets/worldclim/bioclim.jl @@ -1,5 +1,5 @@ """ - worldclim(layer::Integer; resolution::Float64=10.0, left=nothing, right=nothing, bottom=nothing, top=nothing) + SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, layer::Integer=1; resolution::Float64=10.0, left=nothing, right=nothing, bottom=nothing, top=nothing) Download and prepare a WorldClim 2.1 bioclimatic variable, and returns it as an `SimpleSDMPredictor`. Layers are called by their number, from 1 to 19. The list @@ -42,9 +42,30 @@ will be much faster. Original data: https://www.worldclim.org/data/worldclim21.html """ -function worldclim(layer::Integer; resolution::Float64=10.0, left=nothing, right=nothing, bottom=nothing, top=nothing) - return raster(SimpleSDMPredictor, WorldClim(resolution), layer=layer, left=left, right=right, bottom=bottom, top=top) +function SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, layer::Integer=1; resolution::Float64=10.0, kwargs...) + @assert resolution in [2.5, 5.0, 10.0] + file = _get_raster(WorldClim, BioClim, layer, resolution) + return geotiff(SimpleSDMPredictor, file; kwargs...) end -worldclim(layers::Vector{T}; args...) where {T <: Integer} = [worldclim(l; args...) for l in layers] -worldclim(layers::UnitRange{T}; args...) where {T <: Integer} = [worldclim(l; args...) for l in layers] +function SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(WorldClim, BioClim, l; kwargs...) for l in layers] +end + +""" + SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, mod::CMIP6, fut::SharedSocioeconomicPathway, layer::Integer=1; year="2021-2040", resolution::Float64=10.0, kwargs...) + +Future biolcim data, where year is in "2021-2040", "2041-2060", "2061-2080", "2081-2100" . +""" +function SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, mod::CMIP6, fut::SharedSocioeconomicPathway, layer::Integer=1; year="2021-2040", resolution::Float64=10.0, kwargs...) + @assert year in ["2021-2040", "2041-2060", "2061-2080", "2081-2100"] + @assert resolution in [2.5, 5.0, 10.0] + file = _get_raster(WorldClim, BioClim, mod, fut, resolution, year) + return geotiff(SimpleSDMPredictor, file, layer; kwargs...) +end + +function SimpleSDMPredictor(::Type{WorldClim}, ::Type{BioClim}, mod::CMIP6, fut::SharedSocioeconomicPathway, layers::AbstractArray; kwargs...) + @assert eltype(layers) <: Integer + return [SimpleSDMPredictor(WorldClim, BioClim, mod, fut, l; kwargs...) for l in layers] +end \ No newline at end of file diff --git a/src/datasets/worldclim/download.jl b/src/datasets/worldclim/download.jl new file mode 100644 index 00000000..e8b3afe5 --- /dev/null +++ b/src/datasets/worldclim/download.jl @@ -0,0 +1,53 @@ +function _get_raster(::Type{WorldClim}, ::Type{BioClim}, layer::Integer, resolution=10.0) + 1 ≤ layer ≤ 19 || throw(ArgumentError("The layer must be between 1 and 19")) + + res = Dict(2.5 => "2.5", 5.0 => "5", 10.0 => "10") + + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(WorldClim), _rasterpath(BioClim), res[resolution]) + isdir(path) || mkpath(path) + + output_file = joinpath(path, "wc2.1_$(res[resolution])m_bio_$(layer).tif") + zip_file = joinpath(path, "bioclim_2.1_$(res[resolution])m.zip") + + if !isfile(path) + if !isfile(zip_file) + root = "https://biogeo.ucdavis.edu/data/worldclim/v2.1/base/" + stem = "wc2.1_$(res[resolution])m_bio.zip" + Downloads.download(root * stem, zip_file) + end + zf = ZipFile.Reader(zip_file) + file_to_read = + first(filter(f -> joinpath(path, f.name) == output_file, zf.files)) + + if !isfile(joinpath(path, file_to_read.name)) + write(joinpath(path, file_to_read.name), read(file_to_read)) + end + close(zf) + end + + return joinpath(path, file_to_read.name) +end + +function _get_raster(::Type{WorldClim}, ::Type{BioClim}, mod::CMIP6, fut::SharedSocioeconomicPathway, resolution=10.0, year="2021-2040") + res = Dict(2.5 => "2.5", 5.0 => "5", 10.0 => "10") + + path = joinpath(SimpleSDMLayers._layers_assets_path, _rasterpath(WorldClim), _rasterpath(BioClim), _rasterpath(mod), _rasterpath(fut), year, res[resolution]) + isdir(path) || mkpath(path) + + zip_file = joinpath(path, "$(res[resolution])m_$(_rasterpath(mod))_$(_rasterpath(fut)).zip") + if !isfile(path) + if !isfile(zip_file) + root = "https://biogeo.ucdavis.edu/data/worldclim/v2.1/fut/" + stem = "$(res[resolution])m/wc2.1_$(res[resolution])m_bioc_$(_rasterpath(mod))_$(_rasterpath(fut))_$(year).zip" + Downloads.download(root * stem, zip_file) + end + zf = ZipFile.Reader(zip_file) + file_to_read = only(zf.files) + if !isfile(joinpath(path, "stack.tif")) + write(joinpath(path, "stack.tif"), read(file_to_read)) + end + close(zf) + end + + return joinpath(path, "stack.tif") +end diff --git a/src/recipes/recipes.jl b/src/recipes/recipes.jl index 47824204..931910c0 100644 --- a/src/recipes/recipes.jl +++ b/src/recipes/recipes.jl @@ -14,14 +14,6 @@ test 1 longitudes(layer), latitudes(layer), lg elseif get(plotattributes, :seriestype, :histogram) in [:histogram, :density] collect(layer) - elseif get(plotattributes, :seriestype, :surface) in [:surface, :wireframe] - aspect_ratio --> 1 - xlims --> extrema(longitudes(layer)) - ylims --> extrema(latitudes(layer)) - lg = copy(layer.grid) - replace!(lg, nothing => minimum(layer)) - lg = convert(Matrix{Float64}, lg) - longitudes(layer), latitudes(layer), lg end end diff --git a/test/ascii.jl b/test/ascii.jl index 7787dffa..4f2180de 100644 --- a/test/ascii.jl +++ b/test/ascii.jl @@ -8,11 +8,12 @@ M[rand(Bool, size(M))] .= nothing S = SimpleSDMPredictor(M, 0.0, 2.0, 0.0, 1.0) -SimpleSDMLayers.ascii(S, "test.asc") +SimpleSDMLayers.ascii("test.asc", S) @test isfile("test.asc") U = SimpleSDMLayers.ascii("test.asc") +@test typeof(S) <: SimpleSDMPredictor @test all(S.grid .== U.grid) @test S.left == U.left @test S.bottom == U.bottom @@ -20,7 +21,7 @@ U = SimpleSDMLayers.ascii("test.asc") @test S.top == U.top @test size(S) == size(U) -geotiff(U, "test.tiff") +geotiff("test.tiff", U) @test isfile("test.tiff") rm("test.asc") diff --git a/test/chelsa.jl b/test/chelsa.jl index e76cdb8d..9ad03b6d 100644 --- a/test/chelsa.jl +++ b/test/chelsa.jl @@ -2,7 +2,23 @@ module SSLTestCHELSA using SimpleSDMLayers using Test -lc1 = bioclim(1; left=-5.0, right=7.0, bottom=30.0, top=45.0) +lc1 = SimpleSDMPredictor(CHELSA, BioClim, 1; left=-5.0, right=7.0, bottom=30.0, top=45.0) @test typeof(lc1) <: SimpleSDMPredictor +lc1 = SimpleSDMPredictor(CHELSA, BioClim, 1:2; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(CHELSA, BioClim, [1,2,3]; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(CHELSA, BioClim, CanESM2, RCP26, 1; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test typeof(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(CHELSA, BioClim, CanESM2, RCP26, 1:2; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(CHELSA, BioClim, CanESM2, RCP26, [1,2,3]; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(lc1) <: SimpleSDMPredictor + + end diff --git a/test/coarsen.jl b/test/coarsen.jl index 1a042208..1891ca24 100644 --- a/test/coarsen.jl +++ b/test/coarsen.jl @@ -17,7 +17,7 @@ M = SimpleSDMResponse(["a" nothing "b" "c"; "d" "e" "f" "g"; "d" "e" nothing "g" @test coarsen(M, x -> reduce(*, x), (2,2)).grid == ["ade" "bfcg"; "dex" "ygz"] # Should work on a real-world example for a series of functions -temperature = worldclim(1) +temperature = SimpleSDMPredictor(WorldClim, BioClim, 1) for f in [minimum, maximum, mean, median] t_coarse = coarsen(temperature, f, (10, 10)) @test typeof(t_coarse) <: SimpleSDMPredictor diff --git a/test/dataframes.jl b/test/dataframes.jl index 2020353a..838daf11 100644 --- a/test/dataframes.jl +++ b/test/dataframes.jl @@ -3,7 +3,7 @@ using SimpleSDMLayers using DataFrames using Test -temperature = worldclim(1) +temperature = SimpleSDMPredictor(WorldClim, BioClim, 1) df = DataFrame(latitude = [0.0, 1.0], longitude = [30.0, 31.0], values = [42.0, 15.0]) diff --git a/test/dataread.jl b/test/dataread.jl index 1271ea6a..df4df3c7 100644 --- a/test/dataread.jl +++ b/test/dataread.jl @@ -2,10 +2,12 @@ module SSLTestDataRead using SimpleSDMLayers using Test -file = joinpath(dirname(pathof(SimpleSDMLayers)), "..", "data", "connectivity.tiff") -mp = geotiff(SimpleSDMResponse, file) +l = SimpleSDMPredictor(WorldClim, BioClim, 1) +f = tempname() +geotiff(f, l) +mp = geotiff(SimpleSDMResponse, f) @test typeof(mp) <: SimpleSDMResponse -@test size(mp) == (1255, 1206) +@test size(mp) == size(l) end diff --git a/test/earthenv.jl b/test/earthenv.jl new file mode 100644 index 00000000..6ae8222f --- /dev/null +++ b/test/earthenv.jl @@ -0,0 +1,20 @@ +module SSLTestLandCover +using SimpleSDMLayers +using Test + +lc1 = SimpleSDMPredictor(EarthEnv, LandCover, 1; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test typeof(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(EarthEnv, LandCover, 1; full=true, left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test typeof(lc1) <: SimpleSDMPredictor + +lc1 = SimpleSDMPredictor(EarthEnv, LandCover, 1:3; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(lc1) <: SimpleSDMPredictor + +he1 = SimpleSDMPredictor(EarthEnv, HabitatHeterogeneity, 1; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test typeof(he1) <: SimpleSDMPredictor + +he1 = SimpleSDMPredictor(EarthEnv, HabitatHeterogeneity, 1:3; left=-5.0, right=7.0, bottom=30.0, top=45.0) +@test eltype(he1) <: SimpleSDMPredictor + +end diff --git a/test/gbif.jl b/test/gbif.jl index 33fbffae..ecff8a9e 100644 --- a/test/gbif.jl +++ b/test/gbif.jl @@ -3,7 +3,7 @@ using SimpleSDMLayers using GBIF using Test -temperature = worldclim(1) +temperature = SimpleSDMPredictor(WorldClim, BioClim, 1) kingfisher = GBIF.taxon("Megaceryle alcyon", strict=true) diff --git a/test/landcover.jl b/test/landcover.jl deleted file mode 100644 index 9779dac4..00000000 --- a/test/landcover.jl +++ /dev/null @@ -1,8 +0,0 @@ -module SSLTestLandCover -using SimpleSDMLayers -using Test - -lc1 = landcover(1; left=-5.0, right=7.0, bottom=30.0, top=45.0) -@test typeof(lc1) <: SimpleSDMPredictor - -end diff --git a/test/overloads.jl b/test/overloads.jl index ed61640f..65e9084f 100644 --- a/test/overloads.jl +++ b/test/overloads.jl @@ -48,10 +48,10 @@ V = collect(Z) @test typeof(V) == Vector{Float64} # hcat / vcat -l1 = worldclim(1, left=0.0, right=10.0, bottom=0.0, top=10.0) -l2 = worldclim(1, left=0.0, right=10.0, bottom=10.0, top=20.0) -l3 = worldclim(1, left=10.0, right=20.0, bottom=0.0, top=10.0) -l4 = worldclim(1, left=10.0, right=20.0, bottom=10.0, top=20.0) +l1 = SimpleSDMPredictor(WorldClim, BioClim, 1; left=0.0, right=10.0, bottom=0.0, top=10.0) +l2 = SimpleSDMPredictor(WorldClim, BioClim, 1; left=0.0, right=10.0, bottom=10.0, top=20.0) +l3 = SimpleSDMPredictor(WorldClim, BioClim, 1; left=10.0, right=20.0, bottom=0.0, top=10.0) +l4 = SimpleSDMPredictor(WorldClim, BioClim, 1; left=10.0, right=20.0, bottom=10.0, top=20.0) ml1 = hcat(l1, l3) vl1 = vcat(l1, l2) diff --git a/test/plots.jl b/test/plots.jl index 756611c6..69a3f0f1 100644 --- a/test/plots.jl +++ b/test/plots.jl @@ -3,11 +3,11 @@ using SimpleSDMLayers using Test using Plots -temperature, precipitation = worldclim([1,12]) +temperature, precipitation = SimpleSDMPredictor(WorldClim, BioClim, [1,12]) ispath("gallery") || mkpath("gallery") -chelsa1 = bioclim(1; left=-11.0, right=31.1, bottom=29.0, top=71.1) +chelsa1 = SimpleSDMPredictor(CHELSA, BioClim, 1; left=-11.0, right=31.1, bottom=29.0, top=71.1) n_chelsa1 = zeros(Float32, size(chelsa1)); for (i,e) in enumerate(chelsa1.grid) n_chelsa1[i] = isnothing(e) ? NaN : Float32(e) @@ -18,19 +18,19 @@ plot(chelsa1, c=:heat, title="Temperature from CHELSA", frame=:box, ylabel= "Latitude") savefig(joinpath("gallery", "range-comparison-chelsa.png")) -wc1 = worldclim(1; left=-11.0, right=31.1, bottom=29.0, top=71.1) +wc1 = SimpleSDMPredictor(WorldClim, BioClim, 1; left=-11.0, right=31.1, bottom=29.0, top=71.1) plot(wc1, c=:heat, title="Temperature from worldclim @ 10", frame=:box, xlabel = "Longitude", ylabel= "Latitude") savefig(joinpath("gallery", "range-comparison-worldclim-10.png")) -wc1 = worldclim(1; resolution=5.0, left=-11.0, right=31.1, bottom=29.0, top=71.1) +wc1 = SimpleSDMPredictor(WorldClim, BioClim, 1; resolution=5.0, left=-11.0, right=31.1, bottom=29.0, top=71.1) plot(wc1, c=:heat, title="Temperature from worldclim @ 5", frame=:box, xlabel = "Longitude", ylabel= "Latitude") savefig(joinpath("gallery", "range-comparison-worldclim-5.png")) -lc1 = landcover(1; left=-11.0, right=31.1, bottom=29.0, top=71.1) +lc1 = SimpleSDMPredictor(EarthEnv, LandCover, 1; left=-11.0, right=31.1, bottom=29.0, top=71.1) n_lc1 = zeros(Float32, size(lc1)); for (i,e) in enumerate(lc1.grid) n_lc1[i] = isnothing(e) ? NaN : Float32(e) @@ -77,18 +77,6 @@ xaxis!("Temperature") yaxis!("Precipitation") savefig(joinpath("gallery", "scatter-2d.png")) -t_europe = worldclim(1; left=-11.0, right=31.1, bottom=29.0, top=71.1) - -wireframe(t_europe, c=:thermal, title="Temperature", - xlabel = "Longitude", - ylabel= "Latitude", camera=(30, 30)) -savefig(joinpath("gallery", "persp_30_30.png")) - -wireframe(t_europe, c=:thermal, title="Temperature", - xlabel = "Longitude", - ylabel= "Latitude", camera=(40, 20)) -savefig(joinpath("gallery", "persp_40_20.png")) - a = rand(Bool, 100, 100) a = convert(Matrix{Union{Bool,Nothing}}, a) a[rand(eachindex(a), 100)] .= nothing diff --git a/test/runtests.jl b/test/runtests.jl index cc1030ce..3fb26d6c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -15,7 +15,7 @@ tests = [ "generated" => "generated.jl", "import" => "dataread.jl", "worldclim" => "worldclim.jl", - "landcover" => "landcover.jl", + "earthenv" => "earthenv.jl", "chelsa" => "chelsa.jl", "coarsen" => "coarsen.jl", "plotting" => "plots.jl", diff --git a/test/worldclim.jl b/test/worldclim.jl index 426ae9f5..170cfea7 100644 --- a/test/worldclim.jl +++ b/test/worldclim.jl @@ -2,19 +2,28 @@ module SSLTestWorldclim using SimpleSDMLayers using Test -wc1and2 = worldclim([1,2]) +wc1and2 = SimpleSDMPredictor(WorldClim, BioClim, [1,2]) @test typeof(first(wc1and2)) <: SimpleSDMPredictor -temp = worldclim(1) +temp = SimpleSDMPredictor(WorldClim, BioClim, 1) @test size(temp) == (1080, 2160) @test round(first(stride(temp)); digits=2) ≈ round(last(stride(temp)); digits=2) @test length(longitudes(temp)) == 2160 @test length(latitudes(temp)) == 1080 -wc3 = worldclim(3) +wc3 = SimpleSDMPredictor(WorldClim, BioClim, 3) @test typeof(wc3) <: SimpleSDMPredictor -wcrange = worldclim(1:5) +wcrange = SimpleSDMPredictor(WorldClim, BioClim, 1:5) @test eltype(wcrange) <: SimpleSDMPredictor +future = SimpleSDMPredictor(WorldClim, BioClim, MIROC6, SSP126, 1) +@test typeof(future) <: SimpleSDMPredictor + +future = SimpleSDMPredictor(WorldClim, BioClim, CanESM5, SSP126, 1:3) +@test eltype(future) <: SimpleSDMPredictor + +future = SimpleSDMPredictor(WorldClim, BioClim, CanESM5, SSP370, [1,2,5]; year="2061-2080") +@test eltype(future) <: SimpleSDMPredictor + end