Estimating carbon in priority habitats

Wed, Oct 28, 2020 8-minute read

Introduction

A few weeks a go a wrote a short article for the Thames Valley Environmental Records Centre (TVERC) on the amount of carbon stored and sequestered by priority habitats in Berkshire and Oxfordshire. The climate emergency has prompted many people, businesses, organisations and governments to think about to address increasing carbon emissions. The lead up to the UK election in December 2019 saw parties of all colours promising to plant trees to absorb some of the UK’s carbon emissions. The Friends of the Earth UK want to double UK tree cover to help in the fight against climate change; others have similar plans for campaigns. Trees are an important part in addressing climate change and carbon emissions. But, we need to ensure we get the right trees, in the right place, for the right reasons. Once again, TVERC are involved in work to work out where trees could go.

We are also in the midst of an ecological crisis. Biodiversity has declined at an unprecedented rate and the time to act is now. The actions we need to take are clear and strategy is emerging to halt and reverse biodiversity loss. However one of the worst things we could do is to not link natural climate solutions (i.e. tree planting) with actions to halt and reverse biodiversity decline. And this means not planting tees on habitats that have a high value for biodiversity.

UK priority habitats are the most important for nature conservation as they support important communities that are rare in the wider countryside. The aim of my article was to show that these habitats also have a role to play in carbon storage and sequestration.

In this post I will demonstrate how you can calculate the carbon storage and sequestration of priority habitats using a small R package I created called carbonhabitats.

Get some data

For the TVERC post I used TVERC data - but for this post I will use the Natural England Priority Habitat Inventory (NE PHI). These data are getting quite out of date now and in some areas they miss a lot of priority habitats. However, if you are looking at regional or national patterns then they are adequate. I downloaded the southern England data from the Natural England website and then selected only those features that intersected with South Somerset District boundary. (I did this in QGIS because I had some trouble downloading the admin boundaries and I want to take a look at things.)

I created a small R package to calculate the carbon storage and sequestration for habitats based on the data in a paper by Field et al. (2020) which you can find on GitHub. In preparing this post I realised one of the key issues with data - people called the same things by different names! I updated it so that it worked with the NE PHI data. One of my current soapbox rants is the need for standards in natural environment data.

Anyway, we now have some data so lets look at calculating carbon storage and sequestration.

Calcuating carbon storage and sequestration

The first thing to do is install the package from Github. I like using the remotes package for this. We also need the sf package for reading in the habitats data. I also like to use the glimpse function from dplyr to look at my data:

# remotes::install_github("drdcarpenter/carbonhabitats")
library(carbonhabitats)
library(sf)
library(dplyr)
library(janitor)

Next we need read in our habitats data. I have mine saved as an ESRI Shapefile:

habs <- st_read("habitats.shp")
## Reading layer `habitats' from data source `/home/danc/Documents/dan-carpenter/content/post/habitats.shp' using driver `ESRI Shapefile'
## Simple feature collection with 10413 features and 27 fields
## geometry type:  POLYGON
## dimension:      XY
## bbox:           xmin: 323194.4 ymin: 104132 xmax: 377398.3 ymax: 138951.4
## projected CRS:  OSGB 1936 / British National Grid

So now we have our habitats data read in. For the carbonhabitats package, we need to supply the name of the column in which the priority habitat typess can be found. So lets have a quick squint at the data:

glimpse(habs)
## Rows: 10,413
## Columns: 28
## $ Main_Habit <fct> Coastal and floodplain grazing marsh, Coastal and floodpla…
## $ Confidence <fct> Low, Low, Low, Low, Low, Low, Low, Low, Low, Low, Low, Low…
## $ Source1    <fct> Higher Level Stewardship, Higher Level Stewardship, Higher…
## $ S1Date     <date> 2006-08-01, 2006-08-01, 2006-08-01, 2008-02-01, 2008-02-0…
## $ S1Habclass <fct> FEP/HLS features and options, FEP/HLS features and options…
## $ S1HabType  <fct> G15: Coastal & Floodplain grazing marsh; HK6, G15: Coastal…
## $ Source2    <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ S2Date     <date> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ S2Habclass <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ S2HabType  <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ Source3    <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ S3Date     <date> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
## $ S3Habclass <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ S3HabType  <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ Basemappng <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ Annex_1    <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ Add_habits <fct> NA, NA, HLS options: HK7, NA, NA, NA, NA, NA, NA, NA, NA, …
## $ Candidates <fct> Main habitat: CFPGM (FEP + HLS); LMEAD (FEP + HLS), Main h…
## $ Rule_Decis <fct> Tests failed: 1.MasterMap description (LMEAD), NA, Tests f…
## $ GenComment <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ LastModDat <date> 2018-08-25, 2018-08-25, 2018-08-25, 2018-08-25, 2018-08-2…
## $ ModReason  <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ Mod_by     <fct> v2.2, v2.2, v2.2, v2.2, v2.2, v2.2, v2.2, v2.2, v2.2, v2.2…
## $ Area_Ha    <dbl> 0.007144000, 2.685990255, 4.505765270, 0.006757025, 0.0320…
## $ URN        <fct> ST7342523450, ST7353223464, ST7311723235, ST3716125868, ST…
## $ Shape_Leng <dbl> 36.45851, 1340.06634, 926.33087, 54.10821, 252.52223, 25.3…
## $ Shape_Area <dbl> 71.44000, 26859.90255, 45057.65270, 67.57025, 320.31930, 3…
## $ geometry   <POLYGON [m]> POLYGON ((373419.5 123447, ..., POLYGON ((373719 1…

