library(data.table)
library(readxl)
library(dplyr)
The Tree Globally Observed Environmental Ranges (TreeGOER) database provides information for most known tree species of their environmental ranges for 38 bioclimatic, eight soil and three topographic variables.
In this document, I show how outputs generated by the GlobalUsefulNativeTrees (GlobUNT) online database can be filtered to match bioclimatic conditions of the planting site. Bioclimatic conditions in historical (baseline) and future climates can be sourced from the CitiesGOER database for 52,602 cities (their distribution is shown here).
A similar algorithm and R script is used internally in GlobUNT to filter species for 10 preselected variables (nine of these correspond to the variables used in case studies of the Tree Globally Observed Environmental Ranges (TreeGOER), so you could check the justification there).
The algorithm calculates a Climate Score for each species whereby:
This algorithm is essentially the same as the BIOCLIM model (see Booth et al. 2014 and Booth 2018).
# treegoer.file <- choose.files() # Provide the location where the TreeGOER file (https://zenodo.org/records/10008994: TreeGOER_2023.txt) was downloaded to
treegoer <- fread(treegoer.file, sep="|", encoding="UTF-8")
nrow(treegoer)
## [1] 2450209
length(unique(treegoer$species))
## [1] 48129
We will calculate climate scores for the baseline climate and for median climates projected for the 2050s for Shared Socio-economic Pathway 3-7.0. Planting conditions will be inferred from the CitiesGOER database for Nairobi.
# Provide the locations where the CitiesGOER files (https://zenodo.org/records/10004594: CitiesGOER_baseline.xlsx and CitiesGOER_2050s_ssp370.xlsx) were downloaded to
site.base <- data.frame(read_excel(baseline.file,
sheet="Cities data",
skip=6))
nrow(site.base)
## [1] 52602
head(site.base)
## SEQ2 SEQ1 Geoname.ID Name ASCII.Name Country.Code
## 1 1 135560 1127768 Aībak Aibak AF
## 2 2 135566 1149052 ‘Alāqahdārī Dīshū 'Alaqahdari Dishu AF
## 3 3 1843 1148709 Anār Darah Anar Darah AF
## 4 4 45163 1148658 Andkhōy Andkhoy AF
## 5 5 65295 1148106 Ārt Khwājah Art Khwajah AF
## 6 6 80501 1148311 Asadābād Asadabad AF
## Country.name.EN Population Modification.date Coordinates LON
## 1 Afghanistan 47823 2020-06-09 36.26468, 68.01551 68.01551
## 2 Afghanistan 9196 2018-02-17 30.43206, 63.29802 63.29802
## 3 Afghanistan 10023 2020-06-09 32.7587, 61.65397 61.65397
## 4 Afghanistan 29208 2020-06-09 36.95293, 65.12376 65.12376
## 5 Afghanistan 18623 2018-02-17 37.08571, 69.47958 69.47958
## 6 Afghanistan 48400 2020-06-09 34.87311, 71.14697 71.14697
## LAT LON.ZONE LAT.ZONE bio01 bio02 bio03 bio04 bio05 bio06 bio07 bio08
## 1 36.26468 67.5 35.0 15.03 12.98 31.98 1010.06 36.6 -4.0 40.6 9.08
## 2 30.43206 62.5 30.0 22.21 16.31 39.58 965.08 42.9 1.7 41.2 12.47
## 3 32.75870 60.0 32.5 18.81 13.53 34.44 966.17 39.1 -0.2 39.3 8.78
## 4 36.95293 65.0 35.0 16.83 14.20 33.81 1028.89 39.6 -2.4 42.0 5.87
## 5 37.08571 67.5 35.0 17.35 14.02 33.08 1037.17 40.3 -2.1 42.4 11.52
## 6 34.87311 70.0 32.5 19.55 13.39 36.69 893.14 38.1 1.6 36.5 13.67
## bio09 bio10 bio11 bio12 bio13 bio14 bio15 bio16 bio17 bio18 bio19 annualPET
## 1 27.62 27.62 2.45 257 59 0 92.85 151 2 2 98 1329.70
## 2 32.83 33.78 10.02 56 15 0 90.71 37 2 2 31 1828.55
## 3 30.62 30.62 6.60 107 27 0 105.45 74 0 0 63 1523.48
## 4 29.72 29.72 4.20 235 57 0 94.56 137 1 1 111 1463.59
## 5 28.50 30.13 4.23 403 93 1 90.88 230 4 5 149 1476.91
## 6 25.62 30.37 8.33 474 104 9 82.43 265 44 46 131 1493.55
## aridityIndexThornthwaite climaticMoistureIndex continentality embergerQ
## 1 85.63 -0.81 28.1 21.87
## 2 96.94 -0.97 26.5 4.60
## 3 92.98 -0.93 27.0 9.30
## 4 86.40 -0.84 28.8 19.17
## 5 87.57 -0.73 29.0 32.52
## 6 76.93 -0.68 23.7 44.31
## growingDegDays0 growingDegDays5 maxTempColdest meanTempColdest
## 1 5511.1 3915.7 5.7 0.8
## 2 8132.7 6307.7 15.2 8.5
## 3 6893.1 5068.1 10.3 5.0
## 4 6165.0 4426.2 7.3 2.4
## 5 6356.9 4606.3 7.3 2.6
## 6 7150.0 5325.0 12.8 7.2
## meanTempWarmest minTempWarmest monthCountByTemp10 PETColdestQuarter
## 1 28.9 21.3 7 33.67
## 2 35.0 27.1 10 65.21
## 3 32.0 24.8 9 47.26
## 4 31.2 22.8 8 35.85
## 5 31.6 22.8 9 35.26
## 6 30.9 24.2 9 48.37
## PETDriestQuarter PETseasonality PETWarmestQuarter PETWettestQuarter
## 1 199.60 6834.21 199.60 76.51
## 2 236.56 7189.45 236.39 85.05
## 3 209.12 6725.91 209.12 64.39
## 4 220.91 7647.82 220.91 49.55
## 5 202.92 7762.62 224.90 81.83
## 6 149.02 6449.21 202.83 93.23
## thermicityIndex MCWD bdod cec clay nitrogen phh2o sand silt soc
## 1 289.83 -1078.88 146.7 177.3 274.4 123.1 78.7 285.4 440.3 73.6
## 2 488.75 -1772.54 145.3 154.6 290.7 42.3 82.4 371.9 337.3 22.6
## 3 394.25 -1416.50 146.2 120.7 259.9 78.9 78.8 512.5 227.4 30.8
## 4 357.25 -1238.91 NA NA NA NA NA NA NA NA
## 5 370.50 -1134.02 NA NA NA NA NA NA NA NA
## 6 394.83 -1036.85 145.3 100.8 167.1 124.1 79.0 445.4 387.9 117.2
## topoWet tri
## 1 10.33 18.75
## 2 13.37 0.62
## 3 9.88 61.25
## 4 14.43 1.62
## 5 11.66 13.38
## 6 8.72 63.88
site.fut <- data.frame(read_excel(future.file,
sheet="Cities data",
skip=6))
nrow(site.fut)
## [1] 52602
GlobUNT was filtered for Kenya and trees used as human food. The search resulted in 461 species.
# globunt.file <- choose.files()
globunt.file <- "C:\\Data\\Kenya_GlobUNT_HF_2023-11-15.txt"
globunt <- fread(globunt.file, sep="|", encoding="UTF-8")
nrow(globunt)
## [1] 461
Similar to the case studies of the TreeGOER manuscript (also see the Supplementary Script), a new data.frame is created that documents the ranges for a selected subset of environmental variables.
globunt.treegoer <- dplyr::left_join(globunt[, c("Species", "Switchboard")],
treegoer,
by=c("Switchboard"="species")) # standardized species names in the databases
length(unique(globunt.treegoer$Switchboard))
## [1] 461
length(unique(globunt.treegoer$Species)) # Each species was standardized to a different taxon
## [1] 461
Any environmental variable from TreeGOER can be included. Here only variables included in the climate filter of GlobUNT will be included.
focal.vars <- c("bio01", "bio12",
"climaticMoistureIndex", "monthCountByTemp10", "growingDegDays5",
"bio05", "bio06", "bio16", "bio17",
"MCWD")
focal.treegoer <- focal.vars[1]
focal.ranges <- globunt.treegoer[globunt.treegoer$var == focal.treegoer, c("Species", "n", "MIN", "Q05", "QRT1", "QRT3", "Q95", "MAX")]
names(focal.ranges)[3:8] <- paste0(focal.treegoer, "_", names(focal.ranges)[3:8])
ranges.lookup <- focal.ranges
for (i in 2:length(focal.vars)) {
focal.treegoer <- focal.vars[i]
focal.ranges <- globunt.treegoer[globunt.treegoer$var == focal.treegoer, c("Species", "n", "MIN", "Q05", "QRT1", "QRT3", "Q95", "MAX")]
names(focal.ranges)[3:8] <- paste0(focal.treegoer, "_", names(focal.ranges)[3:8])
# check - note that data was missing for some explanatory variables especially soil
cat(paste(focal.treegoer, ":", all.equal(focal.ranges$Taxon, ranges.lookup$Taxon), "\n"))
ranges.lookup <- cbind(ranges.lookup, focal.ranges[, c(3:8)])
}
## bio12 : TRUE
## climaticMoistureIndex : TRUE
## monthCountByTemp10 : TRUE
## growingDegDays5 : TRUE
## bio05 : TRUE
## bio06 : TRUE
## bio16 : TRUE
## bio17 : TRUE
## MCWD : TRUE
The new data.frame has separate columns for the minimum, maximum, 5%, 95%, 25% (QRT1) and 75% (QRT3) ranges for each of the 10 focal environmental variables.
names(ranges.lookup)
## [1] "Species" "n"
## [3] "bio01_MIN" "bio01_Q05"
## [5] "bio01_QRT1" "bio01_QRT3"
## [7] "bio01_Q95" "bio01_MAX"
## [9] "bio12_MIN" "bio12_Q05"
## [11] "bio12_QRT1" "bio12_QRT3"
## [13] "bio12_Q95" "bio12_MAX"
## [15] "climaticMoistureIndex_MIN" "climaticMoistureIndex_Q05"
## [17] "climaticMoistureIndex_QRT1" "climaticMoistureIndex_QRT3"
## [19] "climaticMoistureIndex_Q95" "climaticMoistureIndex_MAX"
## [21] "monthCountByTemp10_MIN" "monthCountByTemp10_Q05"
## [23] "monthCountByTemp10_QRT1" "monthCountByTemp10_QRT3"
## [25] "monthCountByTemp10_Q95" "monthCountByTemp10_MAX"
## [27] "growingDegDays5_MIN" "growingDegDays5_Q05"
## [29] "growingDegDays5_QRT1" "growingDegDays5_QRT3"
## [31] "growingDegDays5_Q95" "growingDegDays5_MAX"
## [33] "bio05_MIN" "bio05_Q05"
## [35] "bio05_QRT1" "bio05_QRT3"
## [37] "bio05_Q95" "bio05_MAX"
## [39] "bio06_MIN" "bio06_Q05"
## [41] "bio06_QRT1" "bio06_QRT3"
## [43] "bio06_Q95" "bio06_MAX"
## [45] "bio16_MIN" "bio16_Q05"
## [47] "bio16_QRT1" "bio16_QRT3"
## [49] "bio16_Q95" "bio16_MAX"
## [51] "bio17_MIN" "bio17_Q05"
## [53] "bio17_QRT1" "bio17_QRT3"
## [55] "bio17_Q95" "bio17_MAX"
## [57] "MCWD_MIN" "MCWD_Q05"
## [59] "MCWD_QRT1" "MCWD_QRT3"
## [61] "MCWD_Q95" "MCWD_MAX"
A custom function is used to filter species for the conditions at the planting site. The same function is used internally in the GlobUNT.
# filter function modified from the filter function in the TreeGOER manuscript
filter.function <- function(focal.loc, filter.data, focal.vars, limit.vars=c("Q05", "Q95")) {
filtered.data <- filter.data
for (f in 1:length(focal.vars)) {
focal.var <- focal.vars[f]
LL <- paste0(focal.var, "_", limit.vars[1])
filtered.data <- filtered.data[filtered.data[, LL] <= as.numeric(focal.loc[, focal.var]), ]
UL <- paste0(focal.var, "_", limit.vars[2])
filtered.data <- filtered.data[filtered.data[, UL] >= as.numeric(focal.loc[, focal.var]), ]
}
return(filtered.data) # modify the function to return the list of the suitable species
# return(nrow(filtered.data)) # return number of species as in TreeGOER manuscript
}
For the first filtering exericse, we will only use the mean annual temperature (BIO01). This is the same bioclimatic variable that is used in the BGCI Climate Assessment Tool.
Similar to internal calculations in GlobUNT, the list of species is filtered sequentially for the minimum - maximum range (score = 1), 5% - 95% range (score = 2) and 25% - 75% range (score = 3).
PL.vars <- "bio01"
First we filter for the baseline climate, selecting Nairobi …
PL.data <- site.base[site.base$Name == "Nairobi", ] # PL: Planting Location
PL.data
## SEQ2 SEQ1 Geoname.ID Name ASCII.Name Country.Code Country.name.EN
## 25839 25839 21095 184745 Nairobi Nairobi KE Kenya
## Population Modification.date Coordinates LON LAT
## 25839 2750547 2021-07-29 -1.28333, 36.81667 36.81667 -1.28333
## LON.ZONE LAT.ZONE bio01 bio02 bio03 bio04 bio05 bio06 bio07 bio08 bio09
## 25839 35 -2.5 19.61 11.52 73.88 119.98 28.2 12.6 15.6 20.57 18.12
## bio10 bio11 bio12 bio13 bio14 bio15 bio16 bio17 bio18 bio19 annualPET
## 25839 20.98 17.95 1036 240 21 80.38 527 70 384 85 1549.78
## aridityIndexThornthwaite climaticMoistureIndex continentality embergerQ
## 25839 60.43 -0.33 3.7 226.19
## growingDegDays0 growingDegDays5 maxTempColdest meanTempColdest
## 25839 7156.5 5331.5 22.6 17.6
## meanTempWarmest minTempWarmest monthCountByTemp10 PETColdestQuarter
## 25839 21.3 14.6 12 108.29
## PETDriestQuarter PETseasonality PETWarmestQuarter PETWettestQuarter
## 25839 117.31 1719.36 146.18 131.45
## thermicityIndex MCWD bdod cec clay nitrogen phh2o sand silt
## 25839 495.17 -424.48 127.9 192.8 494.4 180.8 60.3 243.3 262.4
## soc topoWet tri
## 25839 216.7 11.53 10
… and then calculating the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
The following species got the highest score.
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 102
head(country.data[country.data$Climate.score == 3, ])
## Species climate_description Climate.score n.TreeGOER
## 3 Acokanthera oppositifolia seasonally dry tropical 3 117
## 4 Acokanthera schimperi seasonally dry tropical 3 56
## 7 Afrocanthium lactescens seasonally dry tropical 3 52
## 9 Alangium chinense seasonally dry tropical 3 443
## 16 Albizia gummifera seasonally dry tropical 3 331
## 22 Aloe volkensii seasonally dry tropical 3 30
tail(country.data[country.data$Climate.score == 3, ])
## Species climate_description Climate.score n.TreeGOER
## 433 Vepris nobilis seasonally dry tropical 3 148
## 445 Volkameria glabra seasonally dry tropical 3 215
## 447 Warburgia ugandensis wet tropical 3 47
## 450 Ximenia caffra seasonally dry tropical 3 339
## 460 Ziziphus mucronata seasonally dry tropical 3 1129
## 461 Ziziphus spina-christi desert or dry shrubland 3 819
We can cross-check that the planting location is within the 25% - 75% range of the first species:
PL.data[, PL.vars] # Conditions in the selected planting location
## [1] 19.61
check.species <- country.data[country.data$Climate.score == 3, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3
## 1: Acokanthera oppositifolia 15.06 15.45 17.03 19.7
## bio01_Q95 bio01_MAX
## 1: 21.61 22.7
These are the species with score = 2.
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 159
head(country.data[country.data$Climate.score == 2, ])
## Species climate_description Climate.score n.TreeGOER
## 1 Acalypha fruticosa seasonally dry tropical 2 123
## 2 Acalypha ornata seasonally dry tropical 2 240
## 6 Adenium obesum desert or dry shrubland 2 594
## 11 Albizia adianthifolia seasonally dry tropical 2 971
## 12 Albizia amara seasonally dry tropical 2 222
## 13 Albizia anthelmintica seasonally dry tropical 2 218
tail(country.data[country.data$Climate.score == 2, ])
## Species climate_description Climate.score n.TreeGOER
## 448 Woodfordia uniflora seasonally dry tropical 2 24
## 449 Ximenia americana seasonally dry tropical 2 2527
## 454 Xymalos monospora wet tropical 2 321
## 455 Zanha africana seasonally dry tropical 2 47
## 456 Zanthoxylum chalybeum seasonally dry tropical 2 42
## 457 Zanthoxylum gilletii wet tropical 2 235
We can cross-check that the planting location is within the 5% - 95% range, but outside the 25% - 75% range of the first species:
PL.data[, PL.vars] # Conditions in the selected planting location
## [1] 19.61
check.species <- country.data[country.data$Climate.score == 2, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Acalypha fruticosa 14.14 18.18 20.81 24.62 27.04
## bio01_MAX
## 1: 28.9
These are the species with Climate Score = 1.
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 92
head(country.data[country.data$Climate.score == 1, ])
## Species climate_description Climate.score n.TreeGOER
## 5 Adansonia digitata seasonally dry tropical 1 2218
## 8 Afzelia quanzensis seasonally dry tropical 1 252
## 14 Albizia coriaria seasonally dry tropical 1 80
## 20 Alchornea cordifolia wet tropical 1 683
## 23 Annona senegalensis seasonally dry tropical 1 2590
## 35 Balanites rotundifolia desert or dry shrubland 1 32
tail(country.data[country.data$Climate.score == 1, ])
## Species climate_description Climate.score n.TreeGOER
## 427 Vachellia tortilis desert or dry shrubland 1 3009
## 435 Vitex doniana seasonally dry tropical 1 1842
## 436 Vitex ferruginea seasonally dry tropical 1 153
## 443 Voacanga africana seasonally dry tropical 1 531
## 452 Xylopia aethiopica wet tropical 1 645
## 458 Ziziphus abyssinica seasonally dry tropical 1 213
We can cross-check that the planting location is within the 0% - 100% range, but outside the 5% - 95% range of the first species:
PL.data[, PL.vars] # Conditions in the selected planting location
## [1] 19.61
check.species <- country.data[country.data$Climate.score == 1, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Adansonia digitata 13.68 21.64 24.47 27.96 28.85
## bio01_MAX
## 1: 30.53
These are the species with Climate Score = 0.
nrow(country.data[country.data$Climate.score == 0, ])
## [1] 92
head(country.data[country.data$Climate.score == 0, ])
## Species climate_description Climate.score n.TreeGOER
## 10 Alangium salviifolium wet tropical 0 48
## 15 Albizia glaberrima seasonally dry tropical 0 365
## 19 Albizia zygia seasonally dry tropical 0 1198
## 25 Antiaris toxicaria wet tropical 0 1150
## 28 Asteranthe asterias seasonally dry tropical 0 73
## 32 Balanites aegyptiaca desert or dry shrubland 0 3499
tail(country.data[country.data$Climate.score == 0, ])
## Species climate_description Climate.score n.TreeGOER
## 410 Thilachium thomasii desert or dry shrubland 0 16
## 420 Vachellia elatior seasonally dry tropical 0 26
## 426 Vachellia seyal desert or dry shrubland 0 1684
## 434 Vepris samburuensis seasonally dry tropical 0 2
## 446 Warburgia stuhlmannii wet tropical 0 12
## 451 Xylocarpus granatum wet tropical 0 256
We can cross-check that the planting location is outside the 0% - 100% range of the first species:
PL.data[, PL.vars] # Conditions in the selected planting location
## [1] 19.61
check.species <- country.data[country.data$Climate.score == 0, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Alangium salviifolium 22.58 22.85 24.24 26.27 28.06
## bio01_MAX
## 1: 28.42
Almost the same script can be used for filter species suitable for the future climate. The only modification is that we now obtain the bioclimatic conditions from the future climate data set.
We select Nairobi again…
PL.data <- site.fut[site.fut$Name == "Nairobi", ] # PL: Planting Location
PL.data
## SEQ2 SEQ1 Geoname.ID Name ASCII.Name Country.Code Country.name.EN
## 25839 25839 21095 184745 Nairobi Nairobi KE Kenya
## Population Modification.date Coordinates LON LAT bio01
## 25839 2750547 2021-07-29 -1.28333, 36.81667 36.81667 -1.28333 21.2
## bio02 bio03 bio04 bio05 bio06 bio07 bio08 bio09 bio10 bio11 bio12 bio13
## 25839 11.15 74.25 117.1 29.3 14.3 15.1 22.3 19.6 22.5 19.6 1066.5 239
## bio14 bio15 bio16 bio17 bio18 bio19 annualPET aridityIndexThornthwaite
## 25839 20 78.8 518.5 68 393.5 80.5 1585.04 57.4
## climaticMoistureIndex continentality embergerQ growingDegDays0
## 25839 -0.33 3.68 236.41 7731.25
## growingDegDays5 maxTempColdest meanTempColdest meanTempWarmest
## 25839 5906.25 24.2 19.27 22.78
## minTempWarmest monthCountByTemp10 PETColdestQuarter PETDriestQuarter
## 25839 16.4 12 112.81 120.94
## PETseasonality PETWarmestQuarter PETWettestQuarter thermicityIndex MCWD
## 25839 1703.4 148.13 134.08 542.29 -475.2
… and then proceed with the same script to calculate the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
For the future climate, the breakdown is:
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 138
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 186
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 67
nrow(country.data[country.data$Climate.score == 0, ])
## [1] 54
Doing some cross-checking
PL.data[, PL.vars] # Conditions in the selected planting location
## [1] 21.2
check.species <- country.data[country.data$Climate.score == 3, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Acalypha fruticosa 14.14 18.18 20.81 24.62 27.04
## bio01_MAX
## 1: 28.9
check.species <- country.data[country.data$Climate.score == 2, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3
## 1: Acokanthera oppositifolia 15.06 15.45 17.03 19.7
## bio01_Q95 bio01_MAX
## 1: 21.61 22.7
check.species <- country.data[country.data$Climate.score == 1, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Adansonia digitata 13.68 21.64 24.47 27.96 28.85
## bio01_MAX
## 1: 30.53
check.species <- country.data[country.data$Climate.score == 0, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "bio01_MIN", "bio01_Q05", "bio01_QRT1", "bio01_QRT3", "bio01_Q95", "bio01_MAX")]
## Species bio01_MIN bio01_Q05 bio01_QRT1 bio01_QRT3 bio01_Q95
## 1: Alangium salviifolium 22.58 22.85 24.24 26.27 28.06
## bio01_MAX
## 1: 28.42
In the second filtering exercise, we filter the species for three environmental variables, a growing degree days variable (GDD5), the minimum temperature of the coldest month (BIO06) and a moisture index (CMI). Similar environmental variables have been used previously in research on the adaption of tree species to future climates (see Booth 2016).
The same scripts can be used after selecting the variables.
PL.vars <- c("growingDegDays5", "bio06", "climaticMoistureIndex")
PL.vars %in% focal.vars # checking if we had selected these variables earlier
## [1] TRUE TRUE TRUE
First we filter for the baseline climate, selecting Nairobi …
PL.data <- site.base[site.base$Name == "Nairobi", ] # PL: Planting Location
PL.data[, PL.vars]
## growingDegDays5 bio06 climaticMoistureIndex
## 25839 5331.5 12.6 -0.33
… and then calculating the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
PL.data[, PL.vars]
## growingDegDays5 bio06 climaticMoistureIndex
## 25839 5331.5 12.6 -0.33
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 40
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 191
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 113
Doing some cross-checking
PL.data[, PL.vars] # Conditions in the selected planting location
## growingDegDays5 bio06 climaticMoistureIndex
## 25839 5331.5 12.6 -0.33
check.species <- country.data[country.data$Climate.score == 3, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "growingDegDays5_Q05", "growingDegDays5_QRT1", "growingDegDays5_QRT3", "growingDegDays5_Q95",
"bio06_Q05", "bio06_QRT1", "bio06_QRT3", "bio06_Q95",
"climaticMoistureIndex_Q05", "climaticMoistureIndex_QRT1", "climaticMoistureIndex_QRT3", "climaticMoistureIndex_Q95")]
## Species growingDegDays5_Q05 growingDegDays5_QRT1 growingDegDays5_QRT3
## 1: Aloe volkensii 4469.2 4982.85 6626.1
## growingDegDays5_Q95 bio06_Q05 bio06_QRT1 bio06_QRT3 bio06_Q95
## 1: 7615.4 9.2 11.33 16.3 19.6
## climaticMoistureIndex_Q05 climaticMoistureIndex_QRT1
## 1: -0.64 -0.48
## climaticMoistureIndex_QRT3 climaticMoistureIndex_Q95
## 1: -0.2 0.17
check.species <- country.data[country.data$Climate.score == 2, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "growingDegDays5_Q05", "growingDegDays5_QRT1", "growingDegDays5_QRT3", "growingDegDays5_Q95",
"bio06_Q05", "bio06_QRT1", "bio06_QRT3", "bio06_Q95",
"climaticMoistureIndex_Q05", "climaticMoistureIndex_QRT1", "climaticMoistureIndex_QRT3", "climaticMoistureIndex_Q95")]
## Species growingDegDays5_Q05 growingDegDays5_QRT1
## 1: Acalypha fruticosa 4808.96 5763.85
## growingDegDays5_QRT3 growingDegDays5_Q95 bio06_Q05 bio06_QRT1 bio06_QRT3
## 1: 7161.05 8038.47 7.66 11.65 16.2
## bio06_Q95 climaticMoistureIndex_Q05 climaticMoistureIndex_QRT1
## 1: 20.16 -0.86 -0.68
## climaticMoistureIndex_QRT3 climaticMoistureIndex_Q95
## 1: -0.38 -0.13
check.species <- country.data[country.data$Climate.score == 1, "Species"][1]
ranges.lookup[ranges.lookup$Species == check.species,
c("Species", "growingDegDays5_Q05", "growingDegDays5_QRT1", "growingDegDays5_QRT3", "growingDegDays5_Q95",
"bio06_Q05", "bio06_QRT1", "bio06_QRT3", "bio06_Q95",
"climaticMoistureIndex_Q05", "climaticMoistureIndex_QRT1", "climaticMoistureIndex_QRT3", "climaticMoistureIndex_Q95")]
## Species growingDegDays5_Q05 growingDegDays5_QRT1
## 1: Acokanthera oppositifolia 3809.01 4384.3
## growingDegDays5_QRT3 growingDegDays5_Q95 bio06_Q05 bio06_QRT1 bio06_QRT3
## 1: 5362.1 6060.28 1.67 3.4 9.5
## bio06_Q95 climaticMoistureIndex_Q05 climaticMoistureIndex_QRT1
## 1: 11.51 -0.66 -0.55
## climaticMoistureIndex_QRT3 climaticMoistureIndex_Q95
## 1: -0.37 -0.13
Again modifying the script is easy, again also selecting Nairobi:
PL.data <- site.fut[site.fut$Name == "Nairobi", ] # PL: Planting Location
PL.data[, PL.vars]
## growingDegDays5 bio06 climaticMoistureIndex
## 25839 5906.25 14.3 -0.33
… and then calculating the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
PL.data[, PL.vars]
## growingDegDays5 bio06 climaticMoistureIndex
## 25839 5906.25 14.3 -0.33
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 44
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 229
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 100
As you will have expected by now, it is easy to modify the script to include more variables.
PL.vars <- focal.vars
First we filter for the baseline climate, selecting Nairobi …
PL.data <- site.base[site.base$Name == "Nairobi", ] # PL: Planting Location
PL.data[, PL.vars]
## bio01 bio12 climaticMoistureIndex monthCountByTemp10 growingDegDays5
## 25839 19.61 1036 -0.33 12 5331.5
## bio05 bio06 bio16 bio17 MCWD
## 25839 28.2 12.6 527 70 -424.48
… and then calculating the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
PL.data[, PL.vars]
## bio01 bio12 climaticMoistureIndex monthCountByTemp10 growingDegDays5
## 25839 19.61 1036 -0.33 12 5331.5
## bio05 bio06 bio16 bio17 MCWD
## 25839 28.2 12.6 527 70 -424.48
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 17
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 158
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 148
Once again modifying the script is easy, selecting Nairobi:
PL.data <- site.fut[site.fut$Name == "Nairobi", ] # PL: Planting Location
PL.data[, PL.vars]
## bio01 bio12 climaticMoistureIndex monthCountByTemp10 growingDegDays5
## 25839 21.2 1066.5 -0.33 12 5906.25
## bio05 bio06 bio16 bio17 MCWD
## 25839 29.3 14.3 518.5 68 -475.2
… and then calculating the climate score
country.data <- globunt[, c("Species", "climate_description")] # include the main biome documented in WCVP/POWO
country.data <- data.frame(country.data)
filteri <- data.frame(ranges.lookup)
country.data$Climate.score <- rep(-1, nrow(country.data))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 0
country.data <- left_join(country.data,
filteri[ , c("Species", "n")],
by="Species")
names(country.data)[which(names(country.data)=="n")] <- "n.TreeGOER"
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("MIN", "MAX"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 1
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("Q05", "Q95"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 2
filteri <- filter.function(focal.loc=PL.data,
filter.data=filteri,
focal.vars=PL.vars,
limit.vars=c("QRT1", "QRT3"))
country.data[country.data$Species %in% filteri$Species, "Climate.score"] <- 3
PL.data[, PL.vars]
## bio01 bio12 climaticMoistureIndex monthCountByTemp10 growingDegDays5
## 25839 21.2 1066.5 -0.33 12 5906.25
## bio05 bio06 bio16 bio17 MCWD
## 25839 29.3 14.3 518.5 68 -475.2
country.data[country.data$Climate.score == 3, ]
## Species climate_description Climate.score n.TreeGOER
## 22 Aloe volkensii seasonally dry tropical 3 30
## 51 Bridelia micrantha wet tropical 3 705
## 74 Clausena anisata seasonally dry tropical 3 848
## 97 Cordia africana seasonally dry tropical 3 181
## 129 Diospyros natalensis seasonally dry tropical 3 86
## 148 Ekebergia capensis seasonally dry tropical 3 699
## 157 Erythroxylum emarginatum wet tropical 3 267
## 165 Euphorbia umbellata seasonally dry tropical 3 60
## 191 Garcinia buchananii seasonally dry tropical 3 50
## 297 Phoenix reclinata seasonally dry tropical 3 774
## 322 Rauvolfia caffra seasonally dry tropical 3 342
## 382 Strychnos henningsii seasonally dry tropical 3 138
## 412 Trichilia dregeana wet tropical 3 128
## 431 Vangueria madagascariensis seasonally dry tropical 3 220
## 433 Vepris nobilis seasonally dry tropical 3 148
## 437 Vitex fischeri seasonally dry tropical 3 26
nrow(country.data[country.data$Climate.score == 3, ])
## [1] 16
nrow(country.data[country.data$Climate.score == 2, ])
## [1] 195
nrow(country.data[country.data$Climate.score == 1, ])
## [1] 151
This publication was initiated partially from ongoing work in a Darwin Initiative project (DAREX001) that develops a Global Biodiversity Standard for tree planting. The GlobalUsefulNativeTrees database and Tree Globally Observed Environmental Ranges database were realised through co-funding from this project. With scripts such as the ones shown here, when the Global Biodiversity Standard scheme becomes operational, tree planting projects can seek guidance on suitable species for planting.
sessionInfo()
## R version 4.2.1 (2022-06-23 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United Kingdom.utf8
## [2] LC_CTYPE=English_United Kingdom.utf8
## [3] LC_MONETARY=English_United Kingdom.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United Kingdom.utf8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] dplyr_1.1.2 readxl_1.4.1 data.table_1.14.2
##
## loaded via a namespace (and not attached):
## [1] rstudioapi_0.14 knitr_1.40 magrittr_2.0.3 tidyselect_1.2.0
## [5] R6_2.5.1 rlang_1.1.1 fastmap_1.1.1 fansi_1.0.3
## [9] stringr_1.4.1 tools_4.2.1 xfun_0.33 utf8_1.2.2
## [13] cli_3.4.1 jquerylib_0.1.4 htmltools_0.5.6 yaml_2.3.5
## [17] digest_0.6.29 tibble_3.2.1 lifecycle_1.0.3 sass_0.4.2
## [21] vctrs_0.6.3 glue_1.6.2 cachem_1.0.6 evaluate_0.16
## [25] rmarkdown_2.16 stringi_1.7.8 pillar_1.9.0 compiler_4.2.1
## [29] bslib_0.4.0 cellranger_1.1.0 generics_0.1.3 jsonlite_1.8.0
## [33] pkgconfig_2.0.3