From 933a02aae9255a500f1769a679a93169623d534c Mon Sep 17 00:00:00 2001 From: almac2022 Date: Mon, 1 Jul 2024 16:05:15 -0700 Subject: [PATCH] add local study area and map detail. --- .../index/execute-results/html.json | 4 +- .../index/figure-html/plot-1.png | Bin 228402 -> 227933 bytes .../index/figure-html/plot2-1.png | Bin 0 -> 98425 bytes posts/2024-06-30-land-cover/image.jpg | Bin 2613690 -> 594668 bytes posts/2024-06-30-land-cover/index.qmd | 184 ++++++++++++++---- .../index_cache/html/__packages | 1 + ...ers_12a2939ce932b8b1053d81f81cfeead3.RData | Bin 3148 -> 0 bytes ...ayers_12a2939ce932b8b1053d81f81cfeead3.rdx | Bin 166 -> 0 bytes ...ers_5c9c7b85fa6d4227c40a0e70e9e66d54.RData | Bin 0 -> 3184 bytes ...yers_5c9c7b85fa6d4227c40a0e70e9e66d54.rdb} | Bin 3436717 -> 3470390 bytes ...ayers_5c9c7b85fa6d4227c40a0e70e9e66d54.rdx | Bin 0 -> 164 bytes .../index_files/figure-html/plot-1.png | Bin 228402 -> 227933 bytes .../index_files/figure-html/plot2-1.png | Bin 0 -> 98425 bytes scripts/functions.R | 6 +- 14 files changed, 155 insertions(+), 40 deletions(-) create mode 100644 _freeze/posts/2024-06-30-land-cover/index/figure-html/plot2-1.png delete mode 100644 posts/2024-06-30-land-cover/index_cache/html/dl-layers_12a2939ce932b8b1053d81f81cfeead3.RData delete mode 100644 posts/2024-06-30-land-cover/index_cache/html/dl-layers_12a2939ce932b8b1053d81f81cfeead3.rdx create mode 100644 posts/2024-06-30-land-cover/index_cache/html/dl-layers_5c9c7b85fa6d4227c40a0e70e9e66d54.RData rename posts/2024-06-30-land-cover/index_cache/html/{dl-layers_12a2939ce932b8b1053d81f81cfeead3.rdb => dl-layers_5c9c7b85fa6d4227c40a0e70e9e66d54.rdb} (82%) create mode 100644 posts/2024-06-30-land-cover/index_cache/html/dl-layers_5c9c7b85fa6d4227c40a0e70e9e66d54.rdx create mode 100644 posts/2024-06-30-land-cover/index_files/figure-html/plot2-1.png diff --git a/_freeze/posts/2024-06-30-land-cover/index/execute-results/html.json b/_freeze/posts/2024-06-30-land-cover/index/execute-results/html.json index ec35716..67e2ad7 100644 --- a/_freeze/posts/2024-06-30-land-cover/index/execute-results/html.json +++ b/_freeze/posts/2024-06-30-land-cover/index/execute-results/html.json @@ -1,8 +1,8 @@ { - "hash": "30320b3257f9d83b2053cf69ced67973", + "hash": "d3685cb37b776df00c767e09e6854ca0", "result": { "engine": "knitr", - "markdown": "---\ntitle: \"Mapping Land Cover with R\"\nauthor: \"al\"\ndate: \"2024-07-01\"\ndate-modified: \"2024-07-01\"\ncategories: [land cover, R, planetary computer, satellite]\nimage: \"image.jpg\"\nparams:\n repo_owner: \"NewGraphEnvironment\"\n repo_name: \"new_graphiti\"\n post_dir_name: \"2024-06-30-land-cover\"\n update_pkgs: FALSE\n update_gis: FALSE\nexecute:\n warning: false\nformat: \n html:\n code-fold: true\n---\n\n\nWe want to quantifying and visualize remotely sense land cover data.... Here is a first start. We will use the European\nSpace Agency's WorldCover product which provides global land cover maps for the years 2020 and 2021 at 10 meter\nresolution based on the combination of Sentinel-1 radar data and Sentinel-2 imagery. We will use the 2021 dataset\nfor mapping an area of the Skeena watershed near Houston, British Columbia. \n\n
\n\n\nThis post was inspired - with much of the code copied - from a repository on GitHub from the wonderfully talented\n[Milos Popovic](https://github.com/milos-agathon/esa-land-cover). \n\n
\n\nFirst thing we will do is load our packages. If you do not have the packages installed yet you can change the `update_pkgs` param in\nthe `yml` of this file to `TRUE`. Using `pak` is great because it allows you to update your packages when you want to.\n\n\n::: {.cell}\n\n```{.r .cell-code}\npkgs_cran <- c(\n \"usethis\",\n \"rstac\",\n \"here\",\n \"fs\",\n \"terra\",\n \"tidyverse\",\n \"rayshader\",\n \"sf\",\n \"classInt\",\n \"rgl\",\n \"tidyterra\",\n \"tabulapdf\",\n \"bcdata\",\n \"ggplot\",\n \"ggdark\")\n\npkgs_gh <- c(\n \"poissonconsulting/fwapgr\",\n \"NewGraphEnvironment/rfp\"\n )\n\npkgs <- c(pkgs_cran, pkgs_gh)\n\nif(params$update_pkgs){\n # install the pkgs\n lapply(pkgs,\n pak::pkg_install,\n ask = FALSE)\n}\n\n# load the pkgs\npkgs_ld <- c(pkgs_cran,\n basename(pkgs_gh))\ninvisible(\n lapply(pkgs_ld,\n require,\n character.only = TRUE)\n)\n\nsource(here::here(\"scripts/functions.R\"))\n```\n:::\n\n\n# Define our Area of Interest\nHere we diverge a bit from Milos version as we are going to load a custom area of interest. We will be connecting to\nour remote database using Poisson Consulting's `fwapgr::fwa_watershed_at_measure` function which leverages the in database\n[`FWA_WatershedAtMeasure`](https://smnorris.github.io/fwapg/04_functions.html#fwa-watershedatmeasure) function from \n[Simon Norris'](https://github.com/smnorris) wonderful [`fwapg`](https://github.com/smnorris/fwapg)\npackage. \n\n
\n\nWe use a `blue line key` and a `downstream route measure` to define our area of interest which is the Neexdzii Kwa \n(a.k.a Upper Bulkley River) near Houston, British Columbia.\n\n
\n\nAs per the [Freshwater Atlas of BC](https://catalogue.data.gov.bc.ca/dataset/freshwater-atlas-stream-network/resource/5459c8c7-f95c-42fa-a439-24439c11929d) - the `blue line key`:\n\n> Uniquely identifies a single flow line such that a main channel and a secondary channel with the same watershed code would have different blue line keys (the Fraser River and all side channels have different blue line keys).\n\n
\n\nA `downstream route measure` is:\n\n>\tThe distance, in meters, along the route from the mouth of the route to the feature. This distance is measured from the mouth of the containing route to the downstream end of the feature.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# lets build a custom watersehed just for upstream of the confluence of Neexdzii Kwa and Wetzin Kwa\n# blueline key\nblk <- 360873822\n# downstream route measure\ndrm <- 166030.4\n\naoi <- fwapgr::fwa_watershed_at_measure(blue_line_key = blk, \n downstream_route_measure = drm) |> \n sf::st_transform(4326)\n\n#get the bounding box of our aoi\naoi_bb <- sf::st_bbox(aoi)\n```\n:::\n\n\n# Retrieve the Land Cover Data\nFor this example we will retrieve our precipitation data from Microsofts Planetary Computer. We will use `usethis::use_git_ignore` to add the data to our `.gitignore` file so that we do not commit that insano enormous tiff files to our git repository.\n\n usethis::use_git_ignore(paste0('posts/', params$post_dir_name, \"/data/**/*.tif\"))\n \n\n::: {.cell}\n\n```{.r .cell-code}\n# let's create our data directory\ndir_data <- here::here('posts', params$post_dir_name, \"data\")\n\nfs::dir_create(dir_data)\n\n# usethis::use_git_ignore(paste0('posts/', params$post_dir_name, \"/data/**/*.tif\"))\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nms_query <- rstac::stac(\"https://planetarycomputer.microsoft.com/api/stac/v1\")\n\nms_collections <- ms_query |>\n rstac::collections() |>\n rstac::get_request()\n```\n:::\n\n\nNow - we want to understand these datasets a bit better so let's make a little function to view the options for datasets \nwe can download.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Function to extract required fields\n# Function to extract required fields\nextract_fields <- function(x) {\n tibble(\n id = x$id,\n title = x$title,\n time_start = x[[\"cube:dimensions\"]][[\"time\"]][[\"extent\"]][1],\n time_end = x[[\"cube:dimensions\"]][[\"time\"]][[\"extent\"]][2],\n description = x$description\n )\n}\n\n# Apply the function to each element in ms_collections$collections and combine into a dataframe\ndf <- purrr::map_dfr(ms_collections$collections, extract_fields)\n\nmy_dt_table(df, cols_freeze_left = 0, page_length = 5)\n```\n\n::: {.cell-output-display}\n\n```{=html}\n
\n\n```\n\n:::\n:::\n\n\n\n
\n\nHere is the description of our dataset:\n\n\n The European Space Agency (ESA) [WorldCover](https://esa-worldcover.org/en) product provides global land cover maps\n for the years 2020 and 2021 at 10 meter resolution based on the combination of\n [Sentinel-1](https://sentinel.esa.int/web/sentinel/missions/sentinel-1) radar data and\n [Sentinel-2](https://sentinel.esa.int/web/sentinel/missions/sentinel-2) imagery. The discrete classification maps\n provide 11 classes defined using the Land Cover Classification System (LCCS) developed by the United Nations (UN)\n Food and Agriculture Organization (FAO). The map images are stored in [cloud-optimized\n GeoTIFF](https://www.cogeo.org/) format. The WorldCover product is developed by a consortium of European service\n providers and research organizations. [VITO](https://remotesensing.vito.be/) (Belgium) is the prime contractor of\n the WorldCover consortium together with [Brockmann Consult](https://www.brockmann-consult.de/) (Germany), \n [CSSI](https://www.c-s.fr/) (France), [Gamma Remote Sensing AG](https://www.gamma-rs.ch/) (Switzerland), [International\n Institute for Applied Systems Analysis](https://www.iiasa.ac.at/) (Austria), and [Wageningen\n University](https://www.wur.nl/nl/Wageningen-University.htm) (The Netherlands). Two versions of the WorldCover\n product are available: - WorldCover 2020 produced using v100 of the algorithm - [WorldCover 2020 v100 User\n Manual](https://esa-worldcover.s3.eu-central-1.amazonaws.com/v100/2020/docs/WorldCover_PUM_V1.0.pdf) - [WorldCover\n 2020 v100 Validation\n Report]() - WorldCover\n 2021 produced using v200 of the algorithm - [WorldCover 2021 v200 User\n Manual]() -\n [WorldCover 2021 v200 Validaton\n Report]() Since the\n WorldCover maps for 2020 and 2021 were generated with different algorithm versions (v100 and v200, respectively),\n changes between the maps include both changes in real land cover and changes due to the used algorithms.\n\n\nHere we build the query for what we want. We are specifying `collect_this <- \"esa-worldcover\"`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncollect_this <- \"esa-worldcover\"\n\nms_esa_query <- rstac::stac_search(\n q = ms_query,\n collections = collect_this,\n datetime = \"2021-01-01T00:00:00Z/2021-12-31T23:59:59Z\",\n bbox = aoi_bb,\n limit = 100\n ) |>\n rstac::get_request()\n\nms_esa_query\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n###Items\n- features (2 item(s)):\n - ESA_WorldCover_10m_2021_v200_N54W129\n - ESA_WorldCover_10m_2021_v200_N54W126\n- assets: input_quality, map, rendered_preview, tilejson\n- item's fields: \nassets, bbox, collection, geometry, id, links, properties, stac_extensions, stac_version, type\n```\n\n\n:::\n:::\n\nNext we need to sign in to the planetary computer with `rstac::sign_planetary_computer()`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nms_query_signin <- rstac::items_sign(\n ms_esa_query,\n rstac::sign_planetary_computer()\n )\n```\n:::\n\n\n
\n\nTo actually download the data we are going to put a chunk option that allows us to just execute the code once and \nupdate it with the `update_gis` param in our `yml`. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nrstac::assets_download(\n items = ms_query_signin,\n asset_names = \"map\",\n output_dir = here::here('posts', params$post_dir_name, \"data\"),\n overwrite = TRUE\n)\n```\n:::\n\n\n
\n\nNice. So now let's read in these data, clip them to our area of interest with `terra::crop` then combine them into one tiff using\n`terra::mosaic`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndir_out <- here::here('posts', params$post_dir_name, \"data/esa-worldcover/v200/2021/map\")\n\nrast_files <- list.files(\n dir_out,\n full.names = TRUE\n)\n\nland_cover_raster_raw <- rast_files |>\n purrr::map(terra::rast) \n\n# Clip the rasters to the AOI\nland_cover_raster_clipped <- purrr::map(\n land_cover_raster_raw,\n ~ terra::crop(.x, aoi, snap = \"in\", mask = TRUE)\n)\n\n# combine the rasters\nland_cover_raster <- do.call(terra::mosaic, land_cover_raster_clipped)\n\n# Optionally, plot the merged raster\n# terra::plot(land_cover_raster)\n```\n:::\n\n\n# Digital Elevation Model\n\nLet's grab the digital elevation model using `elevatr::get_elev_raster` so we can downsample the land cover raster to the same resolution as the DEM.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndem <- elevatr::get_elev_raster(\n locations = aoi,\n z = 11,\n clip = \"bbox\"\n) |>\n terra::rast()\n```\n:::\n\n\n# Resample\nHere we resample the land cover raster to the same resolution as the DEM.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nland_cover_raster_resampled <- terra::resample(\n land_cover_raster,\n dem,\n method = \"near\"\n)\n\n# terra::plot(land_cover_raster_resampled)\n```\n:::\n\n\n# Plot the Land Cover and DEM\n\n## Get Additional Data\nWe could use some data for context such as major streams and the railway. We get the streams and railway from \ndata distribution bc api using the `bcdata` package. Our `rfp` package calls just allow some extra sanity checks on the \n`bcdata::bcdc_query_geodata` function. It's not really necessary but can be helpful when errors occur (ex. the name of \nthe column to filter on is input incorrectly). \n\n
\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# grab all the railways\nl_rail <- rfp::rfp_bcd_get_data(\n bcdata_record_id = stringr::str_to_upper(\"whse_basemapping.gba_railway_tracks_sp\")\n) |> \n sf::st_transform(4326) |> \n janitor::clean_names() \n\n# streams in the bulkley and then filter to just keep the big ones\nl_streams <- rfp::rfp_bcd_get_data(\n bcdata_record_id = stringr::str_to_upper(\"whse_basemapping.fwa_stream_networks_sp\"),\n col_filter = stringr::str_to_upper(\"watershed_group_code\"),\n col_filter_value = \"BULK\",\n # grab a smaller object by including less columns\n col_extract = stringr::str_to_upper(c(\"linear_feature_id\", \"stream_order\"))\n) |> \n sf::st_transform(4326) |> \n janitor::clean_names() |> \n dplyr::filter(stream_order > 4)\n```\n:::\n\n\nNow we trim up those layers. We have some functions to validate and repair geometries and then we clip them to our area of interest.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlayers_to_trim <- tibble::lst(l_rail, l_streams)\n\n# Function to validate and repair geometries\nvalidate_geometries <- function(layer) {\n layer <- sf::st_make_valid(layer)\n layer <- layer[sf::st_is_valid(layer), ]\n return(layer)\n}\n\n# Apply validation to the AOI and layers\naoi <- validate_geometries(aoi)\nlayers_to_trim <- purrr::map(layers_to_trim, validate_geometries)\n\n# clip them with purrr and sf\nlayers_trimmed <- purrr::map(\n layers_to_trim,\n ~ sf::st_intersection(.x, aoi)\n) \n```\n:::\n\n\n\n## Get Legend Values\n\nSo we need to map the values in the raster to the actual land cover classes. We can do this by extracting the cross\nreference table from the pdf provided in the metatdata of the data. We will use the `tabulapdf` package to extract the\ntable and do some work to collapse it into a cross-referenceing tool we can use for land cover classifications and \nsubsequent color schemes.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# extract the cross reference table from the pdf\npdf_file <- \"https://esa-worldcover.s3.eu-central-1.amazonaws.com/v200/2021/docs/WorldCover_PUM_V2.0.pdf\"\npage <- 15\n\n# table_map <- tabulapdf::locate_areas(pdf_file, pages = page)\n# table_coords <- list(as.numeric(unlist(table_map[[1]])))\n\ntable_coords <- list(c(94.55745, 74.66493, 755.06007, 550.41094))\n\n\nxref_raw <- tabulapdf::extract_tables(\n pdf_file,\n pages = page,\n method = \"lattice\",\n area = table_coords,\n guess = FALSE\n)\n\n# ##this is how we make a clean dataframe\nxref_raw2 <- xref_raw |> \n purrr::pluck(1) |>\n tibble::as_tibble() |>\n janitor::row_to_names(1) |>\n janitor::clean_names()\n\nxref_raw3 <- xref_raw2 %>%\n fill(code, .direction = \"down\")\n\n# Custom function to concatenate rows within each group\ncollapse_rows <- function(df) {\n df %>%\n summarise(across(everything(), ~ paste(na.omit(.), collapse = \" \")))\n}\n\n# Group by code and apply the custom function\nxref <- xref_raw3 %>%\n group_by(code) %>%\n group_modify(~ collapse_rows(.x)) %>%\n ungroup() |> \n dplyr::arrange(code) |> \n purrr::set_names(c(\"code\", \"land_cover_class\", \"lccs_code\", \"definition\", \"color_code\")) |> \n tidyr::separate(color_code, into = c(\"r\", \"g\", \"b\"), sep = \",\", convert = TRUE, remove = FALSE) %>%\n dplyr::mutate(color = rgb(r, g, b, maxColorValue = 255))\n\nmy_dt_table(xref, cols_freeze_left = 0, page_length = 5)\n```\n\n::: {.cell-output-display}\n\n```{=html}\n
\n\n```\n\n:::\n:::\n\n\nSo - looks like when we combined our tiffs together with `terra::mosaic` we lost the color table associated with the SpatRaster object.\nWe can recover that table with `terra::coltab(land_cover_raster_raw[[1]])`\n\n## Plot \nOk. Let's plot it up. We will use `ggplot2` and `tidyterra` to plot the land cover raster and then add the streams and railway on top of that.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolor_table <- terra::coltab(land_cover_raster_raw[[1]])[[1]]\n\ncoltab(land_cover_raster_resampled) <- color_table\n\n\nmap <- ggplot() +\n tidyterra::geom_spatraster(\n data = as.factor(land_cover_raster_resampled),\n use_coltab = TRUE,\n maxcell = Inf\n ) +\n tidyterra::scale_fill_coltab(\n data = as.factor(land_cover_raster_resampled),\n name = \"ESA Land Cover\",\n labels = xref$land_cover_class\n ) +\n # geom_sf(\n # data = aoi,\n # fill = \"transparent\",\n # color = \"white\",\n # linewidth = .5\n # ) +\n geom_sf(\n data = layers_trimmed$l_streams,\n color = \"blue\",\n size = 1\n ) +\n geom_sf(\n data = layers_trimmed$l_rail,\n color = \"yellow\",\n size = 1\n ) +\n ggdark::dark_theme_void()\n\n\n# save the plot\n# ggsave(here::here('posts', params$post_dir_name, \"image.jpg\"), width = 10, height = 10)\n\nmap\n```\n\n::: {.cell-output-display}\n![](index_files/figure-html/plot-1.png){width=672}\n:::\n:::\n", + "markdown": "---\ntitle: \"Mapping Land Cover with R\"\nauthor: \"al\"\ndate: \"2024-07-01\"\ndate-modified: \"2024-07-01\"\ncategories: [land cover, R, planetary computer, remote sensing]\nimage: \"image.jpg\"\nparams:\n repo_owner: \"NewGraphEnvironment\"\n repo_name: \"new_graphiti\"\n post_dir_name: \"2024-06-30-land-cover\"\n update_pkgs: FALSE\n update_gis: FALSE\nexecute:\n warning: false\nformat: \n html:\n code-fold: true\n---\n\n\nWe want to quantifying and visualize remotely sense land cover data.... Here is a first start. We will use the European\nSpace Agency's WorldCover product which provides global land cover maps for the years 2020 and 2021 at 10 meter\nresolution based on the combination of Sentinel-1 radar data and Sentinel-2 imagery. We will use the 2021 dataset\nfor mapping an area of the Skeena watershed near Houston, British Columbia. \n\n
\n\n\nThis post was inspired - with much of the code copied - from a repository on GitHub from the wonderfully talented\n[Milos Popovic](https://github.com/milos-agathon/esa-land-cover). \n\n
\n\nFirst thing we will do is load our packages. If you do not have the packages installed yet you can change the `update_pkgs` param in\nthe `yml` of this file to `TRUE`. Using `pak` is great because it allows you to update your packages when you want to.\n\n\n::: {.cell}\n\n```{.r .cell-code}\npkgs_cran <- c(\n \"usethis\",\n \"rstac\",\n \"here\",\n \"fs\",\n \"terra\",\n \"tidyverse\",\n \"rayshader\",\n \"sf\",\n \"classInt\",\n \"rgl\",\n \"tidyterra\",\n \"tabulapdf\",\n \"bcdata\",\n \"ggplot\",\n \"ggdark\")\n\npkgs_gh <- c(\n \"poissonconsulting/fwapgr\",\n \"NewGraphEnvironment/rfp\"\n )\n\npkgs <- c(pkgs_cran, pkgs_gh)\n\nif(params$update_pkgs){\n # install the pkgs\n lapply(pkgs,\n pak::pkg_install,\n ask = FALSE)\n}\n\n# load the pkgs\npkgs_ld <- c(pkgs_cran,\n basename(pkgs_gh))\ninvisible(\n lapply(pkgs_ld,\n require,\n character.only = TRUE)\n)\n\nsource(here::here(\"scripts/functions.R\"))\n```\n:::\n\n\n# Define our Area of Interest\nHere we diverge a bit from Milos version as we are going to load a custom area of interest. We will be connecting to\nour remote database using Poisson Consulting's `fwapgr::fwa_watershed_at_measure` function which leverages the in database\n[`FWA_WatershedAtMeasure`](https://smnorris.github.io/fwapg/04_functions.html#fwa-watershedatmeasure) function from \n[Simon Norris'](https://github.com/smnorris) wonderful [`fwapg`](https://github.com/smnorris/fwapg)\npackage. \n\n
\n\nWe use a `blue line key` and a `downstream route measure` to define our area of interest which is the Neexdzii Kwa \n(a.k.a Upper Bulkley River) near Houston, British Columbia.\n\n
\n\nAs per the [Freshwater Atlas of BC](https://catalogue.data.gov.bc.ca/dataset/freshwater-atlas-stream-network/resource/5459c8c7-f95c-42fa-a439-24439c11929d) - the `blue line key`:\n\n> Uniquely identifies a single flow line such that a main channel and a secondary channel with the same watershed code would have different blue line keys (the Fraser River and all side channels have different blue line keys).\n\n
\n\nA `downstream route measure` is:\n\n>\tThe distance, in meters, along the route from the mouth of the route to the feature. This distance is measured from the mouth of the containing route to the downstream end of the feature.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# lets build a custom watersehed just for upstream of the confluence of Neexdzii Kwa and Wetzin Kwa\n# blueline key\nblk <- 360873822\n# downstream route measure\ndrm <- 166030.4\n\naoi <- fwapgr::fwa_watershed_at_measure(blue_line_key = blk, \n downstream_route_measure = drm) |> \n sf::st_transform(4326)\n\n#get the bounding box of our aoi\naoi_bb <- sf::st_bbox(aoi)\n```\n:::\n\n\n# Retrieve the Land Cover Data\nFor this example we will retrieve our precipitation data from Microsofts Planetary Computer. We will use `usethis::use_git_ignore` to add the data to our `.gitignore` file so that we do not commit that insano enormous tiff files to our git repository.\n\n usethis::use_git_ignore(paste0('posts/', params$post_dir_name, \"/data/**/*.tif\"))\n \n\n::: {.cell}\n\n```{.r .cell-code}\n# let's create our data directory\ndir_data <- here::here('posts', params$post_dir_name, \"data\")\n\nfs::dir_create(dir_data)\n\n# usethis::use_git_ignore(paste0('posts/', params$post_dir_name, \"/data/**/*.tif\"))\n```\n:::\n\n::: {.cell}\n\n```{.r .cell-code}\nms_query <- rstac::stac(\"https://planetarycomputer.microsoft.com/api/stac/v1\")\n\nms_collections <- ms_query |>\n rstac::collections() |>\n rstac::get_request()\n```\n:::\n\n\nNow - we want to understand these datasets a bit better so let's make a little function to view the options for datasets \nwe can download.\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# Function to extract required fields\n# Function to extract required fields\nextract_fields <- function(x) {\n tibble(\n id = x$id,\n title = x$title,\n time_start = x[[\"cube:dimensions\"]][[\"time\"]][[\"extent\"]][1],\n time_end = x[[\"cube:dimensions\"]][[\"time\"]][[\"extent\"]][2],\n description = x$description\n )\n}\n\n# Apply the function to each element in ms_collections$collections and combine into a dataframe\ndf <- purrr::map_dfr(ms_collections$collections, extract_fields)\n\nmy_dt_table(df, cols_freeze_left = 0, page_length = 5)\n```\n\n::: {.cell-output-display}\n\n```{=html}\n
\n\n```\n\n:::\n:::\n\n\n\n
\n\nHere is the description of our dataset:\n\n\n The European Space Agency (ESA) [WorldCover](https://esa-worldcover.org/en) product provides global land cover maps\n for the years 2020 and 2021 at 10 meter resolution based on the combination of\n [Sentinel-1](https://sentinel.esa.int/web/sentinel/missions/sentinel-1) radar data and\n [Sentinel-2](https://sentinel.esa.int/web/sentinel/missions/sentinel-2) imagery. The discrete classification maps\n provide 11 classes defined using the Land Cover Classification System (LCCS) developed by the United Nations (UN)\n Food and Agriculture Organization (FAO). The map images are stored in [cloud-optimized\n GeoTIFF](https://www.cogeo.org/) format. The WorldCover product is developed by a consortium of European service\n providers and research organizations. [VITO](https://remotesensing.vito.be/) (Belgium) is the prime contractor of\n the WorldCover consortium together with [Brockmann Consult](https://www.brockmann-consult.de/) (Germany), \n [CSSI](https://www.c-s.fr/) (France), [Gamma Remote Sensing AG](https://www.gamma-rs.ch/) (Switzerland), [International\n Institute for Applied Systems Analysis](https://www.iiasa.ac.at/) (Austria), and [Wageningen\n University](https://www.wur.nl/nl/Wageningen-University.htm) (The Netherlands). Two versions of the WorldCover\n product are available: - WorldCover 2020 produced using v100 of the algorithm - [WorldCover 2020 v100 User\n Manual](https://esa-worldcover.s3.eu-central-1.amazonaws.com/v100/2020/docs/WorldCover_PUM_V1.0.pdf) - [WorldCover\n 2020 v100 Validation\n Report]() - WorldCover\n 2021 produced using v200 of the algorithm - [WorldCover 2021 v200 User\n Manual]() -\n [WorldCover 2021 v200 Validaton\n Report]() Since the\n WorldCover maps for 2020 and 2021 were generated with different algorithm versions (v100 and v200, respectively),\n changes between the maps include both changes in real land cover and changes due to the used algorithms.\n\n\nHere we build the query for what we want. We are specifying `collect_this <- \"esa-worldcover\"`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncollect_this <- \"esa-worldcover\"\n\nms_esa_query <- rstac::stac_search(\n q = ms_query,\n collections = collect_this,\n datetime = \"2021-01-01T00:00:00Z/2021-12-31T23:59:59Z\",\n bbox = aoi_bb,\n limit = 100\n ) |>\n rstac::get_request()\n\nms_esa_query\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n###Items\n- features (2 item(s)):\n - ESA_WorldCover_10m_2021_v200_N54W129\n - ESA_WorldCover_10m_2021_v200_N54W126\n- assets: input_quality, map, rendered_preview, tilejson\n- item's fields: \nassets, bbox, collection, geometry, id, links, properties, stac_extensions, stac_version, type\n```\n\n\n:::\n:::\n\nNext we need to sign in to the planetary computer with `rstac::sign_planetary_computer()`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nms_query_signin <- rstac::items_sign(\n ms_esa_query,\n rstac::sign_planetary_computer()\n )\n```\n:::\n\n\n
\n\nTo actually download the data we are going to put a chunk option that allows us to just execute the code once and \nupdate it with the `update_gis` param in our `yml`. \n\n\n::: {.cell}\n\n```{.r .cell-code}\nrstac::assets_download(\n items = ms_query_signin,\n asset_names = \"map\",\n output_dir = here::here('posts', params$post_dir_name, \"data\"),\n overwrite = TRUE\n)\n```\n:::\n\n\n
\n\nNice. So now let's read in these data, clip them to our area of interest with `terra::crop` then combine them into one tiff using\n`terra::mosaic`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndir_out <- here::here('posts', params$post_dir_name, \"data/esa-worldcover/v200/2021/map\")\n\nrast_files <- list.files(\n dir_out,\n full.names = TRUE\n)\n\nland_cover_raster_raw <- rast_files |>\n purrr::map(terra::rast) \n\n# Clip the rasters to the AOI\nland_cover_raster_clipped <- purrr::map(\n land_cover_raster_raw,\n ~ terra::crop(.x, aoi, snap = \"in\", mask = TRUE)\n)\n\n# combine the rasters\nland_cover_raster <- do.call(terra::mosaic, land_cover_raster_clipped)\n\n# Optionally, plot the merged raster\n# terra::plot(land_cover_raster)\n```\n:::\n\n\n# Digital Elevation Model\n\nLet's grab the digital elevation model using `elevatr::get_elev_raster` so we can downsample the land cover raster to the same resolution as the DEM.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ndem <- elevatr::get_elev_raster(\n locations = aoi,\n z = 11,\n clip = \"bbox\"\n) |>\n terra::rast()\n```\n:::\n\n\n# Resample\nHere we resample the land cover raster to the same resolution as the DEM.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nland_cover_raster_resampled <- terra::resample(\n land_cover_raster,\n dem,\n method = \"near\"\n)\n\n# terra::plot(land_cover_raster_resampled)\n```\n:::\n\n\n# Plot the Land Cover and DEM\n\n## Get Additional Data\nWe could use some data for context such as major streams and the railway. We get the streams and railway from \ndata distribution bc api using the `bcdata` package. Our `rfp` package calls just allow some extra sanity checks on the \n`bcdata::bcdc_query_geodata` function. It's not really necessary but can be helpful when errors occur (ex. the name of \nthe column to filter on is input incorrectly). \n\n
\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# grab all the railways\nl_rail <- rfp::rfp_bcd_get_data(\n bcdata_record_id = \"whse_basemapping.gba_railway_tracks_sp\"\n) |> \n sf::st_transform(4326) |> \n janitor::clean_names() \n\n# streams in the bulkley and then filter to just keep the big ones\nl_streams <- rfp::rfp_bcd_get_data(\n bcdata_record_id = \"whse_basemapping.fwa_stream_networks_sp\",\n col_filter = \"watershed_group_code\",\n col_filter_value = \"BULK\",\n # grab a smaller object by including less columns\n col_extract = c(\"linear_feature_id\", \"stream_order\", \"gnis_name\", \"downstream_route_measure\", \"blue_line_key\", \"length_metre\")\n) |> \n sf::st_transform(4326) |> \n janitor::clean_names() |> \n dplyr::filter(stream_order > 4)\n```\n:::\n\n\nNow we trim up those layers. We have some functions to validate and repair geometries and then we clip them to our area of interest.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nlayers_to_trim <- tibble::lst(l_rail, l_streams)\n\n# Function to validate and repair geometries\nvalidate_geometries <- function(layer) {\n layer <- sf::st_make_valid(layer)\n layer <- layer[sf::st_is_valid(layer), ]\n return(layer)\n}\n\n# Apply validation to the AOI and layers\naoi <- validate_geometries(aoi)\nlayers_to_trim <- purrr::map(layers_to_trim, validate_geometries)\n\n# clip them with purrr and sf\nlayers_trimmed <- purrr::map(\n layers_to_trim,\n ~ sf::st_intersection(.x, aoi)\n) \n```\n:::\n\n\n\n## Get Legend Values\n\nSo we need to map the values in the raster to the actual land cover classes. We can do this by extracting the cross\nreference table from the pdf provided in the metatdata of the data. We will use the `tabulapdf` package to extract the\ntable and do some work to collapse it into a cross-referenceing tool we can use for land cover classifications and \nsubsequent color schemes.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\n# extract the cross reference table from the pdf\npdf_file <- \"https://esa-worldcover.s3.eu-central-1.amazonaws.com/v200/2021/docs/WorldCover_PUM_V2.0.pdf\"\npage <- 15\n\n# table_map <- tabulapdf::locate_areas(pdf_file, pages = page)\n# table_coords <- list(as.numeric(unlist(table_map[[1]])))\n\ntable_coords <- list(c(94.55745, 74.66493, 755.06007, 550.41094))\n\n\nxref_raw <- tabulapdf::extract_tables(\n pdf_file,\n pages = page,\n method = \"lattice\",\n area = table_coords,\n guess = FALSE\n)\n\n# ##this is how we make a clean dataframe\nxref_raw2 <- xref_raw |> \n purrr::pluck(1) |>\n tibble::as_tibble() |>\n janitor::row_to_names(1) |>\n janitor::clean_names()\n\nxref_raw3 <- xref_raw2 |> \n tidyr::fill(code, .direction = \"down\")\n\n# Custom function to concatenate rows within each group\ncollapse_rows <- function(df) {\n df |> \n dplyr::summarise(across(everything(), ~ paste(na.omit(.), collapse = \" \")))\n}\n\n# Group by code and apply the custom function\nxref <- xref_raw3 |>\n dplyr::group_by(code) |>\n dplyr::group_modify(~ collapse_rows(.x)) |>\n dplyr::ungroup() |> \n dplyr::mutate(code = as.numeric(code)) |> \n dplyr::arrange(code) |> \n purrr::set_names(c(\"code\", \"land_cover_class\", \"lccs_code\", \"definition\", \"color_code\")) |> \n # now we make a list of the color codes and convert to hex. Even though we don't actually need them here...\n dplyr::mutate(color_code = purrr::map(color_code, ~ as.numeric(strsplit(.x, \",\")[[1]])),\n color = purrr::map_chr(color_code, ~ rgb(.x[1], .x[2], .x[3], maxColorValue = 255))) |> \n dplyr::relocate(definition, .after = last_col())\n\n\nmy_dt_table(xref, cols_freeze_left = 0, page_length = 5)\n```\n\n::: {.cell-output-display}\n\n```{=html}\n
\n\n```\n\n:::\n:::\n\n\nWe seem to get issues when the colors we have in our tiff does not match our cross-reference table. For this reason we will\nremove any values in the `xref` object that are not in the rasters that we are plotting.\n\n\n
\n\nAlso - looks like when we combined our tiffs together with `terra::mosaic` we lost the color table associated with the SpatRaster object.\nWe can recover that table with `terra::coltab(land_cover_raster_raw[[1]])`\n\n## Plot \nOk. Let's plot it up. We will use `ggplot2` and `tidyterra` to plot the land cover raster and then add the streams and railway on top of that.\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncolor_table <- terra::coltab(land_cover_raster_raw[[1]])[[1]]\n\ncoltab(land_cover_raster_resampled) <- color_table\n\nxref_cleaned <- xref |> \n filter(code %in% sort(unique(terra::values(land_cover_raster_resampled))))\n\n\nmap <- ggplot() +\n tidyterra::geom_spatraster(\n data = as.factor(land_cover_raster_resampled),\n use_coltab = TRUE,\n maxcell = Inf\n ) +\n tidyterra::scale_fill_coltab(\n data = as.factor(land_cover_raster_resampled),\n name = \"ESA Land Cover\",\n labels = xref_cleaned$land_cover_class\n ) +\n # geom_sf(\n # data = aoi,\n # fill = \"transparent\",\n # color = \"white\",\n # linewidth = .5\n # ) +\n geom_sf(\n data = layers_trimmed$l_streams,\n color = \"blue\",\n size = 1\n ) +\n geom_sf(\n data = layers_trimmed$l_rail,\n color = \"black\",\n size = 1\n ) +\n ggdark::dark_theme_void()\n\n\nmap\n```\n\n::: {.cell-output-display}\n![](index_files/figure-html/plot-1.png){width=672}\n:::\n:::\n\n\n# Refine the Area of Interest\n\n\n::: {.cell}\n\n```{.r .cell-code}\nbuffer_size <- 250\nlength_define <- 10000\n```\n:::\n\n\n\nNice. So let's say we want to zoom in on a particular area such as the mainstem of the Wetzin Kwa for the lowest\n10km of the stream for a buffered area at approximately 250m either side of the stream. We can do that by getting the name of the stream. The smallest downstream route measure and the first segments that add to 10000m in linear length. \n\n\n\n::: {.cell}\n\n```{.r .cell-code}\naoi_neexdzii <- layers_trimmed$l_streams |> \n dplyr::filter(gnis_name == \"Bulkley River\") |> \n dplyr::arrange(downstream_route_measure) |> \n # calculate when we get to 10000m by adding up the length_metre field and filtering out everything up to 10000m\n dplyr::filter(cumsum(length_metre) <= length_define) \n\naoi_neexdzii_buffered <- sf::st_buffer(aoi_neexdzii, buffer_size, endCapStyle = \"FLAT\")\n```\n:::\n\n\n## Plot Refined Area\n\nClip the resampled raster to the buffered area.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nland_cover_sample <- terra::crop(land_cover_raster_resampled, aoi_neexdzii_buffered, snap = \"in\", mask = TRUE, extend = TRUE)\n```\n:::\n\n\n
\n\nSo it looks like we lose our color values with the crop. We see that with `has.colors(land_cover_sample)`.\n\n\n::: {.cell}\n\n```{.r .cell-code}\nhas.colors(land_cover_sample)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] TRUE\n```\n\n\n:::\n:::\n\n\n
\n\nLet's add them back in with the `terra::coltab` function.\n\n\n\n::: {.cell}\n\n```{.r .cell-code}\ncoltab(land_cover_sample) <- color_table\nhas.colors(land_cover_sample)\n```\n\n::: {.cell-output .cell-output-stdout}\n\n```\n[1] TRUE\n```\n\n\n:::\n:::\n\n\n
\n\nNow we should be able to plot what we have. Let's re-trim up our extra data layers and add those in as well.\n\n::: {.cell}\n\n```{.r .cell-code}\n# clip them with purrr and sf\nlayers_trimmed <- purrr::map(\n layers_to_trim,\n ~ sf::st_intersection(.x, aoi_neexdzii_buffered)\n) \n\nxref_cleaned <- xref |> \n filter(code %in% sort(unique(terra::values(land_cover_sample))))\n\nmap <- ggplot2::ggplot() +\n tidyterra::geom_spatraster(\n data = as.factor(land_cover_sample),\n use_coltab = TRUE,\n maxcell = Inf\n ) +\n tidyterra::scale_fill_coltab(\n data = as.factor(land_cover_sample),\n name = \"ESA Land Cover\",\n labels = xref_cleaned$land_cover_class\n ) +\n geom_sf(\n data = layers_trimmed$l_rail,\n color = \"black\",\n size = 1\n ) +\n geom_sf(\n data = layers_trimmed$l_streams,\n color = \"blue\",\n size = 1\n ) +\n ggdark::dark_theme_void()\n\n# save the plot\n# ggsave(here::here('posts', params$post_dir_name, \"image.jpg\"), width = 10, height = 10)\n \nmap\n```\n\n::: {.cell-output-display}\n![](index_files/figure-html/plot2-1.png){width=672}\n:::\n:::\n\n\n
\n\nPretty sweet. Next up is to summarize the land cover classes for different areas to build our understanding of\npotential impacts due to land cover changes. We will likely use the `terra::zonal` function to do this. Stay tuned.\n", "supporting": [], "filters": [ "rmarkdown/pagebreak.lua" diff --git a/_freeze/posts/2024-06-30-land-cover/index/figure-html/plot-1.png b/_freeze/posts/2024-06-30-land-cover/index/figure-html/plot-1.png index 25004b7c087c37c066f773ea380f0f2cad06df10..b099984b8aa8c484292cc77878efc4ee23091b9c 100644 GIT binary patch delta 199491 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