<- sf::st_read(
sites_possible "data/sites/GRTS_PossibleCaARU_sample_draw_base.shp",
quiet = TRUE) |>
::mutate(source = "GRTS_PossibleCaARU_sample_draw_base") |>
dplyr::mutate(fullID = paste(SampleID, ID, source, sep = "_"))
dplyr
<- readr::read_csv("data/sites/Selected_Peat_Sites.csv") |>
additional_sites ::st_as_sf(coords=c("lon_WGS84", "lat_WGS84"), crs = 4326) |>
sf::st_transform(sf::st_crs(sites_possible)) |>
sf::mutate(source = "Selected_Peat_Sites") |>
dplyr::mutate(fullID = paste(SampleID, ID, source, sep = "_"))
dplyr
<- sites_possible |>
all_sites ::bind_rows(additional_sites)
dplyr
<- sf::st_read(
ecodistrict "data/ecodistrict_shp/Ecodistricts/ecodistricts.shp",
quiet = TRUE) |>
::filter(ECODISTRIC == 1028)
dplyr<- raster::raster("data/land_use/FarNorth_LandCover_Class_UTM16.tif")
lu_16 <- raster::raster("data/land_use/FarNorth_LandCover_Class_UTM17.tif")
lu_17 <- readr::read_csv("data/land_use/attr_table_northen_ont_lc.txt") |>
lu_dat ::mutate(cats = as.factor(code)) dplyr
Land Cover Analysis
Loading data
We load the possible sites (quiet = TRUE
is for not displaying verbose loading information). We also load the two halves of the far north land cover dataset, along with the the attribute table of land cover classes. We load the ecodistrict data and select for the relevant lowlands disctrict, coded as 1028.
Plotting spatial data
It is always a good idea to try and plot spatial data before any processing.
ggplot() +
geom_sf(data = ecodistrict) +
geom_sf(data = sf::st_transform(all_sites,
::st_crs(ecodistrict))) +
sftheme_bw()
Plotting the land cover data is difficult because it is provided is two different UTMs.
Extracting Land Cover data
The following functions will take care of land cover extraction for sites.
<- function(scale_m, sites, lu) {
extract_from_points
<- sites |>
sites_buffer ::st_transform(sf::st_crs(lu)) |>
sf::st_buffer(dist = scale_m) |>
sf::select(fullID)
dplyr
<- exactextractr::exact_extract(lu, sites_buffer,
extr progress = FALSE,
include_cols = "fullID")
<- mapply(extr, 1:length(extr),
extr FUN = \(x, y) dplyr::mutate(x, id = y),
SIMPLIFY = F)
<- do.call(rbind, extr) |>
extr_df ::filter(!is.na(value)) |>
dplyr::relocate(id)
dplyr
return(extr_df)
}
<- function(scale_m, sites,
compute_land_cover
lu_16, lu_17, lu_dat,summarise_all = TRUE) {
<- extract_from_points(scale_m, sites, lu_16)
extr_16_df <- extract_from_points(scale_m, sites, lu_17)
extr_17_df
stopifnot(all(!(extr_16_df$siteID %in% extr_17_df$siteID)))
<- rbind(extr_16_df, extr_17_df) |>
extr ::arrange(id, value)
dplyr
if (summarise_all) {
<- extr |>
extr_table ::group_by(value) |>
dplyr::summarise(coverage_fraction_sum = sum(coverage_fraction)) |>
dplyr::mutate(prop =
dplyr/sum(coverage_fraction_sum)) |>
coverage_fraction_sum::ungroup() |>
dplyr::mutate(value = as.factor(value)) |>
dplyr::left_join(lu_dat, by = c("value" = "cats")) |>
dplyr::select(category_code, prop, label)
dplyr
else {
}
<- extr |>
extr_table ::group_by(fullID, value) |>
dplyr::summarise(coverage_fraction_sum = sum(coverage_fraction)) |>
dplyr::mutate(prop =
dplyr/sum(coverage_fraction_sum)) |>
coverage_fraction_sum::ungroup() |>
dplyr::mutate(value = as.factor(value)) |>
dplyr::left_join(lu_dat, by = c("value" = "cats")) |>
dplyr::select(fullID, category_code, prop, label)
dplyr
}
is.na(extr_table)] <- 0
extr_table[
return(extr_table)
}
We extract at different scales (buffer radius around points): 1 m, 50 m, 100 m and 1 km.
<- mapply(FUN = compute_land_cover,
res_points c(`1 m` = 1, `50 m` = 50,
`100 m` = 100, `1 km` = 1000),
MoreArgs = list(
sites = sites_possible,
lu_16 = lu_16, lu_17 = lu_17, lu_dat = lu_dat),
SIMPLIFY = F) |>
::bind_rows(.id = 'scale') |>
dplyr::mutate(scale = forcats::fct_relevel(scale, "1 m", "50 m",
dplyr"100 m", "1 km"),
label = forcats::fct_reorder(label, prop)) |>
::arrange(scale, dplyr::desc(prop))
dplyr
::kable(res_points) knitr
scale | category_code | prop | label |
---|---|---|---|
1 m | TrBOG | 0.26 | Treed Bog |
1 m | TrFEN | 0.23 | Treed Fen |
1 m | OBOG | 0.20 | Open Bog |
1 m | ConSWA | 0.11 | Coniferous Swamp |
1 m | ConTRE | 0.10 | Coniferous Treed |
1 m | OFEN | 0.06 | Open Fen |
1 m | SpTRE | 0.02 | Sparse Treed |
1 m | MixTRE | 0.02 | Mixed Treed |
50 m | TrBOG | 0.23 | Treed Bog |
50 m | TrFEN | 0.22 | Treed Fen |
50 m | OBOG | 0.21 | Open Bog |
50 m | ConSWA | 0.12 | Coniferous Swamp |
50 m | ConTRE | 0.09 | Coniferous Treed |
50 m | OFEN | 0.07 | Open Fen |
50 m | SpTRE | 0.03 | Sparse Treed |
50 m | MixTRE | 0.02 | Mixed Treed |
50 m | WAT | 0.01 | Clear Open Water |
50 m | XWAT | 0.00 | Turbid Water |
100 m | TrBOG | 0.23 | Treed Bog |
100 m | TrFEN | 0.22 | Treed Fen |
100 m | OBOG | 0.20 | Open Bog |
100 m | ConSWA | 0.12 | Coniferous Swamp |
100 m | ConTRE | 0.10 | Coniferous Treed |
100 m | OFEN | 0.08 | Open Fen |
100 m | WAT | 0.03 | Clear Open Water |
100 m | SpTRE | 0.02 | Sparse Treed |
100 m | MixTRE | 0.01 | Mixed Treed |
100 m | XWAT | 0.01 | Turbid Water |
1 km | TrFEN | 0.21 | Treed Fen |
1 km | TrBOG | 0.21 | Treed Bog |
1 km | OBOG | 0.19 | Open Bog |
1 km | ConSWA | 0.17 | Coniferous Swamp |
1 km | OFEN | 0.07 | Open Fen |
1 km | ConTRE | 0.06 | Coniferous Treed |
1 km | WAT | 0.04 | Clear Open Water |
1 km | MixTRE | 0.01 | Mixed Treed |
1 km | XWAT | 0.01 | Turbid Water |
1 km | ThSWA | 0.01 | Thicket Swamp |
1 km | SpTRE | 0.00 | Sparse Treed |
1 km | NSWood | 0.00 | Disturbance - Non and Sparse Woody |
1 km | DecTRE | 0.00 | Deciduous Treed |
1 km | TrOrSHr | 0.00 | Disturbance - Treed and/or Shrub |
1 km | BED | 0.00 | Bedrock |
We also want to do the same operation for the ecodistrict to allow for comparison. We don’t need to use exact extraction, insteadt the crop and mask each raster. This operation is costly so we write out the rasters and load them again (see unrendered code).
<- sf::st_transform(ecodistrict, sf::st_crs(lu_16))
ecodistrict_16 <- sf::st_transform(ecodistrict, sf::st_crs(lu_17))
ecodistrict_17
<- raster::crop(lu_16, ecodistrict_16)
lu_16_crop <- raster::mask(lu_16_crop, ecodistrict_16)
lu_16_crop_mask
<- raster::crop(lu_17, ecodistrict_17)
lu_17_crop <- raster::mask(lu_17_crop, ecodistrict_17) lu_17_crop_mask
We can then get the frequencies of values. This operation is also costly so we write out the objects and load them again (see unrendered code).
<- raster::freq(lu_16_crop_mask)
lu_16_freq <- raster::freq(lu_17_crop_mask) lu_17_freq
We combine the results of both UTMs.
<- rbind(lu_16_freq, lu_17_freq) |>
res_ecodistrict as.data.frame() |>
::group_by(value) |>
dplyr::summarise(count = sum(count)) |>
dplyr::ungroup() |>
dplyr::filter(!is.na(value)) |>
dplyr::mutate(prop = count/sum(count)) |>
dplyr::mutate(value = as.factor(value)) |>
dplyr::left_join(lu_dat, by = c("value" = "cats")) |>
dplyr::filter(!is.na(label)) |>
dplyr::select(category_code, prop, label) |>
dplyr::mutate(scale = "Ecodistrict") |>
dplyr::relocate(scale) |>
dplyr::arrange(scale, dplyr::desc(prop))
dplyr
::kable(res_ecodistrict) knitr
scale | category_code | prop | label |
---|---|---|---|
Ecodistrict | TrFEN | 0.25 | Treed Fen |
Ecodistrict | OBOG | 0.20 | Open Bog |
Ecodistrict | TrBOG | 0.19 | Treed Bog |
Ecodistrict | ConSWA | 0.12 | Coniferous Swamp |
Ecodistrict | OFEN | 0.09 | Open Fen |
Ecodistrict | WAT | 0.07 | Clear Open Water |
Ecodistrict | ConTRE | 0.04 | Coniferous Treed |
Ecodistrict | NSWood | 0.01 | Disturbance - Non and Sparse Woody |
Ecodistrict | TrOrSHr | 0.01 | Disturbance - Treed and/or Shrub |
Ecodistrict | MixTRE | 0.01 | Mixed Treed |
Ecodistrict | ThSWA | 0.01 | Thicket Swamp |
Ecodistrict | SpTRE | 0.00 | Sparse Treed |
Ecodistrict | XWAT | 0.00 | Turbid Water |
Ecodistrict | DecTRE | 0.00 | Deciduous Treed |
Ecodistrict | MIN | 0.00 | Sand/Gravel/Mine Tailings |
Ecodistrict | FrMAR | 0.00 | Freshwater Marsh |
Ecodistrict | BED | 0.00 | Bedrock |
Ecodistrict | URB | 0.00 | Community/Infrastructure |
Ecodistrict | DecSWA | 0.00 | Deciduous Swamp |
Ecodistrict | InMAR | 0.00 | Intertidal Marsh |
And then combine the results between scales and utm.
<- rbind(res_points, res_ecodistrict) |>
res ::complete(scale, label) |>
tidyr::replace_na(list(prop = 0)) |>
tidyr::mutate(label = forcats::fct_reorder(label, prop)) dplyr
For individual site identity, at different scales:
<-
res_points_by_site mapply(FUN = compute_land_cover,
c(`1 m` = 1, `50 m` = 50,
`100 m` = 100, `1 km` = 1000),
MoreArgs = list(
sites = sites_possible,
lu_16 = lu_16, lu_17 = lu_17, lu_dat = lu_dat),
summarise_all = FALSE,
SIMPLIFY = F) |>
::bind_rows(.id = 'scale') |>
dplyr::mutate(scale = forcats::fct_relevel(scale, "1 m", "50 m",
dplyr"100 m", "1 km"),
label = forcats::fct_reorder(label, prop)) |>
::group_by(scale, fullID) |>
dplyr::arrange(dplyr::desc(prop)) |>
dplyr::rename(primary_category_code = category_code,
dplyrprimary_prop = prop,
primary_label = label) |>
::mutate(secondary_category_code = primary_category_code[2],
dplyrsecondary_prop = primary_prop[2],
secondary_label = primary_label[2],
prop_sum = primary_prop + secondary_prop) |>
::slice(1) |>
dplyr::ungroup() |>
dplyr::arrange(fullID) dplyr
`summarise()` has grouped output by 'fullID'. You can override using the
`.groups` argument.
`summarise()` has grouped output by 'fullID'. You can override using the
`.groups` argument.
`summarise()` has grouped output by 'fullID'. You can override using the
`.groups` argument.
`summarise()` has grouped output by 'fullID'. You can override using the
`.groups` argument.
::datatable(res_points_by_site) DT
We save this table.
::write_csv(res_points_by_site, "outputs/res_points_by_site.csv") readr
Results
We can plot the results with “dodged” ggplot2 barplots.
<- c('#c7e9b4','#7fcdbb','#41b6c4','#1d91c0','#225ea8','#0c2c84')
my_pal
ggplot(res) +
geom_bar(aes(x = label, y = prop, fill = scale, colour = scale),
alpha = 0.8,
stat = "identity",
position = "dodge") +
theme_bw() +
theme(axis.text.x = element_text(angle = 20, vjust = 1, hjust = 1)) +
labs(x = "Land Use Class", y = "Proportion",
fill = "Scale", colour = "Scale") +
scale_fill_manual(values = my_pal) +
scale_color_manual(values = my_pal)
Removing the land use classes than are not present around sites, we get a slightly easier graph to read.
<- res |>
only_at_sites ::filter(prop > 0) |>
dplyr::filter(scale != "Ecodistrict") |>
dplyr::pull(label)
dplyr
<- res |>
res_filt ::filter(label %in% only_at_sites)
dplyr
ggplot(res_filt) +
geom_bar(aes(x = label, y = prop, fill = scale, colour = scale),
alpha = 0.8,
stat = "identity",
position = "dodge") +
theme_bw() +
theme(axis.text.x = element_text(angle = 20, vjust = 1, hjust = 1)) +
labs(x = "Land Use Class", y = "Proportion",
fill = "Scale", colour = "Scale") +
scale_fill_manual(values = my_pal) +
scale_color_manual(values = my_pal)