The column “Main_Habit” has the habitat types in it. So now we can use the carbonhabitats package to calculate the carbon storage and sequestration values for these habitats. The function carbonise in the package does this:

habs <- carbonise(habs, habitats = "Main_Habit")

The carbonise function adds columns to your dataframe that include the above ground (ABG) stored, soil and total stored carbon, plus the amount of carbon sequestered each year. Using some standard dplyr code we can produce a summary table of the amount of asbove ground carbon stored by each habitat:

storedc <- habs %>% 
  as.data.frame() %>% 
  select(Main_Habit, storedC) %>% 
  group_by(Main_Habit) %>% 
  summarise(total_stored_C = round(sum(storedC), 0)) %>% 
  adorn_totals()

knitr::kable(storedc)
Main_Habit total_stored_C
Coastal and floodplain grazing marsh NA
Deciduous woodland 215071
Good quality semi-improved grassland 1198
Lowland calcareous grassland 651
Lowland dry acid grassland 49
Lowland fens 50
Lowland heathland 2
Lowland meadows 3126
No main habitat but additional habitats present NA
Purple moor grass and rush pastures 210
Reedbeds 3
Traditional orchard 6284
Total 226644

And similarly the amount of carbon sequestered by each habitat annually:

sequesteredc <- habs %>% 
  as.data.frame() %>% 
  select(Main_Habit, seqC) %>% 
  group_by(Main_Habit) %>% 
  summarise(total_seq_c = round(sum(seqC), 0)) %>% 
  adorn_totals()

knitr::kable(sequesteredc)
Main_Habit total_seq_c
Coastal and floodplain grazing marsh NA
Deciduous woodland 37212
Good quality semi-improved grassland 928
Lowland calcareous grassland 504
Lowland dry acid grassland 38
Lowland fens 15
Lowland heathland 3
Lowland meadows 2423
No main habitat but additional habitats present NA
Purple moor grass and rush pastures 163
Reedbeds 1
Traditional orchard 6731
Total 48018

Incidentally, adorn_totals() is a useful function from the janitor package.

So there we have it. A method for calculating the stored carbon and sequestered carbon for priority habitats in the UK.

Next steps

There are a few things to do. I need to make sure the code is robust to other versions of the names for priority habitats. Or persuade people to adopt a standard! The other thing that was pointed out by an early user was that I have added soil carbon to ponds, and I need to re-read the papers to work out how to deal with this. In the first instance I can remove soil carbon from ponds - that seems sensible. But I need to go back to the Taylor et al. (2019) paper to see exactly what is included in their estimates for carbon storage and burial. I also need to work on adding other priority habitats that are currently missing from the package.

sessionInfo()
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
## LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0
## 
## locale:
##  [1] LC_CTYPE=en_GB.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_GB.UTF-8        LC_COLLATE=en_GB.UTF-8    
##  [5] LC_MONETARY=en_GB.UTF-8    LC_MESSAGES=en_GB.UTF-8   
##  [7] LC_PAPER=en_GB.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] janitor_2.0.1        dplyr_1.0.2          sf_0.9-5            
## [4] carbonhabitats_1.0.0
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.5          highr_0.8           pillar_1.4.6       
##  [4] compiler_3.6.3      class_7.3-15        tools_3.6.3        
##  [7] digest_0.6.25       lubridate_1.7.9     evaluate_0.14      
## [10] lifecycle_0.2.0     tibble_3.0.3        lattice_0.20-40    
## [13] pkgconfig_2.0.3     rlang_0.4.7         cli_2.0.2          
## [16] DBI_1.1.0           yaml_2.2.1          blogdown_0.20      
## [19] xfun_0.15           e1071_1.7-3         exactextractr_0.4.0
## [22] stringr_1.4.0       knitr_1.28          raster_3.3-13      
## [25] generics_0.0.2      vctrs_0.3.4         classInt_0.4-3     
## [28] grid_3.6.3          tidyselect_1.1.0    snakecase_0.11.0   
## [31] glue_1.4.2          R6_2.4.1            fansi_0.4.1        
## [34] rmarkdown_2.3       bookdown_0.20       sp_1.4-2           
## [37] purrr_0.3.4         magrittr_1.5        codetools_0.2-16   
## [40] ellipsis_0.3.1      htmltools_0.5.0     units_0.6-7        
## [43] assertthat_0.2.1    utf8_1.1.4          KernSmooth_2.23-16 
## [46] stringi_1.4.6       crayon_1.3.4