diff --git a/.Rbuildignore b/.Rbuildignore index 50fc28c1..d579db7b 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -17,5 +17,7 @@ ^Meta$ ^.vscode$ ^\.agent$ +^Rplot\.png$ +^typology_kgha_agricultural\.jpg$ ^\.claude$ ^CLAUDE\.md$ diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..c891dc0a --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [ + "Bash(\"C:/Users/Usuario/AppData/Local/Programs/R/R-4.4.3/bin/Rscript.exe\" -e \"devtools::load_all\\('.', quiet=TRUE\\); df <- whep_read_file\\('n_excretion_ygs'\\); cat\\('Columns:', paste\\(names\\(df\\), collapse=', '\\), '\\\\n'\\); print\\(head\\(df, 2\\)\\)\")", + "Read(//c/Users/Usuario/AppData/Local/Programs/**)", + "Read(//c/Program Files/R//**)", + "Bash(ls /c/R*)", + "Bash('/c/Program Files/R/R-4.5.0/bin/Rscript.exe' -e ' *)", + "Bash('/c/Program Files/R/R-4.5.0/bin/Rscript.exe' -e 'suppressMessages\\(devtools::load_all\\('\\\\''.'\\\\''\\)\\); df <- whep_read_file\\('\\\\''livestock_prod_ygps'\\\\''\\); cat\\(sum\\(df[df$Year==2000,'\\\\''Prod_Mg'\\\\''], na.rm=TRUE\\), '\\\\''\\\\n'\\\\''\\); cat\\(sum\\(df[df$Year==2000,'\\\\''Prod_MgN'\\\\''], na.rm=TRUE\\), '\\\\''\\\\n'\\\\''\\)')", + "Bash('/c/Program Files/R/R-4.5.0/bin/Rscript.exe' --vanilla -e 'library\\(whep\\); df <- whep_read_file\\('\\\\''livestock_prod_ygps'\\\\''\\); sub <- df[df$Year==2000 & df$Province_name=='\\\\''Madrid'\\\\'' & df$Livestock_cat=='\\\\''Cattle_meat'\\\\'', c\\('\\\\''item_prod'\\\\'','\\\\''item_cbs'\\\\'','\\\\''Prod_Mg'\\\\'','\\\\''Prod_MgN'\\\\''\\)]; write.csv\\(sub, stderr\\(\\)\\)')", + "Bash('/c/Program Files/R/R-4.5.0/bin/Rscript.exe' --vanilla -e ' *)" + ] + } +} diff --git a/.gitignore b/.gitignore index 5719c83a..24e02b7f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ vignettes/methods_production_cbs.qmd vignettes/methods_production_cbs.html vignettes/methods_production_cbs_files/ vignettes/methods_references.bib +Rplots.pdf +typology_kgha_agricultural.jpg diff --git a/DESCRIPTION b/DESCRIPTION index 67b60122..a1748234 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,7 @@ Authors@R: c( Description: A set of tools for processing and analyzing data developed in the context of the "Who Has Eaten the Planet" (WHEP) project, funded by the European Research Council (ERC). For more details on multi-regional - input–output model "Food and Agriculture Biomass Input–Output" (FABIO) see + input–output model "Food and Agriculture Biomass Input–Output" (FABIO) see Bruckner et al. (2019) . License: MIT + file LICENSE Imports: @@ -30,20 +30,27 @@ Imports: rappdirs, readr, rlang, + scales, stringr, tibble, tidyr, withr, yaml, + readxl, + sf, + stringi, zoo Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.3 -Suggests: +Suggests: + DiagrammeR, + ggpattern, ggplot2, googlesheets4, here, knitr, + patchwork, pointblank, rmarkdown, testthat (>= 3.0.0) @@ -51,6 +58,6 @@ Config/testthat/edition: 3 VignetteBuilder: knitr URL: https://eduaguilera.github.io/whep/, https://github.com/eduaguilera/whep BugReports: https://github.com/eduaguilera/whep/issues -Depends: +Depends: R (>= 3.5) LazyData: true diff --git a/NAMESPACE b/NAMESPACE index 23a100d1..2ffafa96 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -15,10 +15,15 @@ export(calculate_lmdi) export(calculate_nue_crops) export(calculate_nue_livestock) export(calculate_system_nue) +export(create_alfredos_typologies) +export(create_grafs_plot_df) export(create_n_nat_destiny) export(create_n_production) export(create_n_prov_destiny) export(create_n_soil_inputs) +export(create_typologies_grafs_spain) +export(create_typologies_of_josette) +export(create_typologies_whep) export(expand_trade_sources) export(fill_linear) export(fill_proxy_growth) @@ -32,6 +37,9 @@ export(get_processing_coefs) export(get_wide_cbs) export(harmonize_interpolate) export(harmonize_simple) +export(plot_input_output) +export(plot_input_output_livestock) +export(plot_input_output_system) export(whep_list_file_versions) export(whep_read_file) importFrom(pins,pin_fetch) diff --git a/R/Typologies_Josette.R b/R/Typologies_Josette.R new file mode 100644 index 00000000..05a38f33 --- /dev/null +++ b/R/Typologies_Josette.R @@ -0,0 +1,790 @@ +#' @title Typologies of Josette +#' +#' @description +#' Typologies of provinces in Spain based on nitrogen (N) production +#' data of crops and livestock, considering multiple data inputs and +#' producing classification maps and data frames. +#' +#' @param make_map If TRUE a map of the typologies will be created. +#' +#' @param shapefile_path Path to the shapefile used for mapping provinces. +#' +#' @param map_year The year for which the typology map is created. +#' +#' @return A tibble with the typology classification per year and province. +#' +#' @export +create_typologies_of_josette <- function( + make_map = TRUE, + shapefile_path = paste0( + "C:/PhD/GRAFS/Production Boxes/", + "Final Files/Inputs/ne_10m_admin_1_states_provinces.shp" + ), + map_year = 1980 +) { + inputs_dir <- "C:/PhD/GRAFS/Production Boxes/Final Files/Inputs" + + # Load datasets + data <- .load_inputs_josette(inputs_dir, shapefile_path) + str(data$n_input_df) + + consumption <- .calculate_consumption_prod(data$grafs_prod_destiny_git) + food_consumption_df <- consumption$food_consumption + production_df <- consumption$production + + crop_feed <- .calculate_crop_prod_feed( + data$grafs_prod_destiny_git + ) + cropland_prod_df <- crop_feed$cropland_prod + animal_ingestion_df <- crop_feed$animal_ingestion + + intensive_list <- .calculate_imported_feed( + data$Livestock_Prod_ygps, + data$Codes_coefs, + data$NPP_ygpit, + data$PIE_FullDestinies_FM, + data$grafs_prod_destiny_git + ) + + seminatural_df <- .calculate_natural_feed_share( + data$grafs_prod_destiny_git + ) + feed_domestic_df <- .calculate_feed_domestic_share( + data$PIE_FullDestinies_FM, + intensive_list$lu_totals, + data$codes_coefs_item, + data$biomass_coefs + ) + manure_share_df <- .calculate_manure_share(data$n_input_df) + + typologies_df <- food_consumption_df |> + dplyr::left_join(production_df, by = c("Year", "Province_name")) |> + dplyr::left_join(cropland_prod_df, by = c("Year", "Province_name")) |> + dplyr::left_join(animal_ingestion_df, by = c("Year", "Province_name")) |> + dplyr::left_join( + intensive_list$livestock_density_df, + by = c("Year", "Province_name") + ) |> + dplyr::left_join( + intensive_list$imported_feed_share_df, + by = c("Year", "Province_name") + ) |> + dplyr::left_join(seminatural_df, by = c("Year", "Province_name")) |> + dplyr::left_join(feed_domestic_df, by = c("Year", "Province_name")) |> + dplyr::left_join(manure_share_df, by = c("Year", "Province_name")) + + typologies_df <- .assign_typologies(typologies_df) |> + dplyr::distinct(Year, Province_name, Typology) + + if (make_map) { + .create_typologies_map_josette(typologies_df, shapefile_path, map_year) + } + + df_inputs_plots <- .create_bar_plots_inputs( + typologies_df = typologies_df, + n_input_df = data$n_input_df, + imported_feed_share_df = feed_domestic_df + ) + + list( + typologies_df = typologies_df, + n_input_df = data$n_input_df, + imported_feed_share_df = intensive_list$imported_feed_share_df, + df_inputs_plots = df_inputs_plots + ) +} + +#' @title Load input datasets ------------------------------------------------- +#' @description Loads all necessary datasets, including shapefiles for +#' Spanish provinces. +#' @param shapefile_path The local path where the input data are located. +#' @param inputs_dir Path to the input data directory. +#' @keywords internal +#' @noRd +.load_inputs_josette <- function(inputs_dir, shapefile_path) { + layer_name <- tools::file_path_sans_ext(basename(shapefile_path)) + + sf_provinces_spain <- sf::st_read( + shapefile_path, + query = paste0( + "SELECT * FROM ", + layer_name, + " WHERE iso_a2 = 'ES'" + ) + ) + + list( + Livestock_Prod_ygps = readr::read_csv(file.path( + inputs_dir, + "Livestock_Prod_ygps.csv" + )), + Codes_coefs = readxl::read_excel( + file.path(inputs_dir, "Codes_coefs.xlsx"), + sheet = "Liv_LU_coefs" + ), + codes_coefs_item = readxl::read_excel( + file.path(inputs_dir, "Codes_coefs.xlsx"), + sheet = "items_full" + ), + NPP_ygpit = readr::read_csv(file.path(inputs_dir, "NPP_ygpit.csv.gz")), + grafs_prod_destiny_git = readr::read_csv(file.path( + inputs_dir, + "GRAFS_Prod_Destiny_git.csv" + )), + PIE_FullDestinies_FM = readr::read_csv(file.path( + inputs_dir, + "PIE_FullDestinies_FM.csv" + )), + biomass_coefs = readxl::read_excel( + file.path( + inputs_dir, + "Biomass_coefs.xlsx" + ), + sheet = "Coefs", + skip = 1 + ), + sf_provinces_spain = sf_provinces_spain, + n_input_df = readr::read_csv(file.path(inputs_dir, "n_inputs_combined.csv")) + ) +} + +#' @title Calculate food consumption and total production---------------------- +#' @description Calculated N from food consumption and agricultural +#' production per province and year. +#' @param grafs_prod_destiny_git Data frame containing nitrogen data by +#' destiny and box. +#' @return A list with two tibbles: 'food_consumption' and 'production'. +#' @keywords internal +#' @noRd +.calculate_consumption_prod <- function(grafs_prod_destiny_git) { + # Food consumption + food_consumption <- grafs_prod_destiny_git |> + dplyr::filter(Destiny == "Food") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Food_Consumption_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) + + # Agricultural production + production <- grafs_prod_destiny_git |> + dplyr::filter( + Destiny %in% + c( + "Food", + "Feed", + "Other_uses", + "Export", + "Import" + ) + ) |> + dplyr::group_by(Year, Province_name, Destiny) |> + dplyr::summarise(MgN = sum(MgN, na.rm = TRUE), .groups = "drop") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Production_MgN = sum( + MgN[ + Destiny %in% + c( + "Food", + "Feed", + "Other_uses", + "Export" + ) + ], + na.rm = TRUE + ) - + sum(MgN[Destiny == "Import"], na.rm = TRUE), + .groups = "drop" + ) + + list( + food_consumption = food_consumption, + production = production + ) +} + +#' @title Calculate Crop Production and Animal Feed Ingestion------------------ +#' @description Calculates total cropland N production and N ingested by animals +#' @param grafs_prod_destiny_git Data frame containing N data by destiny and box +#' @return A list with two tibbles: 'cropland_prod' and 'animal_ingestion'. +#' @keywords internal +#' @noRd +.calculate_crop_prod_feed <- function(grafs_prod_destiny_git) { + # Cropland production + cropland_prod <- grafs_prod_destiny_git |> + dplyr::filter( + Box == "Cropland", + Destiny %in% + c( + "Food", + "Feed", + "Other_uses", + "Export", + "Import" + ) + ) |> + dplyr::group_by(Year, Province_name, Destiny) |> + dplyr::summarise(MgN = sum(MgN, na.rm = TRUE), .groups = "drop") |> + tidyr::pivot_wider( + names_from = Destiny, + values_from = MgN, + values_fill = 0 + ) |> + dplyr::mutate( + Cropland_Production_MgN = Food + Feed + `Other_uses` + Export - Import + ) |> + dplyr::select(Year, Province_name, Cropland_Production_MgN) + + # Animal ingestion (Feed) + animal_ingestion <- grafs_prod_destiny_git |> + dplyr::filter(Destiny == "Feed") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Animal_Ingestion_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) + + list( + cropland_prod = cropland_prod, + animal_ingestion = animal_ingestion + ) +} + +#' @title Decision: Livestock density > 1 LU/ha UAA & >33% animal feed +#' from imports +#' +#' @description +#' Calculates livestock unit totals, livestock density per UAA (Utilized +#' Agricultural Area), and the share of imported feed at the province level. +#' +#' @param livestock_df A data frame containing livestock production per year +#' and province. +#' @param codes_coefs_df A data frame with livestock unit (LU) coefficients for +#' different categories. +#' @param npp_df A data frame with net primary production (NPP) and agricultural +#' area (UAA) data. +#' @param feed_df A data frame containing feed data including import, export, +#' and production. +#' @param destiny_df A data frame from GRAFS showing nitrogen fluxes by +#' destination and box. +#' +#' @return A list containing LU per province and year, Livestock Density, +#' share of imported feed +#' @keywords internal +#' @noRd +.calculate_imported_feed <- function( + livestock_df, + codes_coefs_df, + npp_df, + feed_df, + destiny_df +) { + lu_coefs <- .prepare_lu_coefs(codes_coefs_df) + lu_detailed <- .calculate_lu_totals(livestock_df, lu_coefs) + lu_totals <- .aggregate_lu_totals(lu_detailed) + area_uaa <- .aggregate_area_aa(npp_df) + livestock_density_df <- .calculate_livestock_density(lu_totals, area_uaa) + + feed_import_by_province <- .calculate_feed_import_share(feed_df, lu_totals) + + domestic_feed_by_province <- destiny_df |> + dplyr::filter(Destiny == "Feed", Box == "Cropland") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Domestic_feed_MgN = sum(MgN, na.rm = TRUE), + .groups = "drop" + ) + + imported_feed_share_df <- .calculate_imported_feed_share( + feed_import_by_province, + domestic_feed_by_province + ) + + list( + lu_totals = lu_totals, + livestock_density_df = livestock_density_df, + imported_feed_share_df = imported_feed_share_df + ) +} + +#' @title Decision: >50% animal feed from Semi-natural agroecosystems +#' @description Calculates the share of feed N that comes from semi-natural +#' agroecosystems. +#' @param destiny_df Data frame of N data by destiny and box. +#' @return A tibble with 'Year', 'Province_name', and 'SemiNatural_feed_share'. +#' @keywords internal +#' @noRd +.calculate_natural_feed_share <- function(destiny_df) { + seminatural_feed <- destiny_df |> + dplyr::filter(Destiny == "Feed", Box == "Semi_natural_agroecosystems") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + SemiNatural_feed_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) + + total_feed <- destiny_df |> + dplyr::filter(Destiny == "Feed") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise(Total_feed_MgN = sum(MgN, na.rm = TRUE), .groups = "drop") + + seminatural_share_df <- seminatural_feed |> + dplyr::left_join(total_feed, by = c("Year", "Province_name")) |> + dplyr::mutate( + SemiNatural_feed_share = SemiNatural_feed_MgN / Total_feed_MgN + ) + + seminatural_share_df +} + +#' @title Decision: >25% animal feed from local crop --------------------------- +#' @description Calculate domestic feed supply by province. +#' @param feed_df Data frame of feed data (PIE_FullDestinies_FM). +#' @param lu_df Data frame with livestock unit totals per province and year. +#' @return A tibble with domestic feed share. +#' @keywords internal +#' @noRd +.calculate_feed_domestic_share <- function( + feed_df, + lu_df, + codes_coefs_item, + biomass_coefs +) { + # FM to DM to N + feed_df <- feed_df |> + dplyr::left_join( + codes_coefs_item |> dplyr::select(item, Name_biomass), + by = c("Item" = "item") + ) |> + dplyr::left_join( + biomass_coefs |> + dplyr::select( + Name_biomass, + Product_kgDM_kgFM, + Product_kgN_kgDM + ), + by = "Name_biomass" + ) |> + dplyr::mutate( + Value_destiny_DM = Value_destiny * Product_kgDM_kgFM, + Value_destiny_N = Value_destiny_DM * Product_kgN_kgDM + ) + + # Filter relevant feed data: Production, Exports and + # Imports destined for feed use + feed_summary <- feed_df |> + dplyr::filter( + Element %in% c("Production", "Export", "Import"), + Destiny == "Feed" + ) |> + tidyr::pivot_wider( + names_from = Element, + values_from = Value_destiny_N, + values_fill = 0 + ) + + # Calculate total Livestock Units (LU) in Spain per year + total_lu_spain <- lu_df |> + dplyr::group_by(Year) |> + dplyr::summarise( + LU_total_spain = sum( + LU_total, + na.rm = TRUE + ), + .groups = "drop" + ) + + # Calculate LU share per province + lu_with_share <- lu_df |> + dplyr::left_join(total_lu_spain, by = "Year") |> + dplyr::mutate(LU_share = LU_total / LU_total_spain) + + # Allocate Production and Import to provinces proportional to LU share + # Calculate local feed share per province: share of feed coming from domestic + feed_prov <- lu_with_share |> + dplyr::left_join(feed_summary, by = "Year") |> + dplyr::mutate( + Production_prov = LU_share * Production, + Import_prov = LU_share * Import, + Export_prov = LU_share * Export, + Net_feed_import = Import_prov - Export_prov, + local_feed_share = (Production_prov - Export_prov) / + ((Production_prov - Export_prov) + Import_prov), + ) |> + dplyr::select( + Year, + Province_name, + LU_total, + LU_share, + Production_prov, + Import_prov, + Export_prov, + Net_feed_import, + local_feed_share + ) + + feed_prov +} + + +#' @title Decision: >25% cropland N input from manure-------------------------- +#' @description Calculates the share of cropland N inputs, coming from manure. +#' @param n_input_df Data frame of N inputs. +#' @return A tibble with 'Year', 'Province_name', and 'Manure_share'. +#' @keywords internal +#' @noRd +.calculate_manure_share <- function(n_input_df) { + cropland_n_inputs <- n_input_df |> + dplyr::filter(Box == "Cropland") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + MgN_manure = sum(MgN_manure, na.rm = TRUE), + MgN_dep = sum(MgN_dep, na.rm = TRUE), + MgN_fix = sum(MgN_fix, na.rm = TRUE), + MgN_syn = sum(MgN_syn, na.rm = TRUE), + MgN_urban = sum(MgN_urban, na.rm = TRUE) + ) |> + dplyr::mutate( + MgN_total = MgN_dep + MgN_fix + MgN_syn + MgN_manure + MgN_urban, + Manure_share = MgN_manure / MgN_total + ) + + cropland_n_inputs +} + +#' @title Assign Typologies---------------------------------------------------- +#' @description Applies classifications to assign each province to one of the +#' typologies. +#' @param df A tibble containing all required indicator variables. +#' @return A tibble with 'Year', 'Province_name', and 'Typology'. +#' @keywords internal +#' @noRd +.assign_typologies <- function(df) { + df |> + dplyr::mutate( + Typology = dplyr::case_when( + Food_Consumption_MgN > Production_MgN ~ "Urban system", + Cropland_Production_MgN > 1.5 * Animal_Ingestion_MgN ~ + "Specialized stockless cropping system", + Livestock_density > 1 & + Imported_feed_share > 0.33 ~ + "Specialized livestock system", + SemiNatural_feed_share > 0.5 ~ "Grass-based crop & livestock system", + local_feed_share > 0.05 & + Manure_share > 0.05 ~ + "Forage-based crop & livestock system", + TRUE ~ "Disconnected crop & livestock system" + ) + ) |> + dplyr::select(Year, Province_name, Typology) +} + +#' @title Create map----------------------------------------------------------- +#' @description Generates a map of typologies for Spanish provinces for a +#' specified year using ggplot2 and sf. +#' @param typologies_df A tibble with province-level typologies. +#' @param shapefile_path Path to the shapefile. +#' @param map_year Year for which the map should be drawn. +#' @return A ggplot2 object displaying the typology map. +#' @keywords internal +#' @noRd +.create_typologies_map_josette <- function( + typologies_df, + shapefile_path, + map_year +) { + layer_name <- tools::file_path_sans_ext(basename(shapefile_path)) + + sf_provinces <- sf::st_read( + shapefile_path, + query = paste0( + "SELECT * FROM ", + layer_name, + " WHERE iso_a2 = 'ES'" + ) + ) + + sf_provinces <- sf_provinces |> + dplyr::mutate( + name = stringi::stri_trans_general(name, "Latin-ASCII"), + name = gsub(" ", "_", name), + name = dplyr::case_when( + name == "La_Rioja" ~ "Rioja", + name == "Alava" ~ "Araba", + name == "Lerida" ~ "Lleida", + name == "Castellon" ~ "Castello", + name == "La_Coruna" ~ "A_Coruna", + name == "Orense" ~ "Ourense", + name == "Gerona" ~ "Girona", + TRUE ~ name + ) + ) |> + dplyr::filter(!name %in% c("Las_Palmas", "Tenerife", "Illes_Balears")) + + typologies_year <- typologies_df |> + dplyr::filter(Year == map_year) |> + dplyr::filter( + !Province_name %in% + c( + "Las_Palmas", + "Tenerife", + "Illes_Balears" + ) + ) + + sf_provinces_filtered <- sf_provinces |> + dplyr::filter(name %in% typologies_year$Province_name) + + map_data <- sf_provinces_filtered |> + dplyr::rename(Province_name = name) |> + dplyr::inner_join(typologies_year, by = "Province_name") + + map_typologies_josette <- ggplot2::ggplot(map_data) + + ggplot2::geom_sf(ggplot2::aes(fill = Typology)) + + ggplot2::scale_fill_manual( + values = c( + "Urban system" = "#FF6666", + "Specialized stockless cropping system" = "#FFEB00", + "Grass-based crop & livestock system" = "#66a61e", + "Forage-based crop & livestock system" = "#FFA500", + "Disconnected crop & livestock system" = "#FFFF99", + "Specialized livestock system" = "#FF0000" + ) + ) + + ggplot2::labs(title = paste("Typologies in Spain for", map_year)) + + ggplot2::theme_minimal() + + print(map_typologies_josette) +} + +#' @title Create stacked bar plots for N inputs -------------------------------- +#' @description Stacked bar plots by province and typology, taking N deposition, +#' fixation, synthetic fertilizer, net feed import into account. +#' +#' @param typologies_df A data frame with columns Year, Province_name, Typology +#' @param n_input_df A data frame with N input values by Year and Province_name +#' @return A plot showing stacked bar plots of inputs by typology. +#' @param year The year to filter the data for plotting +#' @keywords internal +#' @noRd + +.create_bar_plots_inputs <- function( + typologies_df, + n_input_df, + imported_feed_share_df +) { + # Filter datasets to those years + typologies_df_filtered <- typologies_df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise(Typology = dplyr::first(Typology), .groups = "drop") + + n_inputs_agg <- n_input_df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Deposition = sum(MgN_dep, na.rm = TRUE), + Fixation = sum(MgN_fix, na.rm = TRUE), + Synthetic_fert = sum(MgN_syn, na.rm = TRUE), + .groups = "drop" + ) + + feed_import_agg <- imported_feed_share_df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Net_feed_import = sum(Net_feed_import, na.rm = TRUE), + .groups = "drop" + ) + + # Join all data + df_joined <- n_inputs_agg |> + dplyr::left_join(feed_import_agg, by = c("Year", "Province_name")) |> + dplyr::left_join(typologies_df_filtered, by = c("Year", "Province_name")) + + # Convert to long format + df_long <- df_joined |> + tidyr::pivot_longer( + cols = c("Deposition", "Fixation", "Synthetic_fert", "Net_feed_import"), + names_to = "N_input_type", + values_to = "N_input_value" + ) + + # Aggregate by typology and year, convert to GgN + df_plot <- df_long |> + dplyr::group_by(Year, Typology, N_input_type) |> + dplyr::summarise( + N_input_value = sum(N_input_value, na.rm = TRUE) / 1000, + .groups = "drop" + ) + + # Create stacked bar plot + year_breaks <- df_plot$Year |> + unique() |> + sort() + year_breaks <- year_breaks[year_breaks %% 20 == 0] + + df_plot$N_input_type <- factor( + df_plot$N_input_type, + levels = c( + "Synthetic_fert", + "Fixation", + "Deposition", + "Net_feed_import" + ) + ) + + p <- ggplot2::ggplot( + df_plot, + ggplot2::aes( + x = factor(Year), + y = N_input_value, + fill = N_input_type + ) + ) + + ggplot2::geom_bar(stat = "identity", position = "stack") + + ggplot2::facet_wrap(~Typology, scales = "free_y") + + ggplot2::scale_x_discrete(breaks = year_breaks) + + ggplot2::scale_fill_manual( + values = c( + "Net_feed_import" = "dodgerblue4", + "Deposition" = "gray40", + "Fixation" = "olivedrab4", + "Synthetic_fert" = "red4" + ) + ) + + ggplot2::labs( + title = "Nitrogen Inputs by Typology", + x = "Year", + y = "Nitrogen Input (GgN)", + fill = "Input Type" + ) + + ggplot2::theme_minimal() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + print(p) + df_plot + + # Additional plot: Total N inputs by typology over time----------------------- + + # Aggregate total N inputs per province and year + df_total_input_typ <- df_long |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Total_N_input = sum(N_input_value, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::left_join(typologies_df_filtered, by = c("Year", "Province_name")) |> + dplyr::group_by(Year, Typology) |> + dplyr::summarise( + Total_N_input = sum(Total_N_input, na.rm = TRUE) / 1000, + .groups = "drop" + ) + + typology_colors <- c( + "Forage-based crop & livestock system" = "#FFA500", + "Disconnected crop & livestock system" = "#FFFF99", + "Specialized stockless cropping system" = "#FFEB00", + "Specialized livestock system" = "#FF0000", + "Grass-based crop & livestock system" = "#66a61e", + "Urban system" = "#FF6666" + ) + + df_total_input_typ$Typology <- factor( + df_total_input_typ$Typology, + levels = names(typology_colors) + ) + + # Define year breaks + year_breaks_typ <- df_total_input_typ$Year |> + unique() |> + sort() + year_breaks_typ <- year_breaks_typ[year_breaks_typ %% 20 == 0] + + # Create stacked bar plot + p_typology_stack <- ggplot2::ggplot( + df_total_input_typ, + ggplot2::aes( + x = factor(Year), + y = Total_N_input, + fill = Typology + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::scale_x_discrete(breaks = year_breaks_typ) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + title = "Total Nitrogen Inputs in Spain by Typology", + x = "Year", + y = "Total Nitrogen Input (GgN)", + fill = "Typology" + ) + + ggplot2::theme_minimal() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + print(p_typology_stack) + + # Relative share of N inputs by typology (in %) ------------------------------ + + # Aggregate total N inputs per province and year + df_total_input_typ_pct <- df_long |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Total_N_input = sum(N_input_value, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::left_join(typologies_df_filtered, by = c("Year", "Province_name")) |> + dplyr::group_by(Year, Typology) |> + dplyr::summarise( + Total_N_input = sum(Total_N_input, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::group_by(Year) |> + dplyr::mutate( + Total_N_input_all = sum(Total_N_input, na.rm = TRUE), + Percent_N_input = (Total_N_input / Total_N_input_all) * 100 + ) |> + dplyr::ungroup() + + # Keep the same color mapping for typologies + df_total_input_typ_pct$Typology <- factor( + df_total_input_typ_pct$Typology, + levels = names(typology_colors) + ) + + # Define x-axis breaks (every 20 years) + year_breaks_typ_pct <- df_total_input_typ_pct$Year |> + unique() |> + sort() + year_breaks_typ_pct <- year_breaks_typ_pct[year_breaks_typ_pct %% 20 == 0] + + # Create stacked bar plot with percentages + p_typology_pct <- ggplot2::ggplot( + df_total_input_typ_pct, + ggplot2::aes( + x = factor(Year), + y = Percent_N_input, + fill = Typology + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::scale_x_discrete(breaks = year_breaks_typ_pct) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + title = "Nitrogen Inputs by Typology in Spain (%)", + x = "Year", + y = "Share of Total N Input (%)", + fill = "Typology" + ) + + ggplot2::theme_minimal() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + print(p_typology_pct) +} diff --git a/R/Typologies_Julia.R b/R/Typologies_Julia.R new file mode 100644 index 00000000..b3970f2d --- /dev/null +++ b/R/Typologies_Julia.R @@ -0,0 +1,652 @@ +#' @title Typologies of Julia +#' +#' @description +#' Typologies of provinces in Spain based on nitrogen (N) production +#' data of crops and livestock, using various input datasets and generating +#' classification maps and data frames. +#' +#' @param make_map If TRUE a map of the typologies will be created. +#' +#' @param shapefile_path Path to the shapefile used for mapping provinces. +#' +#' @param map_year The year for which the typology map is created. +#' +#' @returns +#' A tibble with the classification of Spanish provinces into typologies. +#' It contains the following columns: +#' - `year`: The year in which the classification is performed. +#' - `province_name`: The name of the Spanish province. +#' - `livestock_density`: Livestock units (LU) per hectare of agricultural area +#' (UAA). Reflects the intensity of animal farming. +#' - `productivity_kgN_ha`: Crop N productivity, in kilograms of N harvested +#' per hectare of cropland. +#' - `semi_nat_share`: Share of total feed coming from semi-natural +#' agroecosystems. Expressed between 0 and 1. +#' - `feed_imported_share`: Share of feed that is imported. Expressed between +#' 0 and 1. +#' - `typology`: Assigned typology category for each province. +#' This is based on thresholds in livestock density, crop +#' productivity, and feed patterns. The typologies include: +#' - `Specialized cropping system` +#' - `Extensive cropping system` +#' - `Extensive mixed crop-livestock system` +#' - `Intensive mixed crop-livestock system` +#' - `Specialized livestock-farming system` +#' +#' @export +create_typologies_grafs_spain <- function( + make_map = TRUE, + shapefile_path = paste0( + "C:/PhD/GRAFS/Production Boxes/", + "Final Files/Inputs/ne_10m_admin_1_states_provinces.shp" + ), + map_year = 1980 +) { + inputs_dir <- "C:/PhD/GRAFS/Production Boxes/Final Files/Inputs" + + # Load datasets + data <- .load_inputs_typologies_julia(inputs_dir, shapefile_path) + data$sf_provinces <- data$sf_provinces_spain + + data$sf_provinces$name <- stringi::stri_trans_general( + data$sf_provinces$name, + "Latin-ASCII" + ) + data$Livestock_Prod_ygps$Province_name <- stringi::stri_trans_general( + data$Livestock_Prod_ygps$Province_name, + "Latin-ASCII" + ) + data$sf_provinces$name <- gsub(" ", "_", data$sf_provinces$name) + data$sf_provinces$name[data$sf_provinces$name == "La_Rioja"] <- "Rioja" + data$sf_provinces$name[data$sf_provinces$name == "Alava"] <- "Araba" + data$sf_provinces$name[data$sf_provinces$name == "Lerida"] <- "Lleida" + data$sf_provinces$name[data$sf_provinces$name == "Castellon"] <- "Castello" + data$sf_provinces$name[data$sf_provinces$name == "La_Coruna"] <- "A_Coruna" + data$sf_provinces$name[data$sf_provinces$name == "Orense"] <- "Ourense" + data$sf_provinces$name[data$sf_provinces$name == "Gerona"] <- "Girona" + data$sf_provinces <- data$sf_provinces[ + !data$sf_provinces$name %in% + c( + "Las_Palmas", + "Tenerife" + ), + ] + + # Prepare LU coefficients with Livestock_cat mapping + lu_coefs_mapped <- .prepare_lu_coefs(data$Codes_coefs) + + # Merge livestock data with LU coefficients and calculate totals + lu_totals_detailed <- .calculate_lu_totals( + data$Livestock_Prod_ygps, + lu_coefs_mapped + ) + + # Aggregate LU_total per Year, Province + lu_aggregated <- .aggregate_lu_totals(lu_totals_detailed) + + # Aggregate Area + area_aggregated <- .aggregate_area_aa(data$NPP_ygpit) + + # Calculate livestock density + livestock_density <- .calculate_livestock_density( + lu_aggregated, + area_aggregated + ) + + # Calculate cropland productivity + cropland_productivity <- .aggregate_crop_productivity(data$NPP_ygpit) + + # Aggregate feed from semi natural agroecosystems + semi_natural_feed <- .aggregate_semi_nat_feed_mgn(data$GRAFS_Prod_Destiny_git) + + cropland_feed <- .aggregate_cropland_feed_mgn(data$GRAFS_Prod_Destiny_git) + + # Calculate feed share (semi_natural feed / total feed) + feed_share <- .calculate_semi_nat_feed_share(data$GRAFS_Prod_Destiny_git) + + # Use feed supply from GRAFS_Prod_Destiny_git + LU data + feed_domestic_prov <- .calculate_feed_domest_supply( + data$GRAFS_Prod_Destiny_git, + lu_aggregated + ) + + # Calculate feed import per province based on national imports & LU shares + feed_import_by_province <- .calculate_feed_import_share( + data$PIE_FullDestinies_FM, + lu_aggregated + ) + + # Calculate imported feed share at province level + feed_imported_share <- .calculate_imported_feed_share( + feed_import_by_province, + feed_domestic_prov + ) + + typologies_result <- .assign_decision_tree( + livestock_density, + cropland_productivity, + feed_share, + feed_imported_share, + sf_provinces = data$sf_provinces_spain, + year = map_year + ) + + if (make_map) { + typologies_df <- typologies_result$Typologies + map_plot <- typologies_result$Typologies_map + } else { + typologies_df <- typologies_result$Typologies + map_plot <- NULL + } + + typologies_df +} + +#' @title Load input datasets -------------------------------------------------- +#' +#' @param shapefile_path The local path where the input data are located. +#' @param inputs_dir Path to the input data directory. +#' +#' @keywords internal +#' @noRd +.load_inputs_typologies_julia <- function(inputs_dir, shapefile_path) { + layer_name <- tools::file_path_sans_ext(basename(shapefile_path)) + + sf_provinces_spain <- sf::st_read( + shapefile_path, + query = paste0( + "SELECT * FROM ", + layer_name, + " WHERE iso_a2 = 'ES'" + ) + ) + + list( + Livestock_Prod_ygps = readr::read_csv(file.path( + inputs_dir, + "Livestock_Prod_ygps.csv" + )), + Codes_coefs = readxl::read_excel( + file.path(inputs_dir, "Codes_coefs.xlsx"), + sheet = "Liv_LU_coefs" + ), + NPP_ygpit = readr::read_csv(file.path(inputs_dir, "NPP_ygpit.csv.gz")), + GRAFS_Prod_Destiny_git = readr::read_csv(file.path( + inputs_dir, + "GRAFS_Prod_Destiny_git.csv" + )), + PIE_FullDestinies_FM = readr::read_csv(file.path( + inputs_dir, + "PIE_FullDestinies_FM.csv" + )), + sf_provinces_spain = sf_provinces_spain + ) +} + +#' @title Mapping: Livestock_cat (from livestock data) to Animal_class +#' (from coefficients) +#' @keywords internal +#' @noRd +.create_livestockcat_mapping <- function() { + tibble::tribble( + ~Livestock_cat, ~Animal_class, + "Cattle_milk", "Dairy_cows", + "Cattle_meat", "Cattle", + "Sheep", "Sheep_goats", + "Goats", "Sheep_goats", + "Donkeys_mules", "Equines", + "Horses", "Equines", + "Pigs", "Pigs", + "Poultry", "Hens", + "Rabbits", "Rabbits" + ) +} + +#' @title Prepare LU coefficients with Livestock_cat mapping ------------------- +#' +#' @param codes_coefs_df An excel file including coefficients. +#' @keywords internal +#' @noRd +.prepare_lu_coefs <- function(codes_coefs_df) { + mapping <- .create_livestockcat_mapping() + + mapping |> + dplyr::inner_join(codes_coefs_df, by = "Animal_class") |> + dplyr::select(Livestock_cat, Animal_class, LU_head) |> + dplyr::distinct() +} + +#' @title Calculate LU_total per row ------------------------------------------- +#' +#' @param livestock_df A data frame containing livestock data. +#' @param lu_coefs_df A data frame with livestock unit coefficients. +#' +#' @return A tibble with columns 'Year', 'Province_name', 'Livestock_cat', +#' 'Animal_class', Stock_Number', 'LU_head', and 'LU_total'. +#' @keywords internal +#' @noRd +.calculate_lu_totals <- function(livestock_df, lu_coefs_df) { + livestock_df |> + dplyr::select(Year, Province_name, Livestock_cat, Stock_Number) |> + dplyr::left_join(lu_coefs_df, by = "Livestock_cat") |> + dplyr::mutate(LU_total = Stock_Number * LU_head) |> + dplyr::select( + Year, + Province_name, + Livestock_cat, + Animal_class, + Stock_Number, + LU_head, + LU_total + ) |> + dplyr::distinct() +} + +#' @title Aggregate LU_total --------------------------------------------------- +#' @description Aggregates total land use (LU_total) by year and province. +#' +#' @param lu_detailed_df A data frame containing columns 'Year', +#' 'Province_name', and 'LU_total'. +#' +#' @return A tibble with total land use summed for each year and province, +#' sorted by year and province. +#' @keywords internal +#' @noRd +.aggregate_lu_totals <- function(lu_detailed_df) { + lu_aggregated <- lu_detailed_df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + LU_total = sum( + LU_total, + na.rm = TRUE + ), + .groups = "drop" + ) |> + dplyr::arrange(Year, Province_name) + + lu_aggregated +} + +#' @title Aggregate Area AA ---------------------------------------------------- +#' @description Aggregates the area (Area_ygpit_ha) by year and province. +#' +#' @param npp_df A data frame containing the columns 'Year', 'Province_name', +#' and 'Area_ygpit_ha'. +#' +#' @return A tibble with the sum of areas per year and province. +#' @keywords internal +#' @noRd +.aggregate_area_aa <- function(npp_df) { + npp_df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Area_ha = sum( + Area_ygpit_ha, + na.rm = TRUE + ), + .groups = "drop" + ) +} + +#' @title Calculate livestock density ------------------------------------------ +#' +#' @param lu_totals_df A data frame containing livestock total data. +#' @param area_df A data frame containing area information. +#' +#' @return A tibble with columns 'Year', 'Province_name', 'LU_total', 'Area_ha', +#' and 'Livestock_density' (LU_total divided by Area_ha). +#' @keywords internal +#' @noRd +.calculate_livestock_density <- function(lu_totals_df, area_df) { + lu_totals_df |> + dplyr::left_join(area_df, by = c("Year", "Province_name")) |> + dplyr::mutate(Livestock_density = LU_total / Area_ha) |> + dplyr::select(Year, Province_name, LU_total, Area_ha, Livestock_density) |> + dplyr::arrange(Year, Province_name) +} + +#' @title Aggregate Productivity for Cropland ---------------------------------- +#' +#' @param npp_df A data frame containing columns 'Year', 'Province_name', +#' 'LandUse','Prod_MgN', and 'Area_ygpit_ha'. +#' +#' @return A tibble grouped by year and province with total production, +#' total cropland area, and productivity in kg N per hectare. +#' @keywords internal +#' @noRd +.aggregate_crop_productivity <- function(npp_df) { + cropland_prod <- npp_df |> + dplyr::filter(LandUse == "Cropland") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Prod_MgN_total = sum(Prod_MgN, na.rm = TRUE), + Area_ha_cropland = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + Productivity_kgN_ha = Prod_MgN_total / Area_ha_cropland * 1000 + ) |> + dplyr::arrange(Year, Province_name) + + cropland_prod +} + +#' @title Aggregate Feed from Semi natural agroecosystems for Grassland > 60% +#' of Livestock intake from Grassland +#' +#' @param df A data frame containing columns'Year','Province_name','Box', +#'' Destiny', and'MgN'. +#' +#' @return A tibble grouped by year and province with the total feed nitrogen +#' (MgN) from semi-natural agroecosystems. +#' @keywords internal +#' @noRd +.aggregate_semi_nat_feed_mgn <- function(df) { + df |> + dplyr::filter(Box == "Semi_natural_agroecosystems", Destiny == "Feed") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Semi_nat_feed_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) +} + +#' @title Aggregate Feed from Cropland ---------------------------------------- +#' +#' @param df A data frame containing columns 'Year', 'Province_name', +#' 'Box', 'Destiny', and 'MgN'. +#' +#' @return A tibble grouped by year and province with the total cropland feed +#' nitrogen (MgN). +#' @keywords internal +#' @noRd +.aggregate_cropland_feed_mgn <- function(df) { + df |> + dplyr::filter(Box == "Cropland", Destiny == "Feed") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Cropland_feed_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) +} + +#' Aggregate total feed from all boxes (Feed destiny) ------------------------- +#' +#' @param df A data frame containing nitrogen data with columns including +#' 'Year', 'Province_name', 'Destiny', 'Box', and 'MgN'. +#' +#' @return A tibble with total feed nitrogen (MgN) summed by year and province. +#' @keywords internal +#' @noRd +.aggregate_total_feed_mgn <- function(df) { + df |> + dplyr::filter( + Destiny == "Feed", + Box %in% c("Semi_natural_agroecosystems", "Cropland") + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Total_feed_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) |> + dplyr::arrange(Year, Province_name) +} + +#' @title Calculate Feed share (between semi natural agroecosystems and total +#' feed) +#' +#' @param df A data frame containing nitrogen data with columns including +#' 'Year', 'Province_name', 'Destiny', 'Box', and 'MgN'. +#' +#' @return A data frame including the share of feed from semi natural +#' agroecosystems +#' @keywords internal +#' @noRd +.calculate_semi_nat_feed_share <- function(df) { + total_feed <- .aggregate_total_feed_mgn(df) + semi_nat_feed <- .aggregate_semi_nat_feed_mgn(df) + + dplyr::left_join( + total_feed, + semi_nat_feed, + by = c("Year", "Province_name") + ) |> + dplyr::mutate( + Semi_nat_feed_MgN = ifelse( + is.na(Semi_nat_feed_MgN), + 0, + Semi_nat_feed_MgN + ), + Semi_nat_share = Semi_nat_feed_MgN / Total_feed_MgN + ) |> + dplyr::arrange(Year, Province_name) +} + +#' @title Calculate feed domestic supply --------------------------------------- +#' +#' @param grafs_df A data frame containing GRAFS data with the columns Destiny', +#' 'Year', 'Province_name', and 'MgN'. +#' @param lu_df A data frame with land use data. +#' +#' @return A tibble with columns 'Year', 'Province_name', and +#' 'Domestic_feed_MgN' representing the total domestic feed supply in MgN. +#' @keywords internal +#' @noRd +.calculate_feed_domest_supply <- function(grafs_df, lu_df) { + domestic_feed <- grafs_df |> + dplyr::filter(Destiny == "Feed") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Domestic_feed_MgN = sum( + MgN, + na.rm = TRUE + ), + .groups = "drop" + ) + + # Add LU_total for use in further steps + domestic_feed |> + dplyr::left_join( + lu_df |> dplyr::select(Year, Province_name, LU_total), + by = c("Year", "Province_name") + ) +} + +#' @title Calculate feed import per province ----------------------------------- +#' +#' @param feed_df A data frame containing feed data. +#' @param lu_df A data frame with land use information. +#' +#' @return A tibble with columns 'Year', 'Province_name', 'LU_total', +#' 'LU_share', and 'Feed_import_MgN', where 'Feed_import_MgN' is the estimated +#' feed import allocated to each province. +#' @keywords internal +#' @noRd +.calculate_feed_import_share <- function(feed_df, lu_df) { + feed_filtered <- feed_df |> + dplyr::filter(Element == "Import", Destiny == "Feed") |> + dplyr::group_by(Year) |> + dplyr::summarise( + Total_feed_import = sum( + Value_destiny, + na.rm = TRUE + ), + .groups = "drop" + ) + + total_lu_spain <- lu_df |> + dplyr::group_by(Year) |> + dplyr::summarise( + LU_total_spain = sum( + LU_total, + na.rm = TRUE + ), + .groups = "drop" + ) + + lu_with_share <- lu_df |> + dplyr::left_join(total_lu_spain, by = "Year") |> + dplyr::mutate(LU_share = LU_total / LU_total_spain) + + feed_import_by_province <- lu_with_share |> + dplyr::left_join(feed_filtered, by = "Year") |> + dplyr::mutate(Feed_import_MgN = LU_share * Total_feed_import) |> + dplyr::select(Year, Province_name, LU_total, LU_share, Feed_import_MgN) + + feed_import_by_province +} + +#' @title Calculate feed share of imported/consumed feed ----------------------- +#' +#' @param feed_import_by_province A data frame containing imported feed data. +#' @param domestic_feed_by_province A data frame containing domestic feed data. +#' +#' @return A data frame with the imported feed share. +#' @keywords internal +#' @noRd +.calculate_imported_feed_share <- function( + feed_import_by_province, + domestic_feed_by_province +) { + feed_import_by_province |> + dplyr::left_join( + domestic_feed_by_province, + by = c("Year", "Province_name") + ) |> + dplyr::mutate( + Total_feed_MgN = Domestic_feed_MgN + Feed_import_MgN, + Imported_feed_share = Feed_import_MgN / Total_feed_MgN, + Imported_feed_share = ifelse( + is.nan(Imported_feed_share), + NA, + Imported_feed_share + ) + ) |> + dplyr::select( + Year, + Province_name, + LU_total = LU_total, + Feed_import_MgN, + Domestic_feed_MgN, + Total_feed_MgN, + Imported_feed_share + ) +} + +#' @title Assign Typologies and optionally plot map---------------------------- +#' +#' @param livestock_density A data frame with livestock density values. +#' @param productivity A data frame with productivity (kgN/ha) values. +#' @param semi_nat_share A data frame with semi-natural agroecosystem share. +#' @param imported_feed_share A data frame with share of imported feed. +#' @param sf_provinces An sf object with province geometries. +#' @param year Integer specifying the year for which typologies are assigned. +#' +#' @return A tibble with province names and their assigned farming system +#' typology for the specified year. +#' @keywords internal +#' @noRd +.assign_decision_tree <- function( + livestock_density, + productivity, + semi_nat_share, + imported_feed_share, + sf_provinces, + year +) { + typologies <- livestock_density |> + dplyr::inner_join(productivity, by = c("Year", "Province_name")) |> + dplyr::inner_join(semi_nat_share, by = c("Year", "Province_name")) |> + dplyr::inner_join(imported_feed_share, by = c("Year", "Province_name")) |> + dplyr::filter(Year == year) |> + dplyr::mutate( + Typologie = dplyr::case_when( + Livestock_density < 0.4 & + Productivity_kgN_ha > 60 ~ + "Specialized cropping system", + Livestock_density < 0.4 & + Productivity_kgN_ha <= 60 ~ + "Extensive cropping system", + Livestock_density >= 0.4 & + Semi_nat_share > 0.6 ~ + "Extensive mixed crop-livestock system", + Livestock_density >= 0.4 & + Semi_nat_share <= 0.6 & + Imported_feed_share < 0.5 ~ + "Intensive mixed crop-livestock system", + Livestock_density >= 0.4 & + Semi_nat_share <= 0.6 & + Imported_feed_share >= 0.5 ~ + "Specialized livestock-farming system", + TRUE ~ NA_character_ + ) + ) |> + dplyr::select(Province_name, Typologie) + + sf_provinces_filtered <- sf_provinces |> + dplyr::rename(Province_name = name) |> + dplyr::inner_join(typologies, by = "Province_name") + + map <- ggplot2::ggplot(sf_provinces_filtered) + + ggplot2::geom_sf(ggplot2::aes(fill = Typologie), color = "white") + + ggplot2::scale_fill_manual( + values = c( + "Specialized cropping system" = "#FFD700", + "Extensive cropping system" = "#FFFF99", + "Extensive mixed crop-livestock system" = "#66a61e", + "Intensive mixed crop-livestock system" = "#d95f02", + "Specialized livestock-farming system" = "#7570b3" + ), + na.value = "grey80" + ) + + ggplot2::labs( + title = paste("Typologies by Province -", year), + fill = "Typologie" + ) + + ggplot2::theme_minimal() + + # Typologies for each year as a dataset + typologies_all_years <- livestock_density |> + dplyr::inner_join(productivity, by = c("Year", "Province_name")) |> + dplyr::inner_join(semi_nat_share, by = c("Year", "Province_name")) |> + dplyr::inner_join(imported_feed_share, by = c("Year", "Province_name")) |> + dplyr::mutate( + Typologie = dplyr::case_when( + Livestock_density < 0.4 & + Productivity_kgN_ha > 60 ~ + "Specialized cropping system", + Livestock_density < 0.4 & + Productivity_kgN_ha <= 60 ~ + "Extensive cropping system", + Livestock_density >= 0.4 & + Semi_nat_share > 0.6 ~ + "Extensive mixed crop-livestock system", + Livestock_density >= 0.4 & + Semi_nat_share <= 0.6 & + Imported_feed_share < 0.5 ~ + "Intensive mixed crop-livestock system", + Livestock_density >= 0.4 & + Semi_nat_share <= 0.6 & + Imported_feed_share >= 0.5 ~ + "Specialized livestock-farming system", + TRUE ~ NA_character_ + ) + ) |> + dplyr::select(Year, Province_name, Typologie) |> + dplyr::arrange(Year, Province_name) + + list( + Typologies = typologies, + Typologies_map = map, + Typologies_all_years = typologies_all_years + ) +} diff --git a/R/alfredo_typologies.R b/R/alfredo_typologies.R new file mode 100644 index 00000000..6cdeaa0b --- /dev/null +++ b/R/alfredo_typologies.R @@ -0,0 +1,196 @@ +#' @title Alfredo's typology classification +#' +#' @description Calculates typologies for provinces based on grassland, +#' fertilizer, imported feed, and woody/herbaceous shares. +#' +#' @param soil_inputs A data frame containing soil nitrogen inputs. +#' @param prod_destiny A data frame containing production and destiny data. +#' @param years Years between 1860 and 2020. +#' +#' @return A data frame with the columns: Year, Province_name, grass_N, +#' fertilizer_N, feed_import_N, woody, herbaceous, woody_share, and Category. +#' +#' @export +create_alfredos_typologies <- function( + soil_inputs = NULL, + prod_destiny = NULL, + years = 1860:2020 +) { + if (is.null(soil_inputs)) { + soil_inputs <- create_n_soil_inputs() + } + + if (is.null(prod_destiny)) { + prod_destiny <- create_n_prov_destiny() + } + + prod_destiny_mean <- prod_destiny |> + dplyr::filter(year %in% years) |> + dplyr::group_by(year, province_name, box, item, box_destiny, destiny) |> + dplyr::summarise(mg_n = mean(mg_n, na.rm = TRUE), .groups = "drop") + + # Grassland N + grassland <- prod_destiny_mean |> + dplyr::filter( + box == "Semi_natural_agroecosystems", + item == "Grassland", + box_destiny %in% c("semi_natural_to_livestock", "semi_natural_export") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise(grass_N = sum(mg_n, na.rm = TRUE), .groups = "drop") + + # Fertiliser N + fertiliser <- soil_inputs |> + dplyr::filter(year %in% years) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + fertiliser_N = sum(synthetic, na.rm = TRUE), + .groups = "drop" + ) + + # Feed and Food + Other_uses share + destiny_shares <- prod_destiny_mean |> + dplyr::filter(destiny %in% c("feed", "food", "other_uses")) |> + dplyr::group_by(year, province_name, item) |> + dplyr::summarise( + total = sum(mg_n, na.rm = TRUE), + feed_share = sum(mg_n[destiny == "feed"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + human_share = sum( + mg_n[destiny %in% c("food", "other_uses")], + na.rm = TRUE + ) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # Feed-import + feed_imports <- prod_destiny_mean |> + dplyr::filter( + destiny == "import", + box %in% c("Cropland", "Semi_natural_agroecosystems") + ) |> + dplyr::left_join(destiny_shares, by = c("year", "province_name", "item")) |> + dplyr::mutate(feed_import_N = mg_n * feed_share) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + feed_import_N = sum(feed_import_N, na.rm = TRUE), + .groups = "drop" + ) + + # Synthetic woody + woody_share <- prod_destiny_mean |> + dplyr::filter( + box %in% c("Cropland", "Semi_natural_agroecosystems"), + item %in% c("Firewood", "Acorns", "Grassland") + ) |> + dplyr::left_join( + prod_destiny_mean |> + dplyr::filter(destiny == "import") |> + dplyr::group_by(year, province_name, item) |> + dplyr::summarise( + imported_mg_n = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ), + by = c("year", "province_name", "item") + ) |> + dplyr::mutate( + local_mg_n = mg_n - dplyr::coalesce(imported_mg_n, 0), + local_mg_n = ifelse(local_mg_n < 0, 0, local_mg_n), + woody = ifelse(item %in% c("Firewood", "Acorns"), local_mg_n, 0), + herbaceous = ifelse( + item == "Grassland" & box == "Semi_natural_agroecosystems", + local_mg_n, + 0 + ) + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + woody = sum(woody, na.rm = TRUE), + herbaceous = sum(herbaceous, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + woody_share = ifelse( + woody + herbaceous > 0, + woody / (woody + herbaceous), + 0 + ) + ) + + alfredos_typologies <- grassland |> + dplyr::left_join(fertiliser, by = c("year", "province_name")) |> + dplyr::left_join(feed_imports, by = c("year", "province_name")) |> + dplyr::left_join(woody_share, by = c("year", "province_name")) |> + dplyr::mutate( + Category = dplyr::case_when( + grass_N > fertiliser_N & grass_N > feed_import_N ~ "Grassland", + fertiliser_N > feed_import_N & woody_share >= 0.5 ~ "Synthetic woody", + fertiliser_N > feed_import_N & woody_share < 0.5 ~ + "Synthetic herbaceous", + TRUE ~ "Imported feed" + ) + ) + + alfredos_typologies +} + +#' @title Plot of Alfredo's typology classification +#' +#' @description Generates a plot of province typologies over time based on +#' Alfredo's typology classification. +#' +#' @param alfredos_typologies A data frame returned by +#' `create_alfredos_typologies()`. +#' +#' @return A plot showing province typology evolution from 1860 to 2020. +#' +#' @keywords internal +#' @noRd +.plot_province_typologies <- function(alfredos_typologies) { + alfredos_typologies <- alfredos_typologies |> + dplyr::mutate( + fertiliser_N = tidyr::replace_na(fertiliser_N, 0), + feed_import_N = tidyr::replace_na(feed_import_N, 0), + woody_share = tidyr::replace_na(woody_share, 0), + province_name = factor( + province_name, + levels = sort(unique(province_name), decreasing = TRUE) + ) + ) + + typology_colors <- c( + "Grassland" = "#2E8B57", + "Synthetic woody" = "#8b4513", + "Synthetic herbaceous" = "#9bb8e6", + "Imported feed" = "#F2D16B" + ) + + ggplot2::ggplot( + alfredos_typologies, + ggplot2::aes(x = year, y = province_name, fill = Category) + ) + + ggplot2::geom_tile() + + ggplot2::scale_x_continuous( + breaks = seq( + min(alfredos_typologies$year), + max(alfredos_typologies$year), + by = 20 + ), + expand = c(0, 0) + ) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + x = "Year", + y = "Province", + fill = "Typology", + title = paste0( + "Province type classification evolution (1860-2020)" + ) + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5, hjust = 1), + axis.text.y = ggplot2::element_text(size = 8) + ) +} diff --git a/R/decomposition_analysis.R b/R/decomposition_analysis.R new file mode 100644 index 00000000..b54aebd8 --- /dev/null +++ b/R/decomposition_analysis.R @@ -0,0 +1,125 @@ +prepare_lmdi_dataset <- function() { + grafs_data <- create_n_nat_destiny() + population_data <- whep_read_file("population_yg") + + df_veg <- grafs_data |> + dplyr::filter( + origin %in% + c("Cropland", "semi_natural_agroecosystems") | + origin %in% + c("Deposition", "Fixation", "Synthetic", "Livestock", "People") + ) + + outputs <- df_veg |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_mono", + "livestock_rum", + "export" + ), + origin %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(outputs = sum(mg_n, na.rm = TRUE), .groups = "drop") + + inputs <- grafs_data |> + dplyr::filter( + origin %in% + c("Deposition", "Fixation", "Synthetic", "Livestock", "People"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(inputs = sum(mg_n, na.rm = TRUE), .groups = "drop") + + surplus_df <- inputs |> + dplyr::left_join(outputs, by = "year") |> + dplyr::mutate( + outputs = dplyr::coalesce(outputs, 0), + surplus = inputs - outputs + ) + + food_df <- df_veg |> + dplyr::filter( + destiny == "population_food", + origin %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(food = sum(mg_n, na.rm = TRUE), .groups = "drop") + + pop_df <- population_data |> + dplyr::rename_with(tolower) |> + dplyr::group_by(year) |> + dplyr::summarise( + population = sum(pop_mpeop_yg, na.rm = TRUE), + .groups = "drop" + ) + + surplus_df |> + dplyr::left_join(food_df, by = "year") |> + dplyr::left_join(pop_df, by = "year") |> + dplyr::mutate( + food = dplyr::coalesce(food, 0), + population = dplyr::coalesce(population, 0), + A = dplyr::if_else(population > 0, food / population, NA_real_), + t_ratio = dplyr::if_else(food > 0, surplus / food, NA_real_) + ) |> + dplyr::select(year, surplus, population, food, A, t_ratio) |> + dplyr::arrange(year) +} + +prepare_lmdi_production_area <- function() { + n_data <- create_n_nat_destiny() + + surplus <- n_data |> + dplyr::group_by(year) |> + dplyr::summarise( + surplus = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + production <- n_data |> + dplyr::filter( + origin %in% c("Cropland", "semi_natural_agroecosystems"), + destiny %in% + c( + "livestock_mono", + "livestock_rum", + "population_food", + "population_other_uses", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + production = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + area <- whep_read_file("crop_area_npp_ygpitr_no_fallow") |> + dplyr::rename_with(tolower) |> + dplyr::group_by(year) |> + dplyr::summarise( + area = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + df <- surplus |> + dplyr::left_join(production, by = "year") |> + dplyr::left_join(area, by = "year") |> + dplyr::mutate( + yield = production / area, + intensity = surplus / production + ) |> + dplyr::select( + year, + surplus, + area, + yield, + intensity + ) + + df +} diff --git a/R/grafs_plot_df.R b/R/grafs_plot_df.R new file mode 100644 index 00000000..a968e76e --- /dev/null +++ b/R/grafs_plot_df.R @@ -0,0 +1,1480 @@ +#' @title Create GRAFS plot dataset +#' +#' @description +#' Combines land input data and N flows from crops, livestock, imports, and +#' exports to generate a dataset of nitrogen (mg_n) by province and year, to +#' create a GRAFS plot, offered by Alfredo Rodríguez. +#' +#' @return +#' A tibble containing province, year, label, data, and alignment. +#' +#' @export +create_grafs_plot_df <- function() { + prov_destiny_df <- create_n_prov_destiny() + nat_destiny_df <- create_n_nat_destiny() + + nat_destiny_df <- nat_destiny_df |> + dplyr::mutate(province_name = "Spain") + + prov_destiny_df <- prov_destiny_df |> + dplyr::filter(province_name != "Spain") |> + dplyr::bind_rows(nat_destiny_df) + + n_balance <- whep_read_file("n_balance_ygpit_all") |> + dplyr::rename_with(tolower) + + df_land <- .create_land_df() + df_flow <- .create_n_flow_df(prov_destiny_df) + df_import <- .create_n_import_df(prov_destiny_df) + df_lu <- .create_livestock_lu_df() + df_population <- .create_population_df() + df_n_input <- .create_n_input_df(n_balance) + df_land_surplus <- .create_land_surplus_df(prov_destiny_df) + df_livestock <- .create_livestock_df(prov_destiny_df) + df_lv_r_m <- .create_feed_df(prov_destiny_df) + df_crop_losses <- .create_crop_losses_df(n_balance, prov_destiny_df) + df_animal_losses <- .create_animal_losses_df(prov_destiny_df) + df_livestock_export <- .create_livestock_export_df(prov_destiny_df) + df_milk <- .create_milk_df(prov_destiny_df) + df_livestock_total <- .create_livestock_total_df(prov_destiny_df) + + df_crplndtot <- df_flow |> + dplyr::filter( + province != "Spain", + label %in% + c( + "{CROP_EXPORT}", + "{CROPS_TO_POP}", + "{CROPS_TO_LIVESTOCK}" + ) + ) |> + dplyr::mutate(data = suppressWarnings(as.numeric(data))) |> + dplyr::group_by(province, year) |> + dplyr::summarise( + data = sum(data, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + label = "{CRPLNDTOTN}", + align = "R" + ) + + df_crplndtot_spain <- df_flow |> + dplyr::filter( + province == "Spain", + label %in% c("{CROP_EXPORT}", "{CROPS_TO_POP}", "{CROPS_TO_LIVESTOCK}") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(data = sum(data, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate(province = "Spain", label = "{CRPLNDTOTN}", align = "R") + + df_crplndtot <- dplyr::bind_rows( + df_crplndtot, + df_crplndtot_spain + ) + + df_all_flows <- dplyr::bind_rows( + df_flow, + df_import, + df_livestock, + df_milk, + df_livestock_export, + df_lv_r_m, + df_crop_losses, + df_animal_losses + ) + + df_livestock_surplus <- .create_livestock_surplus_df(df_all_flows) + df_wastewater_surplus <- .create_wastewater_surplus_df( + df_all_flows, + prov_destiny_df + ) + + df_combined <- .combine_and_finalize_df( + crop_livestock_flows = df_flow, + df_livestock = dplyr::bind_rows(df_livestock, df_milk, df_livestock_export), + df_lv_r_m = df_lv_r_m, + df_crop_losses = df_crop_losses, + df_animal_losses = df_animal_losses, + df_livestock_total = df_livestock_total, + df_livestock_surplus = df_livestock_surplus, + df_land_surplus = df_land_surplus + ) + + df_final <- dplyr::bind_rows( + df_land |> dplyr::mutate(data = as.character(data)), + df_combined, + df_import |> dplyr::mutate(data = as.character(data)), + df_lu |> dplyr::mutate(data = as.character(data)), + df_n_input |> dplyr::mutate(data = as.character(data)), + df_population |> dplyr::mutate(data = as.character(data)), + df_crplndtot |> dplyr::mutate(data = as.character(data)), + df_wastewater_surplus |> dplyr::mutate(data = as.character(data)) + ) |> + dplyr::arrange(province, year, label) |> + dplyr::filter(!is.na(province) & !is.na(year)) |> + dplyr::mutate(arrowColor = "") |> + dplyr::select(province, year, label, data, align, arrowColor) + + n_labels <- c( + "{IMANOT}", + "{IMANOTR}", + "{IMANOTM}", + "{IMPHUMANMEAT}", + "{IMPHUMANEGGS}", + "{IMPHUMFISH}", + "{IMPHUMMILK}", + "{IMPORT_ANIMALCR}", + "{IMPORT_ANIMALCR_RUM}", + "{IMPORT_ANIMALCR_MONOG}", + "{CROP_POPIMPORT}", + "{IMPHMANA}", + "{CROP_EXPORT}", + "{CROPS_TO_POP}", + "{CROPS_TO_LIVESTOCK}", + "{LIVESTOCK_TO_HUMAN}", + "{GRASS_TO_LIVESTOCK}", + "{RCRTOLVSTCK_R}", + "{MCRTOLVSTCK_M}", + "{CRP_OTHUSES}", + "{AN_LS}", + "{AN_OTH}", + "{AN_LS_OTH}", + "{LV_EDBL}", + "{LVSTCK_NOEDIBLE}", + "{LVST_MILK}", + "{LIVESTOCK_EXPORTED}", + "{LVSTCKTOTN}", + "{OXDEPCROPS}", + "{FIXCR}", + "{LIVESTOCK_TO_CROPS}", + "{LIVESTOCK_TO_GRASS}", + "{OXDEPGRASS}", + "{SYF_GRASS}", + "{SYNTHF_TOTAL}", + "{SYNTHF}", + "{FIXGR}", + "{FIX_DEP_GRASS}", + "{FIX_DEP_CR}", + "{CROP_SURPLUS}", + "{GRASS_SURPLUS}", + "{LIVGASLOSS}", + "{WASTEWATER}", + "{ORGOT}", + "{CRPLNDTOTN}", + "{GREHN}", + "{FORN}", + "{PERiN}", + "{PERrN}", + "{HORiN}", + "{HORrN}", + "{NPEiN}", + "{NPErN}", + "{ARAiN}", + "{ARArN}" + ) + + df_final <- df_final |> + dplyr::mutate( + data = suppressWarnings( + ifelse( + label %in% n_labels, + as.numeric(data) / 1000, + as.numeric(data) + ) + ), + data = as.character(data) + ) + + missing_labels <- c( + "{FORha}", + "{FORMha}", + "{FORN}", + "{HAGRASS}", + "{HACULT}", + "{PERiN}", + "{PERrN}", + "{NPEiN}", + "{NPErN}", + "{GREHN}", + "{GREHMha}", + "{POPULATIONM}", + "{PERiMha}", + "{PERrMha}", + "{NPEiMha}", + "{NPErMha}", + "{RUMIANTSMLU}", + "{MONOGMLU}", + "{GRASSMha}", + "{HORiN}", + "{HORrN}", + "{ARAiN}", + "{ARArN}", + "{KM2_PROVINCE}", + "{AN_LS}", + "{AN_OTH}", + "{AN_LS_OTH}" + ) + + df_missing_spain <- df_final |> + dplyr::filter(province != "Spain") |> + dplyr::filter(label %in% missing_labels) |> + dplyr::mutate(data = as.numeric(data)) |> + dplyr::group_by(year, label) |> + dplyr::summarise(data = sum(data, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + province = "Spain", + align = "R", + data = as.character(data) + ) + + df_final <- dplyr::bind_rows( + df_final, + df_missing_spain + ) + + non_additive_labels <- c( + "{YEAR}", + "{PROVINCE_NAME}", + "{WIDTH_MAX}", + "{FORha}", + "{FORMha}", + "{HAGRASS}", + "{HACULT}", + "{GREHha}", + "{GREHMha}", + "{PERiha}", + "{PERrha}", + "{HORiha}", + "{HORrha}", + "{NPEiha}", + "{NPErha}", + "{ARAiha}", + "{ARArha}", + "{PERiMha}", + "{PERrMha}", + "{HORiMha}", + "{HORrMha}", + "{NPEiMha}", + "{NPErMha}", + "{ARAiMha}", + "{ARArMha}", + "{RUMIANTSLU}", + "{RUMIANTSMLU}", + "{MONOGLU}", + "{MONOGMLU}", + "{POPULATIONM}", + "{KM2_PROVINCE}" + ) + + df_final <- df_final |> + dplyr::mutate(data_num = suppressWarnings(as.numeric(data))) |> + dplyr::group_by(province, year, label) |> + dplyr::summarise( + data = dplyr::case_when( + label[1] %in% non_additive_labels & any(!is.na(data_num)) ~ + as.character(dplyr::first(data_num[!is.na(data_num)])), + label[1] %in% non_additive_labels ~ dplyr::first(data), + all(is.na(data_num)) ~ dplyr::first(data), + TRUE ~ as.character(sum(data_num, na.rm = TRUE)) + ), + align = dplyr::first(align), + arrowColor = "", + .groups = "drop" + ) + + #Path needs to be adjusted by user, until the final version can be uploaded + #to SACO + readr::write_csv( + df_final, + "C:/PhD/GRAFS_plot/inst/extdata/GRAFS_spain_data.csv" + ) + + df_final +} + +#' @title Create nitrogen import dataset by province +#' +#' @description +#' Generates a dataset of nitrogen flows (mg_n) by province and year, including +#' N soil inputs, production data, imports. +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return +#' A tibble with columns `province`, `year`, `label`, `data`, and `align`. +#' +#' @keywords internal +.create_n_import_df <- function(prov_destiny_df = NULL) { + if (is.null(prov_destiny_df)) { + prov_destiny_df <- create_n_prov_destiny() + } + + item_box_lookup <- whep_read_file("codes_coefs_items_full") |> + dplyr::select(item, group) |> + dplyr::filter(!is.na(item), !is.na(group)) |> + dplyr::distinct() |> + dplyr::group_by(item) |> + dplyr::summarise(group = dplyr::first(group), .groups = "drop") + + df_n_flows <- prov_destiny_df |> + dplyr::left_join(item_box_lookup, by = c("item" = "item")) |> + dplyr::mutate( + box_filled = dplyr::case_when( + item %in% c("Holm oak", "Average wood") ~ + "semi_natural_agroecosystems", + item == "Fallow" ~ "Cropland", + group %in% c("Crop products", "Primary crops", "crop residue") ~ + "Cropland", + group %in% c("Livestock products", "Livestock") ~ "Livestock", + group %in% c("Agro-industry", "Fish") ~ group, + !is.na(box) ~ box, + TRUE ~ NA_character_ + ) + ) |> + dplyr::mutate( + label = dplyr::case_when( + box_filled == "Agro-industry" & + origin == "Outside" & + destiny == "livestock_rum" ~ + "{IMANOTR}", + box_filled == "Agro-industry" & + origin == "Outside" & + destiny == "livestock_mono" ~ + "{IMANOTM}", + item %in% + c( + "Milk - Excluding Butter", + "Milk, lactation", + "Whey", + "Butter, Ghee" + ) & + origin == "Outside" & + destiny %in% c("population_food", "population_other_uses") ~ + "{IMPHUMMILK}", + item %in% + c( + "Bovine Meat", + "Mutton & Goat Meat", + "Pigmeat", + "Poultry Meat", + "Meat, Other", + "Offals, Edible" + ) & + origin == "Outside" & + destiny %in% c("population_food", "population_other_uses") ~ + "{IMPHUMANMEAT}", + item == "Eggs" & + origin == "Outside" & + destiny %in% c("population_food", "population_other_uses") ~ + "{IMPHUMANEGGS}", + box_filled == "Fish" & + origin == "Outside" & + destiny %in% c("population_food", "population_other_uses") ~ + "{IMPHUMFISH}", + box_filled != "Agro-industry" & + origin == "Outside" & + destiny == "livestock_rum" ~ + "{IMPORT_ANIMALCR_RUM}", + box_filled != "Agro-industry" & + origin == "Outside" & + destiny == "livestock_mono" ~ + "{IMPORT_ANIMALCR_MONOG}", + origin == "Livestock" & + destiny == "Cropland" ~ + "{LIVESTOCK_TO_CROPS}", + origin == "Livestock" & + destiny == "semi_natural_agroecosystems" ~ + "{LIVESTOCK_TO_GRASS}", + origin == "Deposition" & + destiny == "Cropland" ~ + "{OXDEPCROPS}", + origin == "Fixation" & + destiny == "Cropland" ~ + "{FIXCR}", + origin == "Synthetic" & + destiny == "Cropland" ~ + "{SYNTHF}", + origin == "Deposition" & + destiny == "semi_natural_agroecosystems" ~ + "{OXDEPGRASS}", + origin == "Fixation" & + destiny == "semi_natural_agroecosystems" ~ + "{FIXGR}", + origin == "Synthetic" & + destiny == "semi_natural_agroecosystems" ~ + "{SYF_GRASS}", + TRUE ~ NA_character_ + ) + ) |> + dplyr::filter(!is.na(label)) |> + dplyr::group_by(province_name, year, label) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") + + df_crop_popimport <- prov_destiny_df |> + dplyr::filter( + box == "Cropland", + origin == "Outside", + destiny %in% c("population_food", "population_other_uses") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = "{CROP_POPIMPORT}", + province = province_name, + year = year, + align = "R" + ) |> + dplyr::select(province, year, label, data, align) + + df_fix_dep_cr <- df_n_flows |> + dplyr::filter(label %in% c("{OXDEPCROPS}", "{FIXCR}")) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(data), .groups = "drop") |> + dplyr::mutate(label = "{FIX_DEP_CR}") + + df_fix_dep_grass <- df_n_flows |> + dplyr::filter(label %in% c("{OXDEPGRASS}", "{FIXGR}")) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(data), .groups = "drop") |> + dplyr::mutate(label = "{FIX_DEP_GRASS}") + + df_import_animalcr <- df_n_flows |> + dplyr::filter( + label %in% c("{IMPORT_ANIMALCR_RUM}", "{IMPORT_ANIMALCR_MONOG}") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(data), .groups = "drop") |> + dplyr::mutate(label = "{IMPORT_ANIMALCR}") + + df_synth_total <- df_n_flows |> + dplyr::filter(label == "{SYNTHF}") |> + dplyr::mutate(label = "{SYNTHF_TOTAL}") + + df_imanot <- df_n_flows |> + dplyr::filter(label %in% c("{IMANOTR}", "{IMANOTM}")) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(data), .groups = "drop") |> + dplyr::mutate(label = "{IMANOT}") + + df_n_flows <- dplyr::bind_rows( + df_n_flows, + df_fix_dep_cr, + df_fix_dep_grass, + df_import_animalcr, + df_synth_total, + df_imanot + ) |> + dplyr::mutate( + province = province_name, + year = year, + align = "L" + ) |> + dplyr::select(province, year, label, data, align) + + right_labels <- c( + "{CROP_POPIMPORT}", + "{IMPORT_ANIMALCR_RUM}", + "{IMPORT_ANIMALCR_MONOG}", + "{IMPORT_ANIMALCR}", + "{IMANOTR}", + "{IMANOTM}", + "{IMANOT}" + ) + + df_imphmana <- prov_destiny_df |> + dplyr::filter( + origin == "Outside", + destiny %in% c("population_food", "population_other_uses"), + box %in% c("Livestock", "Fish") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = "{IMPHMANA}", + province = province_name, + year = year, + align = "L" + ) |> + dplyr::select(province, year, label, data, align) + + df_n_flows <- dplyr::bind_rows( + df_n_flows, + df_imphmana, + df_crop_popimport + ) + + df_n_flows <- df_n_flows |> + tidyr::complete( + province, + year, + label, + fill = list(data = 0, align = "L") + ) |> + dplyr::mutate( + align = dplyr::case_when( + label %in% right_labels ~ "R", + TRUE ~ "L" + ) + ) + + df_n_flows +} + + +#' @title Create Livestock LU (Livestock Units) dataset +#' +#' @description +#' Calculated livestock units (LU) by province and year for ruminants and +#' monogastric animals. +#' Converts stock numbers into standardized LU values using conversion factors. +#' +#' @return +#' A tibble with columns `province`, `year`, `label`, `data`, and `align`. +#' +#' @keywords internal +.create_livestock_lu_df <- function() { + livestock_lu <- whep_read_file("stock_prod_ygps") |> + dplyr::rename_with(tolower) + lu_lookup <- whep_read_file("livestock_units") |> + dplyr::rename_with(tolower) + + df_lu <- livestock_lu |> + dplyr::select( + province_name, + year, + livestock_cat, + stock_number + ) |> + dplyr::distinct() |> + dplyr::left_join( + lu_lookup, + by = "livestock_cat" + ) |> + dplyr::filter(!is.na(lu_head), system %in% c("ruminant", "monogastric")) |> + dplyr::mutate( + LU = stock_number * lu_head + ) |> + dplyr::group_by(province_name, year, system) |> + dplyr::summarise(LU = sum(LU, na.rm = TRUE), .groups = "drop") |> + tidyr::pivot_wider( + names_from = system, + values_from = LU, + values_fill = 0 + ) |> + dplyr::mutate( + `{RUMIANTSLU}` = ruminant, + `{RUMIANTSMLU}` = ruminant / 1e6, + `{MONOGLU}` = monogastric, + `{MONOGMLU}` = monogastric / 1e6 + ) |> + dplyr::select( + province = province_name, + year = year, + `{RUMIANTSLU}`, + `{RUMIANTSMLU}`, + `{MONOGLU}`, + `{MONOGMLU}` + ) |> + tidyr::pivot_longer( + cols = -c(province, year), + names_to = "label", + values_to = "data" + ) |> + dplyr::mutate(align = "R") + + df_lu +} + +#' @title Create land dataset by province +#' +#' @description +#' Generates a dataset of land use by province and year of cropland (permanent +#' and non permanent), horticulture, and forest area for N and area (ha), +#' separated into irrigated and rainfed. +#' +#' @return +#' A tibble with columns `province`, `year`, `label`, `data`, and `align`. +#' +#' @keywords internal +.create_land_df <- function() { + n_balance <- whep_read_file("n_balance_ygpit_all") |> + dplyr::rename_with(tolower) + crop_lookup <- whep_read_file("grafs_crop_categories") |> + dplyr::rename_with(tolower) + + permanent_biomass <- crop_lookup |> + dplyr::filter(crop_type == "permanent") |> + dplyr::pull(name_biomass) + + horticulture_biomass <- crop_lookup |> + dplyr::filter(crop_type == "horticulture") |> + dplyr::pull(name_biomass) + + non_permanent_biomass <- crop_lookup |> + dplyr::filter(crop_type == "non_permanent") |> + dplyr::pull(name_biomass) + + df_land <- n_balance |> + dplyr::filter( + landuse %in% c("Cropland", "Forest_low", "Forest_high", "Dehesa") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise( + FORha = sum( + area_ygpit_ha[landuse %in% c("Forest_low", "Forest_high", "Dehesa")], + na.rm = TRUE + ), + FORMha = FORha / 1e6, + FORN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse %in% c("Forest_low", "Forest_high", "Dehesa") + ], + na.rm = TRUE + ), + + PERiha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% permanent_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + PERrha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% permanent_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + PERiMha = PERiha / 1e6, + PERrMha = PERrha / 1e6, + PERiN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% permanent_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + PERrN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% permanent_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + + HORiha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% horticulture_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + HORrha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% horticulture_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + HORiMha = HORiha / 1e6, + HORrMha = HORrha / 1e6, + HORiN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% horticulture_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + HORrN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% horticulture_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + + NPEiha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% non_permanent_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + NPErha = sum( + area_ygpit_ha[ + landuse == "Cropland" & + name_biomass %in% non_permanent_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + NPEiMha = NPEiha / 1e6, + NPErMha = NPErha / 1e6, + NPEiN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% non_permanent_biomass & + irrig_cat == "Irrigated" + ], + na.rm = TRUE + ), + NPErN = sum( + (prod_mgn + usedresidue_mgn + grazedweeds_mgn)[ + landuse == "Cropland" & + name_biomass %in% non_permanent_biomass & + irrig_cat == "Rainfed" + ], + na.rm = TRUE + ), + + .groups = "drop" + ) |> + tidyr::pivot_longer( + -c(province_name, year), + names_to = "var", + values_to = "data" + ) |> + + dplyr::mutate( + label = paste0("{", var, "}"), + province = province_name, + year = year, + align = "R" + ) |> + dplyr::select(province, year, label, data, align) + + df_land +} + + +#' @title Create dataset for greeonhouse, grassland, and N soil input +#' +#' @description +#' Generates dataset for greenhouse, grasslands, total km2, surpluses. +#' Combines with crops/forest dataset. +#' +#' @return +#' A tibble with columns `province`, `year`, `label`, `data`, and `align`. +#' +#' @keywords internal +.create_n_input_df <- function(n_balance) { + n_balance |> + dplyr::group_by(province_name, year) |> + dplyr::summarise( + `{GREHha}` = sum(area_ygpit_ha[irrig_cat == "Greenhouse"], na.rm = TRUE), + `{GREHMha}` = `{GREHha}` / 1e6, + `{GREHN}` = sum( + prod_mgn[irrig_cat == "Greenhouse"] + + usedresidue_mgn[irrig_cat == "Greenhouse"] + + grazedweeds_mgn[irrig_cat == "Greenhouse"], + na.rm = TRUE + ), + `{HAGRASS}` = sum( + area_ygpit_ha[ + landuse %in% + c( + "Dehesa", + "Forest_high", + "Forest_low", + "Other", + "Pasture_Shrubland" + ) + ], + na.rm = TRUE + ), + `{GRASSMha}` = `{HAGRASS}` / 1e6, + `{HACULT}` = sum(area_ygpit_ha[landuse == "Cropland"], na.rm = TRUE), + `{KM2_PROVINCE}` = sum(area_ygpit_ha, na.rm = TRUE) / 100, + .groups = "drop" + ) |> + tidyr::pivot_longer( + -c(province_name, year), + names_to = "label", + values_to = "data" + ) |> + dplyr::select(province = province_name, year = year, label, data) |> + dplyr::mutate(align = "L") +} + + +#' @title Create land nitrogen surplus dataset (cropland & semi-natural systems) +#' +#' @description +#' Calculates nitrogen surplus for cropland and semi-natural agroecosystems +#' (grassland) by province and year. The surplus is calculated as the difference +#' between total nitrogen inputs and outputs. +#' Inputs include nitrogen from synthetic fertilizers, biological fixation, +#' atmospheric deposition, and livestock manure. +#' Outputs include nitrogen flows from cropland to population, livestock, +#' exports, and other uses. +#' +#' @param prov_destiny_df A data frame containing nitrogen flows with columns +#' such as `origin`, `destiny`, `province_name`, `year`, and `mg_n`. +#' +#' @return +#' A tibble with columns: Province name, year, label (`{CROP_SURPLUS}` or `{GRASS_SURPLUS}`), nitrogen +#' surplus (mg_n), and text alignment. +#' +#' @keywords internal +.create_land_surplus_df <- function(prov_destiny_df) { + inputs <- prov_destiny_df |> + dplyr::filter( + (origin %in% + c("Synthetic", "Fixation", "Deposition", "Livestock") & + destiny %in% c("Cropland", "semi_natural_agroecosystems")) + ) |> + dplyr::mutate( + system = dplyr::case_when( + destiny == "Cropland" ~ "crop", + destiny == "semi_natural_agroecosystems" ~ "grass" + ) + ) |> + dplyr::group_by(province_name, year, system) |> + dplyr::summarise(input = sum(mg_n, na.rm = TRUE), .groups = "drop") + + outputs <- prov_destiny_df |> + dplyr::filter( + (origin == "Cropland" & + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + )) | + (origin == "semi_natural_agroecosystems" & + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + )) + ) |> + dplyr::mutate( + system = dplyr::case_when( + origin == "Cropland" ~ "crop", + origin == "semi_natural_agroecosystems" ~ "grass" + ) + ) |> + dplyr::group_by(province_name, year, system) |> + dplyr::summarise(output = sum(mg_n, na.rm = TRUE), .groups = "drop") + + dplyr::full_join( + inputs, + outputs, + by = c("province_name", "year", "system") + ) |> + dplyr::mutate( + input = dplyr::coalesce(input, 0), + output = dplyr::coalesce(output, 0), + surplus = input - output, + label = dplyr::case_when( + system == "crop" ~ "{CROP_SURPLUS}", + system == "grass" ~ "{GRASS_SURPLUS}" + ), + province = province_name, + year = year, + align = "R" + ) |> + dplyr::select(province, year, label, data = surplus, align) +} + + +#' @title Create nitrogen flow dataset by province +#' +#' @description +#' Generates nitrogen flow data (mg_n) by province and year, representing +#' @title Create nitrogen flow dataset by province +#' +#' @description +#' Generates nitrogen flow data (mg_n) by province and year, representing +#' exchanges between cropland, livestock, grassland, population, and exports. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, and +#' `align`. +#' +#' @keywords internal + +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_n_flow_df <- function(prov_destiny_df = NULL) { + if (is.null(prov_destiny_df)) { + prov_destiny_df <- create_n_prov_destiny() + } + + # --- Crop & livestock flows --- + crop_livestock_flows <- prov_destiny_df |> + dplyr::mutate( + label = dplyr::case_when( + origin == "Cropland" & destiny == "export" ~ "{CROP_EXPORT}", + origin == "Cropland" & + destiny %in% c("population_food", "population_other_uses") ~ + "{CROPS_TO_POP}", + origin == "Cropland" & + destiny %in% c("livestock_rum", "livestock_mono") ~ + "{CROPS_TO_LIVESTOCK}", + origin == "Livestock" & + destiny %in% c("population_food", "population_other_uses") ~ + "{LIVESTOCK_TO_HUMAN}", + origin == "semi_natural_agroecosystems" & + destiny %in% c("livestock_rum", "livestock_mono") ~ + "{GRASS_TO_LIVESTOCK}", + origin == "People" & destiny == "Cropland" ~ "{ORGOT}", + TRUE ~ NA_character_ + ) + ) |> + dplyr::filter(!is.na(label)) |> + dplyr::group_by(province_name, year, label) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + align = dplyr::if_else(label == "{ORGOT}", "R", "L") + ) |> + dplyr::rename(province = province_name, year = year) |> + tidyr::complete( + province = unique(prov_destiny_df$province_name), + year = unique(prov_destiny_df$year), + label = c( + "{CROP_EXPORT}", + "{CROPS_TO_POP}", + "{CROPS_TO_LIVESTOCK}", + "{LIVESTOCK_TO_HUMAN}", + "{GRASS_TO_LIVESTOCK}", + "{ORGOT}" + ), + fill = list(data = 0, align = "L") + ) + crop_livestock_flows +} + +#' @title Create livestock production dataset +#' +#' @description +#' Generates nitrogen production from livestock destined for population +#' (food or other uses) by province and year, distinguishing edible and +#' non-edible products. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_livestock_df <- function(prov_destiny_df) { + df_livestock <- prov_destiny_df |> + dplyr::filter( + destiny %in% c("population_food", "population_other_uses"), + origin == "Livestock" + ) |> + dplyr::mutate( + group_item = ifelse( + item %in% c("Hides and skins", "Wool (Clean Eq.)", "Silk"), + "non_edible", + "edible" + ) + ) |> + dplyr::group_by(province_name, year, group_item) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = dplyr::case_when( + group_item == "non_edible" ~ "{LVSTCK_NOEDIBLE}", + group_item == "edible" ~ "{LV_EDBL}" + ), + align = "L" + ) |> + dplyr::select( + province = province_name, + year = year, + label, + data = mg_n, + align + ) + + df_livestock +} + +#' @title Create milk production dataset +#' +#' @description +#' Generates nitrogen data for milk and dairy products consumed by population. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_milk_df <- function(prov_destiny_df) { + df_milk <- prov_destiny_df |> + dplyr::filter( + origin == "Livestock", + destiny == "population_food", + item %in% + c("Milk - Excluding Butter", "Milk, lactation", "Whey", "Butter, Ghee") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = "{LVST_MILK}", + align = "L" + ) |> + dplyr::select( + province = province_name, + year = year, + label, + data = mg_n, + align + ) + + df_milk +} + +#' @title Create livestock export dataset +#' +#' @description +#' Generates nitrogen flows associated with exported livestock products. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_livestock_export_df <- function(prov_destiny_df) { + df_livestock_export <- prov_destiny_df |> + dplyr::filter( + destiny == "export", + origin == "Livestock" + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = "{LIVESTOCK_EXPORTED}", + align = "L", + province = province_name, + year = year + ) |> + dplyr::select(province, year, label, data, align) + + df_livestock_export +} + +#' @title Create feed from cropland dataset +#' +#' @description +#' Creates nitrogen data representing feed transfers from cropland to +#' ruminant and monogastric livestock. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_feed_df <- function(prov_destiny_df) { + df_feed <- prov_destiny_df |> + dplyr::filter( + origin == "Cropland", + destiny %in% c("livestock_rum", "livestock_mono") + ) |> + dplyr::group_by(province_name, year, destiny) |> + dplyr::summarise( + data = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + label = dplyr::case_when( + destiny == "livestock_rum" ~ "{RCRTOLVSTCK_R}", + destiny == "livestock_mono" ~ "{MCRTOLVSTCK_M}" + ), + align = "L" + ) |> + dplyr::rename(province = province_name, year = year) |> + dplyr::select(province, year, label, data, align) + + df_feed +} + +#' @title Create crop losses dataset +#' +#' @description +#' Generates N data from other uses in cropland. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_crop_losses_df <- function(n_balance, prov_destiny_df) { + df_crop_oth <- prov_destiny_df |> + dplyr::filter( + origin == "Cropland", + destiny == "population_other_uses" + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise( + data = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + label = "{CRP_OTHUSES}", + align = "L" + ) |> + dplyr::rename( + province = province_name, + year = year + ) +} + + +#' @title Create animal losses dataset +#' +#' @description +#' Generates nitrogen loss data from livestock, including metabolic losses and +#' livestock products used for other uses. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_animal_losses_df <- function(prov_destiny_df) { + n_excretion <- whep_read_file("n_excretion_ygs") |> + dplyr::rename_with(tolower) |> + dplyr::select( + year, + province_name, + livestock_cat, + n_excr_mgn + ) |> + dplyr::distinct() |> + dplyr::mutate( + `{AN_LS}` = n_excr_mgn + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise( + `{AN_LS}` = sum(`{AN_LS}`, na.rm = TRUE), + .groups = "drop" + ) + + an_oth <- prov_destiny_df |> + dplyr::filter( + origin == "Livestock", + destiny == "population_other_uses" + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise( + `{AN_OTH}` = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + df_animal_losses <- n_excretion |> + dplyr::left_join(an_oth, by = c("province_name", "year")) |> + dplyr::mutate( + `{AN_OTH}` = ifelse(is.na(`{AN_OTH}`), 0, `{AN_OTH}`), + `{AN_LS_OTH}` = `{AN_LS}` + `{AN_OTH}` + ) |> + tidyr::pivot_longer( + cols = c(`{AN_LS}`, `{AN_OTH}`, `{AN_LS_OTH}`), + names_to = "label", + values_to = "data" + ) |> + dplyr::mutate( + align = "R" + ) |> + dplyr::rename( + province = province_name, + year = year + ) + + df_animal_losses +} + +#' @title Create combined livestock nitrogen dataset +#' +#' @description +#' Combines nitrogen data from livestock destined for humans, exports, and +#' losses to generate combined nitrogen output from livestock. +#' +#' @param prov_destiny_df A data frame containing production and destiny +#' information. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_livestock_total_df <- function(prov_destiny_df) { + prov_destiny_df |> + dplyr::filter( + origin == "Livestock", + destiny %in% + c( + "population_food", + "population_other_uses", + "export" + ) + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(data = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + label = "{LVSTCKTOTN}", + align = "L" + ) |> + dplyr::select( + province = province_name, + year = year, + label, + data, + align + ) +} + +#' @title Create livestock loss dataset +#' +#' @description +#' Calculates nitrogen losses from livestock excretion based on +#' excretion and loss share data. +#' +#' @return A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_livestock_surplus_df <- function(df_all_flows) { + input_labels <- c( + "{CROPS_TO_LIVESTOCK}", + "{GRASS_TO_LIVESTOCK}", + "{IMANOTR}", + "{IMANOTM}", + "{IMPORT_ANIMALCR_RUM}", + "{IMPORT_ANIMALCR_MONOG}" + ) + + output_labels <- c( + "{LIVESTOCK_TO_HUMAN}", + "{LIVESTOCK_EXPORTED}", + "{LIVESTOCK_TO_CROPS}", + "{LIVESTOCK_TO_GRASS}", + "{AN_OTH}" + ) + + df_inputs <- df_all_flows |> + dplyr::filter(label %in% input_labels) |> + dplyr::group_by(province, year) |> + dplyr::summarise( + input = sum(as.numeric(data), na.rm = TRUE), + .groups = "drop" + ) + + df_outputs <- df_all_flows |> + dplyr::filter(label %in% output_labels) |> + dplyr::group_by(province, year) |> + dplyr::summarise( + output = sum(as.numeric(data), na.rm = TRUE), + .groups = "drop" + ) + + dplyr::full_join(df_inputs, df_outputs, by = c("province", "year")) |> + dplyr::mutate( + input = dplyr::coalesce(input, 0), + output = dplyr::coalesce(output, 0), + data = input - output, + label = "{LIVGASLOSS}", + align = "R" + ) |> + dplyr::select(province, year, label, data, align) +} + +#' @title Create population dataset +#' +#' @description +#' Loads population data (in million inhabitants, MInhab) and converts it +#' into the GRAFS plot structure. +#' +#' @return +#' A tibble with columns `province`, `year`, `label`, `data`, `align`. +#' +#' @keywords internal +.create_population_df <- function() { + population <- whep_read_file("population_yg") |> + dplyr::rename_with(tolower) + + df_pop <- population |> + dplyr::select( + province = province_name, + year = year, + pop_mpeop_yg + ) |> + dplyr::mutate( + label = "{POPULATIONM}", + data = pop_mpeop_yg, + align = "L" + ) |> + dplyr::select(province, year, label, data, align) + + df_pop +} + + +#' @keywords internal +.create_wastewater_surplus_df <- function(df_all_flows, prov_destiny_df) { + input_labels <- c( + "{CROPS_TO_POP}", + "{CROP_POPIMPORT}", + "{LIVESTOCK_TO_HUMAN}", + "{IMPHMANA}" + ) + + df_inputs <- df_all_flows |> + dplyr::filter(label %in% input_labels) |> + dplyr::group_by(province, year) |> + dplyr::summarise( + input = sum(as.numeric(data), na.rm = TRUE), + .groups = "drop" + ) + + df_returned <- prov_destiny_df |> + dplyr::filter( + origin == "People", + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(province_name, year) |> + dplyr::summarise(returned = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::rename(province = province_name) + + dplyr::full_join(df_inputs, df_returned, by = c("province", "year")) |> + dplyr::mutate( + input = dplyr::coalesce(input, 0), + returned = dplyr::coalesce(returned, 0), + data = input - returned, + label = "{WASTEWATER}", + align = "R" + ) |> + dplyr::select(province, year, label, data, align) +} + + +#' @title Combine and finalize nitrogen flow dataset +#' +#' @description +#' Merges all the created nitrogen datasets into a unified structure. +#' Adding missing labels and setting WIDTH_MAX to 1500. IMPHUMHONEY should be 0. +#' The other labels (CRPNOLV", "NCONTCROP") are +#' set to 0, since I don't know how to create them yet. +#' +#' @param crop_livestock_flows Data frame of crop-livestock nitrogen flows. +#' @param df_livestock Data frame of livestock nitrogen data. +#' @param df_lv_r_m Data frame of livestock feed data. +#' @param df_crop_losses Data frame of crop nitrogen losses. +#' @param df_animal_losses Data frame of animal nitrogen losses. +#' @param df_livestock_total Data frame of total livestock nitrogen. +#' @param df_livestock_surplus Data frame of livestock surplus nitrogen. +#' +#' @return A tibble with standardized columns `province`, `year`, `label`, +#' `data`, and `align`. +#' +#' @keywords internal +.combine_and_finalize_df <- function( + crop_livestock_flows, + df_livestock, + df_lv_r_m, + df_crop_losses, + df_animal_losses, + df_livestock_total, + df_livestock_surplus, + df_land_surplus +) { + df_combi <- dplyr::bind_rows( + crop_livestock_flows |> dplyr::select(province, year, label, data, align), + df_livestock |> dplyr::select(province, year, label, data, align), + df_lv_r_m |> dplyr::select(province, year, label, data, align), + df_crop_losses |> dplyr::select(province, year, label, data, align), + df_animal_losses |> dplyr::select(province, year, label, data, align), + df_livestock_total |> dplyr::select(province, year, label, data, align), + df_livestock_surplus |> dplyr::select(province, year, label, data, align), + df_land_surplus |> dplyr::select(province, year, label, data, align) + ) |> + dplyr::arrange(province, year, label) |> + dplyr::mutate( + data = as.character(data), + align = as.character(align) + ) + + missing_labels <- c( + "{IMPHUMHONEY}", + "{CRP_LS_OTHUSES}", + "{CRP_LS}", + "{CRPNOLV}", + "{NCONTCROP}", + "{WIDTH_MAX}" + ) + + df_combi <- df_combi |> + tidyr::complete( + province, + year, + label = c(unique(df_combi$label), missing_labels), + fill = list(data = "0", align = "L") + ) |> + dplyr::mutate( + align = dplyr::case_when( + label %in% c("{NCONTCROP}", "{ORGOT}") ~ "R", + TRUE ~ align + ) + ) |> + dplyr::bind_rows( + dplyr::distinct(df_combi, province, year) |> + dplyr::mutate( + label = "{YEAR}", + data = as.character(year), + align = "L" + ) |> + dplyr::select(province, year, label, data, align), + dplyr::distinct(df_combi, province, year) |> + dplyr::mutate( + label = "{PROVINCE_NAME}", + data = as.character(province), + align = "L" + ) |> + dplyr::select(province, year, label, data, align) + ) |> + dplyr::mutate( + data = dplyr::case_when( + label == "{WIDTH_MAX}" ~ "1500", + TRUE ~ data + ), + align = dplyr::case_when( + label == "{WIDTH_MAX}" ~ "L", + label %in% + c( + "{NCONTCROP}", + "{ORGOT}" + ) ~ + "R", + TRUE ~ align + ) + ) |> + dplyr::arrange(province, year, label) + + df_combi +} diff --git a/R/input_files.R b/R/input_files.R index 54ef93b6..a03021c4 100644 --- a/R/input_files.R +++ b/R/input_files.R @@ -55,17 +55,18 @@ whep_read_file <- function(file_alias, type = "parquet", version = NULL) { file_info <- .fetch_file_info(file_alias, whep::whep_inputs) version <- .choose_version(file_info$version, version) + pin_name <- if (!is.na(file_info$pin_name)) file_info$pin_name else file_alias paths <- tryCatch( .get_local_board() |> - pins::pin_download(file_alias, version = version), + pins::pin_download(pin_name, version = version), error = function(e) { tryCatch( file_info |> .get_remote_board() |> - pins::pin_download(file_alias, version = version), + pins::pin_download(pin_name, version = version), error = function(e) { - .get_cache_paths(file_info, file_alias, version, e) + .get_cache_paths(file_info, pin_name, version, e) } ) } @@ -91,16 +92,16 @@ whep_read_file <- function(file_alias, type = "parquet", version = NULL) { #' @examples #' whep_list_file_versions("read_example") whep_list_file_versions <- function(file_alias) { - board <- if (file_alias == "read_example") { - .get_local_board() - } else { - file_alias |> - .fetch_file_info(whep::whep_inputs) |> - .get_remote_board() + if (file_alias == "read_example") { + return(.get_local_board() |> pins::pin_versions(file_alias)) } - board |> - pins::pin_versions(file_alias) + file_info <- .fetch_file_info(file_alias, whep::whep_inputs) + pin_name <- if (!is.na(file_info$pin_name)) file_info$pin_name else file_alias + + file_info |> + .get_remote_board() |> + pins::pin_versions(pin_name) } .read_file <- function(paths, extension) { diff --git a/R/input_output_plots.R b/R/input_output_plots.R new file mode 100644 index 00000000..df17a687 --- /dev/null +++ b/R/input_output_plots.R @@ -0,0 +1,532 @@ +#' Plot N inputs, production and surplus +#' +#' @param system Character. One of "Cropland" or +#' "semi_natural_agroecosystems". +#' +#' @return A ggplot object. +#' @export +plot_input_output <- function( + system = c("Cropland", "semi_natural_agroecosystems"), + per_ha = FALSE +) { + system <- match.arg(system) + data <- create_n_nat_destiny() + needs_n_balance <- per_ha || system == "semi_natural_agroecosystems" + n_balance <- if (needs_n_balance) { + whep_read_file("n_balance_ygpit_all") + } else { + NULL + } + accum <- if (system == "semi_natural_agroecosystems") { + landuse_filter <- unique(n_balance$LandUse[n_balance$LandUse != "Cropland"]) + .calculate_n_accum(n_balance, landuse_filter) + } else { + tibble::tibble(year = integer(), mg_n = numeric(), Type = character()) + } + + df_system <- data |> + dplyr::filter( + province_name != "Sea" + ) + + inputs <- df_system |> + dplyr::filter( + destiny == system, + origin %in% + c("Deposition", "Fixation", "Synthetic", "Livestock", "People") + ) |> + dplyr::group_by(year, origin) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate( + Type = dplyr::recode( + origin, + "Deposition" = "Deposition", + "Fixation" = "Fixation", + "Synthetic" = "Synthetic_fertilizer", + "Livestock" = "Manure", + "People" = "Urban" + ) + ) + + residue_items <- c("Straw", "Other crop residues") + + production <- df_system |> + dplyr::filter( + origin == system, + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::mutate( + Type = dplyr::if_else(item %in% residue_items, "Residues", "Production") + ) |> + dplyr::group_by(year, Type) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") + + input_sum <- inputs |> + dplyr::group_by(year) |> + dplyr::summarise(Input_Total = sum(mg_n), .groups = "drop") + + prod_sum <- production |> + dplyr::group_by(year) |> + dplyr::summarise(Production = sum(mg_n), .groups = "drop") + + surplus <- input_sum |> + dplyr::left_join(prod_sum, by = "year") |> + dplyr::left_join( + accum |> dplyr::select(year, Accum = mg_n), + by = "year" + ) |> + dplyr::mutate( + Production = dplyr::coalesce(Production, 0), + Accum = dplyr::coalesce(Accum, 0), + Surplus = pmax(Input_Total - Production - Accum, 0) + ) |> + dplyr::select(year, Surplus) |> + dplyr::mutate(Type = "Surplus") + + lu_area <- if (per_ha) { + lu_filter <- if (system == "Cropland") { + "Cropland" + } else { + unique(n_balance$LandUse[n_balance$LandUse != "Cropland"]) + } + .get_area_national(n_balance, lu_filter) + } else { + NULL + } + + plot_df <- dplyr::bind_rows( + inputs |> dplyr::select(year, Type, mg_n), + production |> dplyr::select(year, Type, mg_n), + accum |> dplyr::select(year, Type, mg_n), + surplus |> dplyr::rename(mg_n = Surplus) + ) |> + .normalize_mg_n(per_ha, lu_area) |> + dplyr::mutate( + mg_n = dplyr::case_when( + Type %in% + c( + "Synthetic_fertilizer", + "Manure", + "Fixation", + "Deposition", + "Urban" + ) ~ -mg_n, + TRUE ~ mg_n + ), + Type = factor( + Type, + levels = c( + "Synthetic_fertilizer", + "Manure", + "Fixation", + "Deposition", + "Urban", + "Surplus", + "Accumulation", + "Production", + "Residues" + ) + ) + ) + + y_lab <- if (per_ha) "kg N/ha" else "Gg N" + + ggplot2::ggplot(plot_df, ggplot2::aes(x = year, y = mg_n, fill = Type)) + + ggplot2::geom_area(position = "stack") + + ggplot2::geom_hline(yintercept = 0, linetype = "dashed") + + ggplot2::annotate( + "text", + x = -Inf, + y = Inf, + label = if (system == "Cropland") "Cropland" else "Semi-natural agroecosystems", + hjust = -0.05, + vjust = 1.5, + size = 3.5, + fontface = "bold" + ) + + ggplot2::labs(x = NULL, y = y_lab, fill = "") + + ggplot2::scale_fill_manual( + values = c( + "Synthetic_fertilizer" = "red4", + "Manure" = "darkorange3", + "Urban" = "darkorange4", + "Fixation" = "olivedrab4", + "Deposition" = "gray40", + "Surplus" = "slategray", + "Accumulation" = "steelblue4", + "Residues" = "goldenrod3", + "Production" = "orange3" + ) + ) + + ggplot2::theme_minimal() +} + + +#' Plot N inputs, production and surplus for Livestock system +#' +#' @return A ggplot object. +#' @export +plot_input_output_livestock <- function(per_ha = FALSE) { + data <- create_n_nat_destiny() + livestock_prod <- whep_read_file("stock_prod_ygps") + lu_area <- if (per_ha) { + .get_area_national(whep_read_file("n_balance_ygpit_all")) + } else { + NULL + } + + df <- data |> + dplyr::filter(province_name != "Sea") + + item_to_type <- livestock_prod |> + dplyr::distinct(Item, Livestock_cat) |> + dplyr::mutate( + prod_type = dplyr::case_when( + Livestock_cat %in% + c( + "Cattle_meat", + "Cattle_milk", + "Goats", + "Sheep", + "Horses", + "Donkeys_mules" + ) ~ + "Production_rum", + Livestock_cat %in% + c("Pigs", "Hogs", "Poultry", "Rabbits", "Bees") ~ + "Production_mono" + ) + ) |> + dplyr::filter(!is.na(prod_type)) |> + dplyr::distinct(Item, prod_type) + + inputs <- df |> + dplyr::filter( + destiny %in% c("livestock_rum", "livestock_mono"), + origin %in% c("semi_natural_agroecosystems", "Cropland", "Outside") + ) |> + dplyr::mutate( + Type = dplyr::case_when( + origin == "semi_natural_agroecosystems" ~ "Grass_local", + origin == "Cropland" ~ "Crops_local", + origin == "Outside" ~ "Imports" + ) + ) |> + dplyr::group_by(year, Type) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") + + production <- df |> + dplyr::filter( + origin == "Livestock", + destiny %in% + c( + "population_food", + "population_other_uses", + "export", + "livestock_rum", + "livestock_mono" + ) + ) |> + dplyr::left_join(item_to_type, by = c("item" = "Item")) |> + dplyr::mutate( + prod_type = dplyr::coalesce(prod_type, "Production_rum") + ) |> + dplyr::group_by(year, Type = prod_type) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") + + input_sum <- inputs |> + dplyr::group_by(year) |> + dplyr::summarise(Input_Total = sum(mg_n), .groups = "drop") + + prod_sum <- production |> + dplyr::group_by(year) |> + dplyr::summarise(Production = sum(mg_n), .groups = "drop") + + surplus <- input_sum |> + dplyr::left_join(prod_sum, by = "year") |> + dplyr::mutate( + Production = dplyr::coalesce(Production, 0), + Surplus = Input_Total - Production + ) |> + dplyr::select(year, Surplus) |> + dplyr::mutate(Type = "Surplus") + + plot_df <- dplyr::bind_rows( + inputs, + production, + surplus |> dplyr::rename(mg_n = Surplus) + ) |> + .normalize_mg_n(per_ha, lu_area) |> + dplyr::mutate( + mg_n = dplyr::case_when( + Type %in% c("Grass_local", "Crops_local", "Imports") ~ -mg_n, + TRUE ~ mg_n + ), + Type = factor( + Type, + levels = c( + "Imports", + "Crops_local", + "Grass_local", + "Surplus", + "Production_rum", + "Production_mono" + ) + ) + ) + + y_lab <- if (per_ha) "kg N/ha" else "Gg N" + + ggplot2::ggplot(plot_df, ggplot2::aes(x = year, y = mg_n, fill = Type)) + + ggplot2::geom_area(position = "stack") + + ggplot2::geom_hline(yintercept = 0, linetype = "dashed") + + ggplot2::annotate( + "text", + x = -Inf, + y = Inf, + label = "Livestock system", + hjust = -0.05, + vjust = 1.5, + size = 3.5, + fontface = "bold" + ) + + ggplot2::labs(x = NULL, y = y_lab, fill = "") + + ggplot2::scale_fill_manual( + values = c( + "Grass_local" = "darkolivegreen3", + "Crops_local" = "#1b9e77", + "Imports" = "steelblue3", + "Surplus" = "slategray", + "Production_rum" = "orange3", + "Production_mono" = "darkorange3" + ) + ) + + ggplot2::theme_minimal() +} + + +#' Plot N inputs and uses for full agro-food system (system level) +#' +#' @return A ggplot object. +#' @export +plot_input_output_system <- function(per_ha = FALSE) { + data <- create_n_nat_destiny() + n_balance <- whep_read_file("n_balance_ygpit_all") + accum <- .calculate_n_accum(n_balance) + lu_area <- if (per_ha) .get_area_national(n_balance) else NULL + + df <- data |> + dplyr::filter(province_name != "Sea") + + soil_inputs <- df |> + dplyr::filter( + origin %in% c("Synthetic", "Fixation", "Deposition"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year, origin) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") |> + dplyr::mutate( + Type = dplyr::recode( + origin, + "Synthetic" = "Synthetic_fertilizer", + "Fixation" = "Fixation", + "Deposition" = "Deposition" + ) + ) + + feed_import <- df |> + dplyr::filter( + origin == "Outside", + destiny %in% c("livestock_rum", "livestock_mono") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") |> + dplyr::mutate(Type = "Feed_import") + + food_import <- df |> + dplyr::filter( + origin == "Outside", + destiny %in% c("population_food", "population_other_uses") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") |> + dplyr::mutate(Type = "Food_import") + + inputs <- dplyr::bind_rows( + soil_inputs |> dplyr::select(year, Type, mg_n), + feed_import, + food_import + ) + + livestock_ingestion <- df |> + dplyr::filter( + destiny %in% c("livestock_rum", "livestock_mono"), + origin %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") |> + dplyr::mutate(Type = "Feed") + + human_ingestion <- df |> + dplyr::filter( + destiny %in% c("population_food", "population_other_uses"), + origin %in% c("Cropland", "semi_natural_agroecosystems", "Livestock") + ) |> + dplyr::mutate( + Type = dplyr::if_else(destiny == "population_food", "Food", "Other_uses") + ) |> + dplyr::group_by(year, Type) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") + + exports <- df |> + dplyr::filter(destiny == "export") |> + dplyr::group_by(year) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") |> + dplyr::mutate(Type = "Export") + + uses_core <- dplyr::bind_rows( + livestock_ingestion, + human_ingestion, + exports + ) + + input_sum <- inputs |> + dplyr::group_by(year) |> + dplyr::summarise(Input_Total = sum(mg_n), .groups = "drop") + + use_sum <- uses_core |> + dplyr::group_by(year) |> + dplyr::summarise(Use_Total = sum(mg_n), .groups = "drop") + + surplus <- input_sum |> + dplyr::left_join(use_sum, by = "year") |> + dplyr::left_join( + accum |> dplyr::select(year, Accum = mg_n), + by = "year" + ) |> + dplyr::mutate( + Use_Total = dplyr::coalesce(Use_Total, 0), + Accum = dplyr::coalesce(Accum, 0), + mg_n = pmax(Input_Total - Use_Total - Accum, 0), + Type = "Surplus" + ) |> + dplyr::select(year, Type, mg_n) + + y_lab <- if (per_ha) "kg N/ha" else "Gg N" + + plot_df <- dplyr::bind_rows( + inputs, + uses_core, + accum |> dplyr::select(year, Type, mg_n), + surplus + ) |> + .normalize_mg_n(per_ha, lu_area) |> + dplyr::mutate( + mg_n = dplyr::case_when( + Type %in% + c( + "Synthetic_fertilizer", + "Fixation", + "Deposition", + "Feed_import", + "Food_import" + ) ~ -mg_n, + TRUE ~ mg_n + ), + Type = factor( + Type, + levels = c( + "Synthetic_fertilizer", + "Fixation", + "Deposition", + "Feed_import", + "Food_import", + "Surplus", + "Accumulation", + "Feed", + "Food", + "Other_uses", + "Export" + ) + ) + ) + + ggplot2::ggplot(plot_df, ggplot2::aes(x = year, y = mg_n, fill = Type)) + + ggplot2::geom_area(position = "stack") + + ggplot2::geom_hline(yintercept = 0, linetype = "dashed") + + ggplot2::annotate( + "text", + x = -Inf, + y = Inf, + label = "Agro-food system", + hjust = -0.05, + vjust = 1.5, + size = 3.5, + fontface = "bold" + ) + + ggplot2::labs(x = NULL, y = y_lab, fill = "") + + ggplot2::scale_fill_manual( + values = c( + "Synthetic_fertilizer" = "red4", + "Fixation" = "olivedrab4", + "Deposition" = "gray40", + "Feed_import" = "#1b9e77", + "Food_import" = "darkolivegreen3", + "Accumulation" = "steelblue4", + "Feed" = "darkorange3", + "Food" = "darkorange4", + "Other_uses" = "sandybrown", + "Export" = "orange3", + "Surplus" = "slategray" + ) + ) + + ggplot2::theme_minimal() +} + + +.get_area_national <- function(n_balance, landuse = NULL) { + df <- n_balance + if (!is.null(landuse)) { + df <- dplyr::filter(df, LandUse %in% landuse) + } + df |> + dplyr::group_by(Year) |> + dplyr::summarise( + area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::rename(year = Year) +} + +.normalize_mg_n <- function(df, per_ha, lu_area) { + if (per_ha) { + df |> + dplyr::left_join(lu_area, by = "year") |> + dplyr::mutate(mg_n = mg_n * 1000 / area_ha) |> + dplyr::select(-area_ha) + } else { + dplyr::mutate(df, mg_n = mg_n / 1000) + } +} + +.calculate_n_accum <- function(n_balance, landuse = NULL) { + df <- n_balance + if (!is.null(landuse)) { + df <- dplyr::filter(df, LandUse %in% landuse) + } + df |> + dplyr::mutate( + Accum_net = Accum_gain_AG_MgN + Accum_gain_BG_MgN - Accum_loss + ) |> + dplyr::group_by(Year) |> + dplyr::summarise(mg_n = sum(Accum_net, na.rm = TRUE), .groups = "drop") |> + dplyr::rename(year = Year) |> + dplyr::mutate(Type = "Accumulation") +} diff --git a/R/intensification_specialization_plot.R b/R/intensification_specialization_plot.R new file mode 100644 index 00000000..8cc3f23e --- /dev/null +++ b/R/intensification_specialization_plot.R @@ -0,0 +1,2095 @@ +intens_spec_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Area per province ----F + area_df <- npp_ygpit |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- New N soil inputs (mg_n) ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% c("Synthetic", "Deposition", "Fixation"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + total_input_mg = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Synthetic share ---- + synthetic_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Synthetic", "Livestock", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + synthetic_share = sum(mg_n[origin == "Synthetic"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Feed import share ---- + feed_import_share <- flows |> + dplyr::filter( + destiny %in% c("livestock_mono", "livestock_rum"), + origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + feed_import_share = sum(mg_n[origin == "Outside"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- n_inputs |> + dplyr::left_join(area_df, by = c("year", "province_name")) |> + dplyr::left_join(synthetic_share, by = c("year", "province_name")) |> + dplyr::left_join(feed_import_share, by = c("year", "province_name")) |> + dplyr::mutate( + intensification = (total_input_mg * 1000) / area_ha, + specialization = pmax(synthetic_share, feed_import_share) + ) |> + dplyr::filter(!is.na(intensification), !is.na(specialization)) + + # ---- Add typology ---- + df <- df |> + dplyr::left_join( + create_typo_ts_plot() |> + dplyr::select(year, province_name, Typology_base) |> + dplyr::rename(Typology = Typology_base), + by = c("year", "province_name") + ) |> + dplyr::mutate( + Typology = gsub(" \\(intensive\\)| \\(extensive\\)", "", Typology) + ) |> + dplyr::filter(!is.na(Typology)) + + # ---- Define periods ---- + df <- df |> + dplyr::mutate( + Period = dplyr::case_when( + year < 1900 ~ "1860-1900", + year < 1950 ~ "1900-1950", + year < 1990 ~ "1950-1990", + TRUE ~ "1990-2021" + ) + ) + + df$Period <- factor( + df$Period, + levels = c("1860-1900", "1900-1950", "1950-1990", "1990-2021") + ) + + # ---- Plot ---- + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems" = "#F7DD5A", + "Specialized livestock systems" = "#b3001b", + "Connected crop-livestock systems" = "#7A4F20", + "Disconnected crop-livestock systems" = "#E67E00" + ) + + df_sample <- df |> dplyr::sample_frac(0.2) + + p <- ggplot2::ggplot( + df_sample, + ggplot2::aes( + x = intensification, + y = specialization, + color = Typology + ) + ) + + ggplot2::geom_point( + alpha = 0.8, + size = 2 + ) + + ggplot2::facet_wrap(~Period) + + ggplot2::scale_color_manual(values = typology_colors) + + ggplot2::labs( + x = "Intensification (kg N / ha)", + y = "Specialization (max synthetic and feed import share)", + color = "Typology", + title = "Provincial intensification and specialization in Spain (1860-2021)" + ) + + ggplot2::theme_minimal() + + print(p) + + list(plot = p, data = df) +} + + +circ_intens_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Area per province ---- + area_df <- npp_ygpit |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- New N soil inputs ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% c("Synthetic", "Deposition", "Fixation"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + total_input_mg = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Manure recycling share ---- + manure_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Livestock", "Synthetic", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + manure_share = sum(mg_n[origin == "Livestock"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Urban recycling share ---- + urban_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Livestock", "Synthetic", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + urban_share = sum(mg_n[origin == "People"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Local feed share ---- + local_feed_share <- flows |> + dplyr::filter( + destiny %in% c("livestock_mono", "livestock_rum"), + origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + local_feed_share = sum( + mg_n[origin %in% c("Cropland", "semi_natural_agroecosystems")], + na.rm = TRUE + ) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- n_inputs |> + dplyr::left_join(area_df, by = c("year", "province_name")) |> + dplyr::left_join(manure_share, by = c("year", "province_name")) |> + dplyr::left_join(urban_share, by = c("year", "province_name")) |> + dplyr::left_join(local_feed_share, by = c("year", "province_name")) |> + dplyr::mutate( + intensification = (total_input_mg * 1000) / area_ha, + circularity = (manure_share + urban_share + local_feed_share) / 3 + ) |> + dplyr::filter(!is.na(intensification), !is.na(circularity)) + + # ---- Add typology ---- + df <- df |> + dplyr::left_join( + create_typo_ts_plot() |> + dplyr::select(year, province_name, Typology_base) |> + dplyr::rename(Typology = Typology_base), + by = c("year", "province_name") + ) |> + dplyr::mutate( + Typology = gsub(" \\(intensive\\)| \\(extensive\\)", "", Typology) + ) |> + dplyr::filter(!is.na(Typology)) + + # ---- Define periods ---- + df <- df |> + dplyr::mutate( + Period = dplyr::case_when( + year < 1900 ~ "1860-1900", + year < 1950 ~ "1900-1950", + year < 1990 ~ "1950-1990", + TRUE ~ "1990-2021" + ) + ) + + df$Period <- factor( + df$Period, + levels = c("1860-1900", "1900-1950", "1950-1990", "1990-2021") + ) + + # ---- Typology colors ---- + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems" = "#F7DD5A", + "Specialized livestock systems" = "#b3001b", + "Connected crop-livestock systems" = "#7A4F20", + "Disconnected crop-livestock systems" = "#E67E00" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = intensification, + y = circularity, + color = Typology + ) + ) + + ggplot2::geom_point( + alpha = 0.8, + size = 2 + ) + + ggplot2::facet_wrap(~Period) + + ggplot2::scale_color_manual(values = typology_colors) + + ggplot2::labs( + x = "Intensification (kg N / ha)", + y = "Circularity index", + color = "Typology", + title = "Circularity and intensification in Spanish agro-food systems (1860-2021)" + ) + + ggplot2::theme_minimal() + + print(p) + + list(plot = p, data = df) +} + +circ_nue_traj_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Cropland area ---- + cropland_area <- npp_ygpit |> + dplyr::filter( + LandUse %in% + c( + "Cropland", + "semi_natural_agroecosystems" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + cropland_area = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Crop production ---- + crop_production <- flows |> + dplyr::filter( + origin %in% + c( + "Cropland", + "semi_natural_agroecosystems" + ), + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Total N inputs ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Fixation", + "Deposition", + "Livestock", + "People" + ), + destiny %in% + c( + "Cropland", + "semi_natural_agroecosystems" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Manure recycling share ---- + manure_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Livestock", "Synthetic", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + manure_share = sum(mg_n[origin == "Livestock"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Urban recycling share ---- + urban_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Livestock", "Synthetic", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + urban_share = sum(mg_n[origin == "People"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Local feed share ---- + local_feed_share <- flows |> + dplyr::filter( + destiny %in% c("livestock_mono", "livestock_rum"), + origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside") + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + local_feed_share = sum( + mg_n[origin %in% c("Cropland", "semi_natural_agroecosystems")], + na.rm = TRUE + ) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- cropland_area |> + dplyr::left_join(crop_production, by = "year") |> + dplyr::left_join(n_inputs, by = "year") |> + dplyr::left_join(manure_share, by = "year") |> + dplyr::left_join(urban_share, by = "year") |> + dplyr::left_join(local_feed_share, by = "year") |> + dplyr::mutate( + # NUE + NUE = (crop_N / total_inputs) * 100, + + # Circularity index + circularity = (manure_share + urban_share + local_feed_share) / 3 + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = NUE, + y = circularity, + color = year + ) + ) + + ggplot2::geom_point(size = 3) + + ggplot2::scale_color_viridis_c(option = "plasma") + + ggplot2::labs( + x = "NUE (%)", + y = "Circularity index", + color = "Year", + title = "Circularity and NUE in Spain (1860-2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + list(plot = p, data = df) +} + +circ_nue_crop_ts_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + + # ---- Crop production ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Total N inputs to cropland ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Fixation", + "Deposition", + "Livestock", + "People" + ), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Manure recycling share ---- + manure_share <- flows |> + dplyr::filter( + destiny == "Cropland", + origin %in% + c( + "Livestock", + "Synthetic", + "Fixation", + "Deposition", + "People" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + manure_share = sum(mg_n[origin == "Livestock"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Urban recycling share ---- + urban_share <- flows |> + dplyr::filter( + destiny == "Cropland", + origin %in% + c( + "Livestock", + "Synthetic", + "Fixation", + "Deposition", + "People" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + urban_share = sum(mg_n[origin == "People"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Local feed share ---- + local_feed_share <- flows |> + dplyr::filter( + box == "Cropland", + destiny %in% + c( + "livestock_mono", + "livestock_rum" + ), + origin %in% + c( + "Cropland", + "Outside" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + local_feed_share = sum(mg_n[origin == "Cropland"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- crop_production |> + dplyr::left_join(n_inputs, by = "year") |> + dplyr::left_join(manure_share, by = "year") |> + dplyr::left_join(urban_share, by = "year") |> + dplyr::left_join(local_feed_share, by = "year") |> + dplyr::mutate( + # Crop NUE + NUE = (crop_N / total_inputs) * 100, + + # Circularity index + circularity = ((manure_share + urban_share + local_feed_share) / 3) * 100 + ) + + # ---- Long format ---- + df_long <- df |> + tidyr::pivot_longer( + cols = c(NUE, circularity), + names_to = "indicator", + values_to = "value" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df_long, + ggplot2::aes( + x = year, + y = value, + color = indicator + ) + ) + + ggplot2::geom_line(size = 1.3) + + ggplot2::scale_color_manual( + values = c( + NUE = "#e41a1c", + circularity = "#377eb8" + ), + labels = c( + "Crop NUE", + "Circularity" + ) + ) + + ggplot2::labs( + x = NULL, + y = "", + color = "Indicator", + title = "Crop N use efficiency and circularity in Spain (1860-2021)" + ) + + ggplot2::scale_y_continuous( + labels = scales::label_percent(scale = 1), + limits = c(0, 100) + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/circularity_nue_cropland_timeseries.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + +intens_spec_ts <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Area per province ---- + area_df <- npp_ygpit |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- New N soil inputs ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% c("Synthetic", "Deposition", "Fixation"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + total_input_mg = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Synthetic fertilizer share ---- + synthetic_share <- flows |> + dplyr::filter( + destiny %in% c("Cropland", "semi_natural_agroecosystems"), + origin %in% + c("Synthetic", "Livestock", "Fixation", "Deposition", "People") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + synthetic_share = sum(mg_n[origin == "Synthetic"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Feed import share ---- + feed_import_share <- flows |> + dplyr::filter( + destiny %in% c("livestock_mono", "livestock_rum"), + origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + feed_import_share = sum(mg_n[origin == "Outside"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- n_inputs |> + dplyr::left_join(area_df, by = c("year", "province_name")) |> + dplyr::left_join(synthetic_share, by = c("year", "province_name")) |> + dplyr::left_join(feed_import_share, by = c("year", "province_name")) |> + dplyr::mutate( + # Intensification (kg N per ha) + intensification = (total_input_mg * 1000) / area_ha, + + # Specialization index + specialization = (synthetic_share + feed_import_share) / 2 + ) + + # ---- National yearly averages ---- + df_ts <- df |> + dplyr::group_by(year) |> + dplyr::summarise( + intensification = mean(intensification, na.rm = TRUE), + specialization = mean(specialization, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Index (1860 = 100) ---- + base <- df_ts |> dplyr::filter(year == min(year)) + + df_ts <- df_ts |> + dplyr::mutate( + intensification_index = intensification / base$intensification * 100, + + specialization_index = specialization / base$specialization * 100 + ) + + # ---- Convert to long format ---- + df_long <- df_ts |> + tidyr::pivot_longer( + cols = c( + intensification_index, + specialization_index + ), + names_to = "indicator", + values_to = "value" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df_long, + ggplot2::aes( + x = year, + y = value, + color = indicator + ) + ) + + ggplot2::geom_line(size = 1.2) + + ggplot2::scale_color_manual( + values = c( + "intensification_index" = "#e41a1c", + "specialization_index" = "#377eb8" + ), + labels = c( + "Intensification", + "Specialization" + ) + ) + + ggplot2::labs( + x = NULL, + y = "Index (1860 = 100)", + color = "Indicator", + title = "Intensification and specialization in Spain (1860-2020)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold"), + legend.position = "right" + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + filename = "C:/PhD/Typologies/Typologies_spain/typology_plot/intensification_specialization_timeseries.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df_ts) +} + + +yield_nue_trajectory_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Cropland area ---- + cropland_area <- npp_ygpit |> + dplyr::filter(landuse == "Cropland") |> + dplyr::group_by(year) |> + dplyr::summarise( + cropland_area = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Crop production ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Total N inputs ---- + n_inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Fixation", + "Deposition", + "Livestock", + "People" + ), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine ---- + df <- cropland_area |> + dplyr::left_join(crop_production, by = "year") |> + dplyr::left_join(n_inputs, by = "year") |> + dplyr::mutate( + # Crop productivity (kg N / ha) + yield = (crop_N * 1000) / cropland_area, + + # Nitrogen Use Efficiency (%) + NUE = (crop_N / total_inputs) * 100 + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = NUE, + y = yield, + color = year + ) + ) + + ggplot2::geom_point(size = 3) + + ggplot2::scale_color_viridis_c(option = "plasma") + + ggplot2::labs( + x = "NUE (%)", + y = "Crop productivity (kgN/ha)", + color = "Year", + title = "Crop productivity (intensification) and crop NUE in Spain (1860-2020)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/yield_nue_trajectory.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + + +intens_traj_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Cropland area ---- + cropland_area <- npp_ygpit |> + dplyr::filter(landuse == "Cropland") |> + dplyr::group_by(year) |> + dplyr::summarise( + cropland_area = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Crop production (N) ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Synthetic fertilizer ---- + fertilizer_input <- flows |> + dplyr::filter( + origin == "Synthetic", + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + fertilizer_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine ---- + df <- cropland_area |> + dplyr::rename(year_dup = year) | + dplyr::left_join(crop_production, by = "year") |> + dplyr::left_join(fertilizer_input, by = "year") |> + dplyr::mutate( + # Land productivity (kg N / ha) + land_productivity = (crop_N * 1000) / cropland_area, + + # Nitrogen intensity (kg N / ha) + nitrogen_intensity = (fertilizer_N * 1000) / cropland_area + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = nitrogen_intensity, + y = land_productivity, + color = year + ) + ) + + ggplot2::geom_point(size = 3) + + ggplot2::scale_color_viridis_c(option = "plasma") + + ggplot2::labs( + x = "Nitrogen input intensity (kg N / ha)", + y = "Land productivity (kg N / ha)", + color = "Year", + title = "Agricultural intensification in Spain (1860-2020)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/intensification_trajectory.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + +intens_ts_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + # ---- Cropland area ---- + cropland_area <- npp_ygpit |> + dplyr::filter(landuse == "Cropland") |> + dplyr::group_by(year) |> + dplyr::summarise( + cropland_area = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Crop production ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Synthetic fertilizer ---- + fertilizer_input <- flows |> + dplyr::filter( + origin == "Synthetic", + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + fertilizer_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine ---- + df <- cropland_area |> + dplyr::rename(year_dup = year) | + dplyr::left_join(crop_production, by = "year") |> + dplyr::left_join(fertilizer_input, by = "year") |> + dplyr::mutate( + land_productivity = (crop_N * 1000) / cropland_area, + nitrogen_intensity = (fertilizer_N * 1000) / cropland_area + ) + + # ---- Convert to long format ---- + df_long <- df |> + tidyr::pivot_longer( + cols = c(land_productivity, nitrogen_intensity), + names_to = "indicator", + values_to = "value" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df_long, + ggplot2::aes( + x = year, + y = value, + color = indicator + ) + ) + + ggplot2::geom_line(size = 1.3) + + ggplot2::scale_color_manual( + values = c( + land_productivity = "#1b9e77", + nitrogen_intensity = "#d95f02" + ), + labels = c( + "Land productivity", + "Nitrogen input intensity" + ) + ) + + ggplot2::labs( + x = NULL, + y = "kg N / ha", + color = "Indicator", + title = "N inputs and crop productivity in Spain (1860-2020)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold"), + legend.position = "right" + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/intensification_timeseries.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + +nue_fertilizer_timeseries_plot <- function() { + flows <- create_n_prov_destiny() + + # ---- Crop production ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Soil inputs ---- + inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Livestock", + "People", + "Fixation", + "Deposition" + ), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + + synthetic = sum(mg_n[origin == "Synthetic"], na.rm = TRUE), + + organic = sum( + mg_n[origin %in% c("Livestock", "People")], + na.rm = TRUE + ), + + fixation_dep = sum( + mg_n[origin %in% c("Fixation", "Deposition")], + na.rm = TRUE + ), + + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- crop_production |> + dplyr::left_join(inputs, by = "year") |> + dplyr::mutate( + NUE = (crop_N / total_inputs) * 100, + + synthetic_share = (synthetic / total_inputs) * 100, + + organic_share = (organic / total_inputs) * 100, + + fertilizer_dependency = ((synthetic + organic) / total_inputs) * 100 + ) + + # ---- Long format ---- + df_long <- df |> + tidyr::pivot_longer( + cols = c( + synthetic_share, + organic_share, + fertilizer_dependency + ), + names_to = "indicator", + values_to = "value" + ) + + # ---- Order ---- + df_long$indicator <- factor( + df_long$indicator, + levels = c( + "fertilizer_dependency", + "synthetic_share", + "organic_share" + ) + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df_long, + ggplot2::aes(x = year, y = value, color = indicator) + ) + + ggplot2::geom_line(size = 1.3) + + ggplot2::scale_color_manual( + values = c( + fertilizer_dependency = "#e7298a", + synthetic_share = "#d95f02", + organic_share = "#1b9e77" + ), + labels = c( + fertilizer_dependency = "Fertilizer dependency", + synthetic_share = "Mineral fertilizer share", + organic_share = "Organic fertilizer share" + ) + ) + + ggplot2::labs( + x = NULL, + y = "", + color = "Indicator", + title = "Fertilizer N dependency in Spanish cropland (1860-2021)" + ) + + ggplot2::scale_y_continuous( + labels = scales::label_percent(scale = 1), + limits = c(0, 100) + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + ggplot2::ggsave( + filename = "C:/PhD/Typologies/Typologies_spain/typology_plot/nue_fertilizer_time.jpeg", + plot = p, + width = 12, + height = 8, + dpi = 300 + ) + + list(plot = p, data = df) +} + +nue_fertilizer_trajectory_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + + # ---- Crop production (N output) ---- + crop_production <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Soil N inputs ---- + inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Livestock", + "People", + "Fixation", + "Deposition" + ), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + + fertilizer = sum( + mg_n[origin %in% c("Synthetic", "Livestock", "People")], + na.rm = TRUE + ), + + .groups = "drop" + ) + + # ---- Combine indicators ---- + df <- crop_production |> + dplyr::left_join(inputs, by = "year") |> + dplyr::mutate( + # Nitrogen use efficiency + NUE = (crop_N / total_inputs) * 100, + + # Fertilizer dependency + fertilizer_dependency = (fertilizer / total_inputs) * 100 + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = NUE, + y = fertilizer_dependency, + color = year + ) + ) + + ggplot2::geom_point(size = 3) + + ggplot2::scale_color_viridis_c(option = "plasma") + + ggplot2::labs( + x = "N use efficiency", + y = "Fertilizer dependency", + color = "Year", + title = "NUE and fertilizer dependency trajectory in Spanish cropland (1860-2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + # ---- Save plot ---- + ggplot2::ggsave( + filename = "C:/PhD/Typologies/Typologies_spain/typology_plot/nue_fertilizer_dependency.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + + +nrr_cropland_timeseries_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + + # ---- Crop production (N output) ---- + crop_outputs <- flows |> + dplyr::filter( + origin == "Cropland", + destiny %in% + c( + "population_food", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_output = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- N soil inputs ---- + crop_inputs <- flows |> + dplyr::filter( + origin %in% + c( + "Synthetic", + "Livestock", + "People", + "Fixation", + "Deposition" + ), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + crop_input = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Recycled N (manure + urban) ---- + recycled_n <- flows |> + dplyr::filter( + origin %in% c("Livestock", "People"), + destiny == "Cropland" + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + recycled = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Combine ---- + df <- crop_inputs |> + dplyr::left_join(crop_outputs, by = "year") |> + dplyr::left_join(recycled_n, by = "year") |> + dplyr::mutate( + crop_output = dplyr::coalesce(crop_output, 0), + recycled = dplyr::coalesce(recycled, 0), + + surplus = crop_input - crop_output, + + NRR = recycled / (recycled + surplus) * 100 + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = year, + y = NRR + ) + ) + + ggplot2::geom_line( + color = "#1b9e77", + size = 1.3 + ) + + ggplot2::labs( + x = NULL, + y = "", + title = "N recycling rate (NRR) in Spanish cropland (1860-2021)" + ) + + ggplot2::scale_y_continuous( + labels = scales::label_percent(scale = 1), + limits = c(0, 100) + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + # ---- Save ---- + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/nrr_cropland_timeseries.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + + +production_diversity_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + + items <- readxl::read_excel( + "C:/PhD/GRAFS/Inputs_SACO/Codes_coefs.xlsx", + sheet = "Names_biomass_CB" + ) + + df <- dplyr::left_join( + flows, + items, + by = c("item" = "Name_biomass") + ) + + df <- df |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ), + origin != "Outside" + ) + + prod_group <- df |> + dplyr::group_by(year, province_name, item) |> + dplyr::summarise( + value = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::filter(value > 0) + + # ---- Shannon index ---- + shannon_index <- function(x) { + p <- x / sum(x) + p <- p[p > 0] + -sum(p * log(p)) + } + + diversity <- prod_group |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + H = shannon_index(value), + n_groups = dplyr::n(), + H_norm = dplyr::if_else(n_groups > 1, H / log(n_groups), NA_real_), + .groups = "drop" + ) + + diversity_ts <- diversity |> + dplyr::group_by(year) |> + dplyr::summarise( + diversity = mean(H_norm, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + diversity_ts, + ggplot2::aes(x = year, y = diversity) + ) + + ggplot2::geom_line( + color = "#1b9e77", + size = 1.3 + ) + + ggplot2::labs( + x = NULL, + y = "Shannon index", + title = "Production diversity (Shannon index) in Spain (1860-2021)" + ) + + ggplot2::scale_y_continuous(limits = c(0, 1)) + + ggplot2::theme_minimal() + + print(p) + + list(plot = p, data = diversity_ts) +} + +intens_spec_sec_axis <- function() { + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + + items <- readxl::read_excel( + "C:/PhD/GRAFS/Inputs_SACO/Codes_coefs.xlsx", + sheet = "Names_biomass_CB" + ) + + df_prod <- flows |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ), + origin != "Outside" + ) + + prod_group <- df_prod |> + dplyr::group_by(year, province_name, item) |> + dplyr::summarise( + value = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::filter(value > 0) + + shannon_index <- function(x) { + p <- x / sum(x) + p <- p[p > 0] + -sum(p * log(p)) + } + + specialization_ts <- prod_group |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + H = shannon_index(value), + n_groups = dplyr::n(), + H_norm = dplyr::if_else(n_groups > 1, H / log(n_groups), NA_real_), + .groups = "drop" + ) |> + dplyr::mutate( + specialization = 1 - H_norm + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + specialization = mean(specialization, na.rm = TRUE), + .groups = "drop" + ) + + area_df <- npp_ygpit |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + intensification_ts <- flows |> + dplyr::filter( + origin %in% c("Synthetic", "Deposition", "Fixation"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + total_input_mg = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::left_join(area_df, by = c("year", "province_name")) |> + dplyr::mutate( + intensification = (total_input_mg * 1000) / area_ha + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + intensification = mean(intensification, na.rm = TRUE), + .groups = "drop" + ) + + df <- intensification_ts |> + dplyr::left_join(specialization_ts, by = "year") + + scale_factor <- max(df$intensification, na.rm = TRUE) / + max(df$specialization, na.rm = TRUE) + + p <- ggplot2::ggplot(df, ggplot2::aes(x = year)) + + ggplot2::geom_line( + ggplot2::aes(y = intensification, color = "Intensification"), + size = 1.3 + ) + + ggplot2::geom_line( + ggplot2::aes(y = specialization * scale_factor, color = "Specialization"), + size = 1.3 + ) + + ggplot2::scale_y_continuous( + name = "Intensification (kg N / ha)", + sec.axis = ggplot2::sec_axis( + ~ . / scale_factor, + name = "Specialization (1 - Shannon)" + ) + ) + + ggplot2::scale_color_manual( + values = c( + "Intensification" = "#e41a1c", + "Specialization" = "#377eb8" + ) + ) + + ggplot2::labs( + x = NULL, + color = "Indicator", + title = "Intensification and production specialization in Spain (1860-2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + ggplot2::ggsave( + "C:/PhD/Typologies/Typologies_spain/typology_plot/intensification_specialization_secondary.jpeg", + plot = p, + width = 10, + height = 7, + dpi = 300 + ) + + list(plot = p, data = df) +} + +#' indicators by Josettes and Gilles paper +n_indicators_ts_plot <- function() { + flows <- create_n_nat_destiny() + + soil_inputs <- flows |> + dplyr::filter( + origin %in% + c("Deposition", "Fixation", "Synthetic", "Livestock", "People"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + total_inputs = sum(mg_n, na.rm = TRUE), + synthetic = sum(mg_n[origin == "Synthetic"], na.rm = TRUE), + recycled = sum( + mg_n[origin %in% c("Livestock", "People")], + na.rm = TRUE + ), + .groups = "drop" + ) + + land_production <- flows |> + dplyr::filter( + origin %in% c("Cropland", "semi_natural_agroecosystems"), + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + land_prod = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + food_ss <- flows |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono" + ) + ) |> + dplyr::group_by(year) |> + dplyr::summarise( + local_consumed = sum(mg_n[origin != "Outside"], na.rm = TRUE), + total_consumed = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + df <- soil_inputs |> + dplyr::left_join(land_production, by = "year") |> + dplyr::left_join(food_ss, by = "year") |> + dplyr::mutate( + nue = land_prod / total_inputs * 100, + circularity = recycled / total_inputs * 100, + synth_share = synthetic / total_inputs * 100, + food_self_suf = local_consumed / total_consumed * 100 + ) |> + dplyr::select(year, nue, circularity, synth_share, food_self_suf) + + indicator_labels <- c( + nue = "System NUE", + circularity = "N-Circularity (recycled share)", + synth_share = "Synthetic fertilizer share", + food_self_suf = "Food Self-Sufficiency" + ) + + indicator_colors <- c( + "System NUE" = "#1b9e77", + "N-Circularity (recycled share)" = "#7570b3", + "Synthetic fertilizer share" = "#d95f02", + "Food Self-Sufficiency" = "#e7298a" + ) + + df_long <- df |> + tidyr::pivot_longer( + cols = -year, + names_to = "indicator", + values_to = "value" + ) |> + dplyr::mutate( + indicator = dplyr::recode(indicator, !!!indicator_labels), + indicator = factor(indicator, levels = indicator_labels) + ) + + p <- ggplot2::ggplot( + df_long, + ggplot2::aes(x = year, y = value, color = indicator) + ) + + ggplot2::geom_line(linewidth = 1.3) + + ggplot2::scale_color_manual(values = indicator_colors) + + ggplot2::scale_y_continuous( + labels = scales::label_percent(scale = 1), + limits = c(0, 100) + ) + + ggplot2::labs( + x = NULL, + y = NULL, + color = "Indicator", + title = "Nitrogen system indicators for Spain (1860–2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold"), + legend.position = "right" + ) + + print(p) + + list(plot = p, data = df) +} + +spatial_diversity <- function() { + flows <- create_n_prov_destiny() + + items <- readxl::read_excel( + "C:/PhD/GRAFS/Inputs_SACO/Codes_coefs.xlsx", + sheet = "Names_biomass_CB" + ) + + df <- flows |> + dplyr::left_join(items, by = c("item" = "Name_biomass")) |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ), + origin != "Outside", + !is.na(Cat_1) + ) + + df_sum <- df |> + dplyr::group_by(year, province_name, Cat_1) |> + dplyr::summarise( + value = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + shannon_index <- function(x) { + p <- x / sum(x) + p <- p[p > 0] + -sum(p * log(p)) + } + + diversity <- df_sum |> + dplyr::group_by(year) |> + dplyr::summarise( + H = shannon_index(value), + n = dplyr::n(), + H_norm = ifelse(n > 1, H / log(n), NA_real_), + .groups = "drop" + ) + + p <- ggplot2::ggplot( + diversity, + ggplot2::aes(x = year, y = H_norm) + ) + + ggplot2::geom_line(color = "#1b9e77", size = 1.3) + + ggplot2::labs( + x = NULL, + y = "Spatial diversity (Shannon index)", + title = "Spatial diversity of agricultural production in Spain" + ) + + ggplot2::theme_minimal() + + print(p) + + list(plot = p, data = diversity) +} + + +spec_hhi_prod_plot <- function() { + # ---- Load data ---- + flows <- create_n_prov_destiny() + + # ---- Select production-related flows ---- + spec_df <- flows |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ), + box %in% + c( + "Cropland", + "Livestock", + "semi_natural_agroecosystems" + ) + ) |> + dplyr::group_by(year, province_name, box) |> + dplyr::summarise( + value = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::filter(value > 0) + + # ---- Calculate shares ---- + spec_df <- spec_df |> + dplyr::group_by(year, province_name) |> + dplyr::mutate( + share = value / sum(value, na.rm = TRUE) + ) + + # ---- Calculate HHI ---- + spec_index <- spec_df |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + HHI = sum(share^2, na.rm = TRUE), + .groups = "drop" + ) + + # ---- National average ---- + spec_ts <- spec_index |> + dplyr::group_by(year) |> + dplyr::summarise( + HHI = mean(HHI, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + spec_ts, + ggplot2::aes(x = year, y = HHI) + ) + + ggplot2::geom_line( + color = "#377eb8", + size = 1.3 + ) + + ggplot2::scale_y_continuous(limits = c(0, 1)) + + ggplot2::labs( + x = NULL, + y = "HHI index", + title = "HHI index of N production in cropland, livestock, semi-natural agroecosystems in Spain (1860-2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p) + + list(plot = p, data = spec_ts) +} + +system_shares_plot <- function() { + flows <- create_n_prov_destiny() + + df <- flows |> + dplyr::filter( + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_rum", + "livestock_mono", + "export" + ), + box %in% + c( + "Cropland", + "Livestock", + "semi_natural_agroecosystems" + ) + ) |> + dplyr::group_by(year, province_name, box) |> + dplyr::summarise( + value = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::group_by(year, province_name) |> + dplyr::mutate( + share = value / sum(value, na.rm = TRUE) + ) + + # ---- National average ---- + df_ts <- df |> + dplyr::group_by(year, box) |> + dplyr::summarise( + share = mean(share, na.rm = TRUE), + .groups = "drop" + ) + + # ---- Plot ---- + p <- ggplot2::ggplot( + df_ts, + ggplot2::aes( + x = year, + y = share, + fill = box + ) + ) + + ggplot2::geom_area() + + ggplot2::scale_fill_manual( + values = c( + "semi_natural_agroecosystems" = "#66a61e", + "Livestock" = "#b3001b", + "Cropland" = "#F7DD5A" + ) + ) + + ggplot2::labs( + x = NULL, + y = "", + fill = "System", + title = "Relative contribution of production in cropland, livestock and semi-natural agroecosystems in Spain (1860-2021)" + ) + + ggplot2::scale_y_continuous( + labels = scales::percent_format(), + limits = c(0, 1) + ) + + ggplot2::theme_minimal() + + print(p) + + list(plot = p, data = df_ts) +} + + +ext_dep_plot_national <- function() { + # ---- Load national data ---- + flows <- create_n_nat_destiny() + + inputs <- flows |> + dplyr::filter( + # ---- Soil inputs ---- + (destiny %in% + c("Cropland", "semi_natural_agroecosystems") & + origin %in% + c("Synthetic", "Fixation", "Deposition", "Livestock", "People")) | + + # ---- Livestock feed and population food ---- + (destiny %in% + c( + "livestock_mono", + "livestock_rum", + "population_food", + "population_other_uses" + ) & + origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside")) + ) |> + + # ---- Classify inputs ---- + dplyr::mutate( + input_type = dplyr::case_when( + # External + origin %in% + c("Synthetic", "Fixation", "Deposition") & + destiny %in% + c("Cropland", "semi_natural_agroecosystems") ~ + "external", + + origin == "Outside" & + destiny %in% + c( + "livestock_mono", + "livestock_rum", + "population_food", + "population_other_uses" + ) ~ + "external", + + # Internal + origin %in% + c("Livestock", "People") & + destiny %in% + c("Cropland", "semi_natural_agroecosystems") ~ + "internal", + + origin %in% + c("Cropland", "semi_natural_agroecosystems") & + destiny %in% + c( + "livestock_mono", + "livestock_rum", + "population_food", + "population_other_uses" + ) ~ + "internal", + + TRUE ~ NA_character_ + ) + ) |> + dplyr::filter(!is.na(input_type)) + + # ---- Aggregate ---- + df <- inputs |> + dplyr::group_by(year, input_type) |> + dplyr::summarise( + value_MgN = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + tidyr::pivot_wider( + names_from = input_type, + values_from = value_MgN, + values_fill = 0 + ) + + df <- df |> + dplyr::mutate( + external_GgN = external / 1000, + internal_GgN = internal / 1000, + total_GgN = external_GgN + internal_GgN, + external_dependency = external_GgN / total_GgN + ) + + # ---- Plot 1: Dependency ---- + p1 <- ggplot2::ggplot( + df, + ggplot2::aes(x = year, y = external_dependency) + ) + + ggplot2::geom_line( + linewidth = 1.4, + color = "#d95f02" + ) + + ggplot2::scale_y_continuous( + labels = scales::percent_format(), + limits = c(0, 1) + ) + + ggplot2::labs( + x = NULL, + y = "External N dependency", + title = "Dependence on external nitrogen inputs in Spain (1860-2021)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p1) + + # ---- Plot 2: Composition ---- + df_long <- df |> + dplyr::select(year, external_GgN, internal_GgN) |> + dplyr::mutate( + total = external_GgN + internal_GgN, + external_share = external_GgN / total * 100, + internal_share = internal_GgN / total * 100 + ) |> + dplyr::filter(is.finite(external_share), is.finite(internal_share)) |> + dplyr::select(year, external_share, internal_share) |> + tidyr::pivot_longer( + cols = c(external_share, internal_share), + names_to = "type", + values_to = "value" + ) |> + dplyr::mutate( + type = dplyr::recode( + type, + external_share = "External inputs", + internal_share = "Internal recycling" + ) + ) + + p2 <- ggplot2::ggplot( + df_long, + ggplot2::aes(x = year, y = value, fill = type) + ) + + ggplot2::geom_area(alpha = 0.9, na.rm = TRUE) + + ggplot2::scale_fill_manual( + values = c( + "External inputs" = "#d95f02", + "Internal recycling" = "#1b9e77" + ) + ) + + ggplot2::scale_y_continuous( + labels = scales::label_percent(scale = 1), + limits = c(0, 100) + ) + + ggplot2::labs( + x = NULL, + y = "Share of N inputs", + fill = "Input type", + title = "Composition of nitrogen inputs in Spain" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + plot.title = ggplot2::element_text(face = "bold") + ) + + print(p2) + + list( + dependency_plot = p1, + composition_plot = p2, + data = df + ) +} diff --git a/R/land_use_typology_panel.R b/R/land_use_typology_panel.R new file mode 100644 index 00000000..21d4f7d7 --- /dev/null +++ b/R/land_use_typology_panel.R @@ -0,0 +1,350 @@ +province_landuse_n_inputs_plot <- function(year_plot = 1980) { + n_flows <- create_n_prov_destiny() + area <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + indicators <- create_typologies_spain(make_map = FALSE) + + inputs <- n_flows |> + dplyr::filter( + year == year_plot, + origin %in% + c( + "Deposition", + "Fixation", + "Synthetic", + "Livestock", + "People" + ), + destiny %in% + c( + "Cropland", + "semi_natural_agroecosystems" + ) + ) |> + dplyr::mutate( + Input = dplyr::case_when( + origin == "Synthetic" ~ "Synthetic_fertilizer", + origin == "Livestock" ~ "Manure", + origin == "People" ~ "Urban", + TRUE ~ origin + ), + LandUse = dplyr::if_else( + destiny == "Cropland", + "Cropland", + "Semi_natural" + ) + ) |> + dplyr::group_by( + province_name, + LandUse, + Input + ) |> + dplyr::summarise( + MgN = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + areas <- area |> + dplyr::filter(year == year_plot) |> + dplyr::mutate( + LandUse = dplyr::if_else( + landuse == "Cropland", + "Cropland", + "Semi_natural" + ) + ) |> + dplyr::group_by(province_name, LandUse) |> + dplyr::summarise( + Area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + df <- inputs |> + dplyr::left_join(areas, by = c("province_name", "LandUse")) + + df_grouped <- df |> + dplyr::group_by(province_name, LandUse, Input) |> + dplyr::summarise( + MgN = sum(MgN), + Area_ha = sum(Area_ha), + .groups = "drop" + ) |> + dplyr::mutate( + kgN_ha = (MgN * 1000) / Area_ha + ) + + df_total <- df |> + dplyr::group_by(province_name, Input) |> + dplyr::summarise( + MgN = sum(MgN), + .groups = "drop" + ) |> + dplyr::left_join( + areas |> + dplyr::group_by(province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ha), + .groups = "drop" + ), + by = "province_name" + ) |> + dplyr::mutate( + LandUse = "Total", + kgN_ha = (MgN * 1000) / Area_ha + ) + + plot_data <- dplyr::bind_rows(df_grouped, df_total) + + typologies <- indicators |> + dplyr::filter(year == year_plot) |> + dplyr::select(province_name, Typology) |> + dplyr::mutate( + Typology = stringr::str_remove( + Typology, + " \\(intensive\\)| \\(extensive\\)" + ) + ) + + plot_data <- plot_data |> + dplyr::left_join(typologies, by = "province_name") + + plot_data$province_name <- factor( + plot_data$province_name, + levels = sort(unique(plot_data$province_name)) + ) + + plot_data$LandUse <- factor( + plot_data$LandUse, + levels = c("Cropland", "Semi_natural", "Total") + ) + + input_colors <- c( + Synthetic_fertilizer = "red4", + Manure = "darkorange3", + Urban = "darkorange4", + Fixation = "olivedrab4", + Deposition = "gray40" + ) + + typology_list <- unique(plot_data$Typology) + + plots <- lapply(typology_list, function(t) { + ggplot2::ggplot( + plot_data |> + dplyr::filter(Typology == t), + ggplot2::aes( + x = LandUse, + y = kgN_ha, + fill = Input + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::facet_wrap(~Province_name, nrow = 1) + + ggplot2::scale_fill_manual(values = input_colors) + + ggplot2::labs( + title = paste( + "N inputs per hectare by province -", + t, + "(", + year_plot, + ")" + ), + x = "Land_use", + y = "kg N ha^-1" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + strip.text = ggplot2::element_text( + size = 10, + face = "bold" + ), + panel.spacing = grid::unit(1.2, "lines"), + axis.text.x = ggplot2::element_text( + angle = 30, + hjust = 1 + ) + ) + }) + + names(plots) <- typology_list + + plots +} + + +prov_all_lu_n_inputs_plot <- function(year_plot = 1980) { + n_balance <- whep_read_file("n_balance_ygpit_all") |> + dplyr::rename_with(tolower) + area <- whep_read_file("npp_ygpit") |> dplyr::rename_with(tolower) + indicators <- create_typologies_spain(make_map = FALSE) + + inputs <- n_balance |> + dplyr::filter(year == year_plot) |> + dplyr::mutate( + LandUse = dplyr::case_when( + landuse %in% c("Forest_high", "Forest_low") ~ "Forest", + TRUE ~ landuse + ) + ) |> + dplyr::group_by(province_name, LandUse) |> + dplyr::summarise( + Deposition = sum(deposition, na.rm = TRUE), + Fixation = sum(bnf, na.rm = TRUE), + Synthetic_fertilizer = sum(synthetic, na.rm = TRUE), + Manure = sum(solid + liquid, na.rm = TRUE), + Urban = sum(urban, na.rm = TRUE), + .groups = "drop" + ) |> + tidyr::pivot_longer( + cols = c( + Deposition, + Fixation, + Synthetic_fertilizer, + Manure, + Urban + ), + names_to = "Input", + values_to = "MgN" + ) + + areas <- area |> + dplyr::filter(year == year_plot) |> + dplyr::mutate( + LandUse = dplyr::case_when( + landuse %in% c("Forest_high", "Forest_low") ~ "Forest", + TRUE ~ landuse + ) + ) |> + dplyr::group_by(province_name, LandUse) |> + dplyr::summarise( + Area_ha = sum(area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + df <- inputs |> + dplyr::left_join( + areas, + by = c("province_name", "LandUse") + ) + + df_grouped <- df |> + dplyr::group_by( + Province_name, + LandUse, + Input + ) |> + dplyr::summarise( + MgN = sum(MgN, na.rm = TRUE), + Area_ha = sum(Area_ha, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + kgN_ha = (MgN * 1000) / Area_ha + ) + + df_total <- df |> + dplyr::group_by( + Province_name, + Input + ) |> + dplyr::summarise( + MgN = sum(MgN, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::left_join( + areas |> + dplyr::group_by(province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ha, na.rm = TRUE), + .groups = "drop" + ), + by = "province_name" + ) |> + dplyr::mutate( + LandUse = "Total", + kgN_ha = (MgN * 1000) / Area_ha + ) + + plot_data <- dplyr::bind_rows( + df_grouped, + df_total + ) + + typologies <- indicators |> + dplyr::filter(year == year_plot) |> + dplyr::select(province_name, Typology) |> + dplyr::mutate( + Typology = stringr::str_remove( + Typology, + " \\(intensive\\)| \\(extensive\\)" + ) + ) + + plot_data <- plot_data |> + dplyr::left_join( + typologies, + by = "province_name" + ) + + plot_data$province_name <- factor( + plot_data$province_name, + levels = sort(unique(plot_data$province_name)) + ) + + plot_data$LandUse <- factor( + plot_data$LandUse, + levels = c( + "Cropland", + "Pasture_Shrubland", + "Dehesa", + "Forest", + "Other", + "Total" + ) + ) + + input_colors <- c( + Synthetic_fertilizer = "red4", + Manure = "darkorange3", + Urban = "darkorange4", + Fixation = "olivedrab4", + Deposition = "gray40" + ) + + typology_list <- unique(plot_data$Typology) + + plots <- lapply(typology_list, function(t) { + ggplot2::ggplot( + plot_data |> + dplyr::filter(Typology == t), + ggplot2::aes( + x = LandUse, + y = kgN_ha, + fill = Input + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::facet_wrap(~Province_name, nrow = 1) + + ggplot2::scale_fill_manual(values = input_colors) + + ggplot2::labs( + title = paste( + "N inputs per hectare by province -", + t, + "(", + year_plot, + ")" + ), + x = "Land use", + y = "kg N ha^-1" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + strip.text = ggplot2::element_text(size = 10, face = "bold"), + panel.spacing = grid::unit(1.2, "lines"), + axis.text.x = ggplot2::element_text(angle = 30, hjust = 1) + ) + }) + + names(plots) <- typology_list + + plots +} diff --git a/R/n_prov_destiny.R b/R/n_prov_destiny.R index ae6556f9..f8980ca7 100644 --- a/R/n_prov_destiny.R +++ b/R/n_prov_destiny.R @@ -10,9 +10,6 @@ #' uses and feed or cropland (in case of N soil inputs). #' Processed items, residues, woody crops, grazed weeds are taken into account. #' -#' @param example If `TRUE`, return a small example output without downloading -#' remote data. Default is `FALSE`. -#' #' @return #' A final tibble containing N flow data by origin and destiny. #' It includes the following columns: @@ -28,31 +25,34 @@ #' - `destiny`: The destiny category of N: population_food, #' population_other_uses, livestock_mono, livestock_rum (feed), export, #' Cropland (for N soil inputs). -#' - `mg_n`: Nitrogen amount in megagrams (Mg). +#' - `MgN`: Nitrogen amount in megagrams (Mg). #' #' @export -#' -#' @examples -#' create_n_prov_destiny(example = TRUE) -create_n_prov_destiny <- function(example = FALSE) { - if (example) { - return(.example_create_n_prov_destiny()) - } +create_n_prov_destiny <- function() { codes_coefs_items_full <- whep_read_file("codes_coefs_items_full") biomass_coefs <- whep_read_file("biomass_coefs") pie_full_destinies_fm <- whep_read_file("pie_full_destinies_fm") processed_prov_fixed <- whep_read_file("processed_prov_fixed") - livestock_prod_ygps <- whep_read_file("livestock_prod_ygps") + livestock_prod_ygps <- whep_read_file("stock_prod_ygps") crop_area_npp_no_fallow <- whep_read_file("crop_area_npp_ygpitr_no_fallow") npp_ygpit <- whep_read_file("npp_ygpit") codes_coefs <- whep_read_file("codes_coefs") intake_ygiac <- whep_read_file("intake_ygiac") population_yg <- whep_read_file("population_yg") - n_balance_ygpit_all <- whep_read_file("n_balance_ygpit_all") + n_balance_ygpit_all <- whep_read_file("n_balance_ygpit_all") |> + dplyr::filter(Year <= 2021) biomass_item_merged <- .merge_items_biomass(npp_ygpit, codes_coefs) n_soil_inputs <- .calculate_n_soil_inputs(n_balance_ygpit_all, codes_coefs) - add_feed_output <- .add_feed(intake_ygiac) + + livestock_product_items <- codes_coefs_items_full |> + dplyr::filter(group %in% c("Livestock products", "Livestock")) |> + dplyr::pull(item) + + add_feed_output <- .add_feed( + intake_ygiac |> + dplyr::filter(!item_cbs %in% livestock_product_items) + ) prod_combined_boxes <- biomass_item_merged |> .aggregate_crop_seminatural( @@ -66,7 +66,7 @@ create_n_prov_destiny <- function(example = FALSE) { .calculate_population_share() |> .calculate_food_and_other_uses(pie_full_destinies_fm) - biomass_item_merged |> + grafs_prod_item_trade <- biomass_item_merged |> .remove_seeds_from_system(pie_full_destinies_fm, prod_combined_boxes) |> .add_grass_wood() |> .prepare_prod_data( @@ -83,21 +83,19 @@ create_n_prov_destiny <- function(example = FALSE) { add_feed_output$feed_share_rum_mono ) |> .add_n_soil_inputs(n_soil_inputs) |> - dplyr::rename_with(tolower) |> - dplyr::rename(mg_n = mgn) |> dplyr::select( - year, - province_name, - item, - irrig_cat, - box, - origin, - destiny, - mg_n + year = Year, + province_name = Province_name, + item = Item, + irrig_cat = Irrig_cat, + box = Box, + origin = Origin, + destiny = Destiny, + mg_n = MgN ) } -#' @title GRAFS Nitrogen (N) flows at Spain national level +#' @title GRAFS Nitrogen (N) flows – National Spain #' #' @description #' Provides N flows of the Spanish agro-food system on a national level @@ -107,39 +105,45 @@ create_n_prov_destiny <- function(example = FALSE) { #' inputs are aggregated nationally before calculating trade with the #' outside. #' -#' @param example If `TRUE`, return a small example output without downloading -#' remote data. Default is `FALSE`. -#' #' @return #' A final tibble containing national N flow data by origin and destiny. -#' It includes the following columns: -#' - `year`: The year in which the recorded event occurred. -#' - `item`: The item which was produced, defined in `names_biomass_cb`. -#' - `irrig_cat`: Irrigation form (irrigated or rainfed) -#' - `box`: One of the GRAFS model systems: cropland, -#' Semi-natural agroecosystems, Livestock, Fish, or Agro-industry. -#' - `origin`: The origin category of N: Cropland, -#' Semi-natural agroecosystems, Livestock, Fish, Agro-industry, Deposition, -#' Fixation, Synthetic, People (waste water), Livestock (manure). -#' - `destiny`: The destiny category of N: population_food, -#' population_other_uses, livestock_mono, livestock_rum (feed), export, -#' Cropland (for N soil inputs). -#' - `mg_n`: Nitrogen amount in megagrams (Mg). -#' - `province_name`: Set to "Spain" for all national-level rows. #' #' @export -#' -#' @examples -#' create_n_nat_destiny(example = TRUE) -create_n_nat_destiny <- function(example = FALSE) { - if (example) { - return(.example_create_n_nat_destiny()) - } - prov <- create_n_prov_destiny() - - nat_shares <- prov |> +create_n_nat_destiny <- function() { + prov <- create_n_prov_destiny() |> + dplyr::rename( + Year = year, + Province_name = province_name, + Item = item, + Irrig_cat = irrig_cat, + Box = box, + Origin = origin, + Destiny = destiny, + MgN = mg_n + ) + + prov_lookup <- prov |> + dplyr::group_by(Item, Box, Irrig_cat) |> + dplyr::summarise(weight = sum(MgN, na.rm = TRUE), .groups = "drop") |> + dplyr::group_by(Item) |> + dplyr::slice_max(weight, n = 1, with_ties = FALSE) |> + dplyr::ungroup() + + nat_production_detail <- prov |> + dplyr::filter(Origin == Box) |> + dplyr::group_by(Year, Item, Box, Irrig_cat) |> + dplyr::summarise(production = sum(MgN, na.rm = TRUE), .groups = "drop") + + nat_production <- nat_production_detail |> + dplyr::group_by(Year, Item) |> + dplyr::summarise( + production = sum(production, na.rm = TRUE), + .groups = "drop" + ) + + nat_consumption <- prov |> dplyr::filter( - destiny %in% + Destiny %in% c( "population_food", "population_other_uses", @@ -147,94 +151,214 @@ create_n_nat_destiny <- function(example = FALSE) { "livestock_mono" ) ) |> - dplyr::group_by(year, item, destiny) |> - dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") |> - dplyr::group_by(year, item) |> - dplyr::mutate(share = mg_n / sum(mg_n, na.rm = TRUE)) |> - dplyr::ungroup() |> - dplyr::select(year, item, destiny, share) - - nat_core <- prov |> - dplyr::filter(origin != "Outside", destiny != "export") |> - dplyr::group_by( - year, - item, - irrig_cat, - box, - origin, - destiny - ) |> + dplyr::group_by(Year, Item, Destiny) |> dplyr::summarise( - mg_n = sum(mg_n, na.rm = TRUE), + consumption = sum(MgN, na.rm = TRUE), .groups = "drop" - ) |> - dplyr::mutate(province_name = "Spain") + ) - nat_balance <- prov |> - dplyr::group_by(year, item, box, irrig_cat) |> + nat_cons_wide <- nat_consumption |> + tidyr::pivot_wider( + names_from = Destiny, + values_from = consumption, + values_fill = 0 + ) + + nat_total_consumption <- nat_consumption |> + dplyr::group_by(Year, Item) |> dplyr::summarise( - production = sum(mg_n[origin == box], na.rm = TRUE), - consumption = sum( - mg_n[ - destiny %in% - c( - "population_food", - "population_other_uses", - "livestock_rum", - "livestock_mono" - ) - ], - na.rm = TRUE - ), + consumption = sum(consumption, na.rm = TRUE), .groups = "drop" - ) |> + ) + + nat_balance <- nat_production |> + dplyr::full_join(nat_total_consumption, by = c("Year", "Item")) |> dplyr::mutate( + production = dplyr::coalesce(production, 0), + consumption = dplyr::coalesce(consumption, 0), export = pmax(production - consumption, 0), - import = pmax(consumption - production, 0), - province_name = "Spain" + import = pmax(consumption - production, 0) + ) + + nat_shares <- nat_cons_wide |> + dplyr::left_join(nat_production, by = c("Year", "Item")) |> + dplyr::mutate( + production = dplyr::coalesce(production, 0), + + food = dplyr::coalesce(population_food, 0), + other = dplyr::coalesce(population_other_uses, 0), + feed_rum = dplyr::coalesce(livestock_rum, 0), + feed_mono = dplyr::coalesce(livestock_mono, 0), + feed = feed_rum + feed_mono, + + demand = food + other + feed, + local = pmin(production, demand), + + food_local = dplyr::if_else(demand > 0, local * (food / demand), 0), + other_local = dplyr::if_else(demand > 0, local * (other / demand), 0), + feed_local = dplyr::if_else(demand > 0, local * (feed / demand), 0), + + food_gap = pmax(food - food_local, 0), + other_gap = pmax(other - other_local, 0), + feed_gap = pmax(feed - feed_local, 0), + + total_gap = food_gap + other_gap + feed_gap, + + share_food = dplyr::if_else(total_gap > 0, food_gap / total_gap, 0), + share_other = dplyr::if_else(total_gap > 0, other_gap / total_gap, 0), + share_feed = dplyr::if_else(total_gap > 0, feed_gap / total_gap, 0), + + share_rum = dplyr::if_else(feed > 0, feed_rum / feed, 0), + share_mono = dplyr::if_else(feed > 0, feed_mono / feed, 0), + + share_feed_rum = share_feed * share_rum, + share_feed_mono = share_feed * share_mono + ) |> + dplyr::select( + Year, + Item, + share_food, + share_other, + share_feed_rum, + share_feed_mono + ) |> + tidyr::pivot_longer( + cols = c( + share_food, + share_other, + share_feed_rum, + share_feed_mono + ), + names_to = "Destiny", + values_to = "share" + ) |> + dplyr::mutate( + Destiny = dplyr::recode( + Destiny, + share_food = "population_food", + share_other = "population_other_uses", + share_feed_rum = "livestock_rum", + share_feed_mono = "livestock_mono" + ) + ) |> + dplyr::ungroup() + + imports <- nat_balance |> + dplyr::filter(import > 0) |> + dplyr::left_join(nat_shares, by = c("Year", "Item")) |> + dplyr::mutate( + share = dplyr::coalesce(share, 0), + MgN = import * share, + Province_name = "Spain", + Origin = "Outside" + ) |> + dplyr::left_join(prov_lookup, by = "Item") |> + dplyr::filter(MgN > 0) |> + dplyr::select( + Year, + Province_name, + Item, + Irrig_cat, + Box, + Origin, + Destiny, + MgN ) + export_shares <- nat_production_detail |> + dplyr::group_by(Year, Item) |> + dplyr::mutate( + total_production = sum(production, na.rm = TRUE), + share = dplyr::if_else( + total_production > 0, + production / total_production, + 0 + ) + ) |> + dplyr::ungroup() |> + dplyr::select(Year, Item, Box, Irrig_cat, share) + exports <- nat_balance |> dplyr::filter(export > 0) |> - dplyr::transmute( - year, - province_name, - item, - irrig_cat, - box, - origin = box, - destiny = "export", - mg_n = export + dplyr::left_join(export_shares, by = c("Year", "Item")) |> + dplyr::mutate( + Province_name = "Spain", + Origin = Box, + Destiny = "export", + MgN = export * dplyr::coalesce(share, 0) + ) |> + dplyr::filter(MgN > 0) |> + dplyr::select( + Year, + Province_name, + Item, + Irrig_cat, + Box, + Origin, + Destiny, + MgN ) - imports <- nat_balance |> - dplyr::filter(import > 0) |> - dplyr::left_join(nat_shares, by = c("year", "item")) |> + nat_soil_inputs <- prov |> + dplyr::filter( + Origin %in% + c("Deposition", "Fixation", "Synthetic", "Livestock", "People"), + Destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(Year, Item, Irrig_cat, Box, Origin, Destiny) |> + dplyr::summarise(MgN = sum(MgN, na.rm = TRUE), .groups = "drop") |> + dplyr::mutate(Province_name = "Spain") + + nat_local_total <- nat_balance |> + dplyr::mutate(local = pmin(production, consumption)) |> + dplyr::select(Year, Item, local) + + nat_destiny_shares <- nat_consumption |> + dplyr::left_join( + nat_total_consumption |> dplyr::rename(total = consumption), + by = c("Year", "Item") + ) |> dplyr::mutate( - mg_n = pmin(import, consumption) * share, - origin = "Outside", - irrig_cat = NA_character_ + destiny_share = dplyr::if_else(total > 0, consumption / total, 0) ) |> - dplyr::filter(mg_n > 0) |> + dplyr::select(Year, Item, Destiny, destiny_share) + + nat_local_detail <- nat_local_total |> + dplyr::left_join(nat_destiny_shares, by = c("Year", "Item")) |> + dplyr::mutate(MgN_local = local * destiny_share) |> + dplyr::left_join(export_shares, by = c("Year", "Item")) |> + dplyr::mutate( + MgN = MgN_local * share, + Origin = Box, + Province_name = "Spain" + ) |> + dplyr::filter(!is.na(Box), MgN > 0) |> dplyr::select( - year, - province_name, - item, - irrig_cat, - box, - origin, - destiny, - mg_n + Year, + Province_name, + Item, + Irrig_cat, + Box, + Origin, + Destiny, + MgN ) - dplyr::bind_rows( - nat_core, - exports, - imports - ) |> - dplyr::arrange(year, item, origin, destiny) + dplyr::bind_rows(nat_local_detail, nat_soil_inputs, exports, imports) |> + dplyr::arrange(Year, Item, Origin, Destiny) |> + dplyr::rename( + year = Year, + province_name = Province_name, + item = Item, + irrig_cat = Irrig_cat, + box = Box, + origin = Origin, + destiny = Destiny, + mg_n = MgN + ) } + #' @title Production of Cropland, Livestock, and Semi-natural agroecosystems #' @description Merge items with biomasses. #' @@ -242,8 +366,8 @@ create_n_nat_destiny <- function(example = FALSE) { #' @param names_biomass_cb Dataframe with biomass names and associated item #' names. #' -#' @return A tibble with npp data merged with item names from the -#' biomass codes. +#' @return A list with two merged dataframes: 'crop_area_npp_merged' and +#' 'npp_ygpit_merged'. #' @keywords internal #' @noRd .merge_items_biomass <- function( @@ -267,6 +391,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' @noRd .summarise_crops_residues <- function(crop_area_npp_ygpitr_no_fallow) { crop_area_npp_prod_residue <- crop_area_npp_ygpitr_no_fallow |> + dplyr::rename(Item = item_cbs) |> dplyr::mutate(LandUse = "Cropland") |> dplyr::rename(prod_type = Product_residue) |> dplyr::group_by( @@ -291,8 +416,6 @@ create_n_nat_destiny <- function(example = FALSE) { #' and grazed grass) ---------------------------------------------------------- #' #' @param npp_ygpit_merged NPP merged data including all biomasses and items. -#' @param crop_area_npp_prod_residue Dataframe with crop production and -#' residues summarised per province and year. #' #' @return A dataframe combining products, residues, and grazed biomass. #' @keywords internal @@ -324,44 +447,38 @@ create_n_nat_destiny <- function(example = FALSE) { Box = "Cropland" ) - semi_natural <- dplyr::bind_rows( - npp_ygpit_merged |> - dplyr::filter(LandUse != "Cropland") |> - dplyr::select( - Year, - Province_name, - Name_biomass, - Item, - LandUse, - Irrig_cat, - production_fm = GrazedWeeds_MgDM - ) |> - dplyr::mutate(prod_type = "Grass"), - npp_ygpit_merged |> - dplyr::filter(LandUse != "Cropland") |> - dplyr::select( - Year, - Province_name, - Name_biomass, - Item, - LandUse, - Irrig_cat, - production_fm = Prod_ygpit_Mg - ) |> - dplyr::mutate(prod_type = "Product"), - npp_ygpit_merged |> - dplyr::filter(LandUse != "Cropland") |> - dplyr::select( - Year, - Province_name, - Name_biomass, - Item, - LandUse, - Irrig_cat, - production_fm = Used_Residue_MgFM - ) |> - dplyr::mutate(prod_type = "Residue") - ) |> + semi_natural <- npp_ygpit_merged |> + dplyr::filter(LandUse != "Cropland") |> + dplyr::mutate( + GrazedWeeds_MgDM = GrazedWeeds_MgDM + + GrazedAcorns_MgDM + + GrazedFodder_MgDM + ) |> + dplyr::select( + Year, + Province_name, + Name_biomass, + Item, + LandUse, + Irrig_cat, + GrazedWeeds_MgDM, + Prod_ygpit_Mg, + Used_Residue_MgFM + ) |> + tidyr::pivot_longer( + cols = c(GrazedWeeds_MgDM, Prod_ygpit_Mg, Used_Residue_MgFM), + names_to = "prod_source", + values_to = "production_fm" + ) |> + dplyr::mutate( + prod_type = dplyr::recode( + prod_source, + GrazedWeeds_MgDM = "Grass", + Prod_ygpit_Mg = "Product", + Used_Residue_MgFM = "Residue" + ) + ) |> + dplyr::select(-prod_source) |> dplyr::mutate(Box = "semi_natural_agroecosystems") combined_biomasses <- dplyr::bind_rows( @@ -382,6 +499,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' @noRd .prepare_livestock_production <- function(livestock_prod_ygps) { livestock <- livestock_prod_ygps |> + dplyr::rename(Item = item_cbs) |> dplyr::select( Year, Province_name, @@ -399,7 +517,9 @@ create_n_nat_destiny <- function(example = FALSE) { #' @title Combine Cropland, Semi_natural_agroecosystems and Livestock ---------- #' -#' @param combined_biomasses Dataframe of crop and semi-natural production. +#' @param combined_biomasses Dataframe of crop production. +#' @param semi_natural_agroecosystems Dataframe of production from semi-natural +#' agroecosystems. #' @param livestock Dataframe of livestock production. #' #' @return Combined dataframe of all production systems. @@ -424,7 +544,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' COMMENT: in a few cases, seeds are higher then production, so that we get #' negative values. When the share is over 50%, it is therefore set back to 50%. #' -#' @param npp_ygpit_merged Dataframe containing crop area by province. +#' @param npp_ygpit_csv Dataframe containing crop area by province. #' @param pie_full_destinies_fm Dataframe containing domestic supply by #' destiny, including seed usage. #' @param grafs_prod_combined Dataframe with total production values. @@ -437,39 +557,43 @@ create_n_nat_destiny <- function(example = FALSE) { pie_full_destinies_fm, grafs_prod_combined ) { - seed_rates <- npp_ygpit_merged |> + cropland_area <- npp_ygpit_merged |> dplyr::filter(LandUse == "Cropland") |> - dplyr::group_by(Year, Province_name, Item) |> dplyr::summarise( Area_ha = sum(Area_ygpit_ha, na.rm = TRUE), - .groups = "drop" + .by = c("Year", "Province_name", "Item") + ) + + seed_reference <- pie_full_destinies_fm |> + dplyr::filter(Element == "Domestic_supply", Destiny == "Seed") |> + dplyr::summarise( + Seed_total = sum(Value_destiny, na.rm = TRUE), + .by = c("Year", "Item") ) |> dplyr::left_join( - pie_full_destinies_fm |> - dplyr::filter(Element == "Domestic_supply", Destiny == "Seed") |> - dplyr::group_by(Year, Item) |> + cropland_area |> dplyr::summarise( - Seed_total = sum(Value_destiny, na.rm = TRUE), - .groups = "drop" - ) |> - dplyr::left_join( - npp_ygpit_merged |> - dplyr::filter(LandUse == "Cropland") |> - dplyr::group_by(Year, Item) |> - dplyr::summarise( - National_area = sum(Area_ygpit_ha, na.rm = TRUE), - .groups = "drop" - ), - by = c("Year", "Item") - ) |> - dplyr::mutate(Seed_rate_per_ha = Seed_total / National_area) |> - dplyr::select(Year, Item, Seed_rate_per_ha), + National_area = sum(Area_ha, na.rm = TRUE), + .by = c("Year", "Item") + ), by = c("Year", "Item") ) |> - dplyr::mutate(Seeds_used_MgFM = Area_ha * Seed_rate_per_ha) |> - tidyr::drop_na() + dplyr::mutate( + Seed_rate_per_ha = dplyr::if_else( + National_area > 0, + Seed_total / National_area, + 0 + ) + ) |> + dplyr::select(Year, Item, Seed_rate_per_ha) - # Substracting the Seed data from Production in grafs_prod_combined + seed_rates <- cropland_area |> + dplyr::left_join(seed_reference, by = c("Year", "Item")) |> + dplyr::mutate( + Seeds_used_MgFM = Area_ha * dplyr::coalesce(Seed_rate_per_ha, 0) + ) + + # Substracting the Seed data from Production in grafs_prod_combined. grafs_prod_combined_no_seeds <- grafs_prod_combined |> dplyr::left_join( seed_rates |> @@ -477,10 +601,11 @@ create_n_nat_destiny <- function(example = FALSE) { by = c("Year", "Province_name", "Item") ) |> dplyr::mutate( + Seeds_used_MgFM = dplyr::coalesce(Seeds_used_MgFM, 0), Seeds_used_capped = dplyr::if_else( - dplyr::coalesce(Seeds_used_MgFM, 0) > 0.5 * production_fm, + Seeds_used_MgFM > 0.5 * production_fm, 0.5 * production_fm, - dplyr::coalesce(Seeds_used_MgFM, 0) + Seeds_used_MgFM ), production_fm = production_fm - Seeds_used_capped ) |> @@ -668,17 +793,30 @@ create_n_nat_destiny <- function(example = FALSE) { ) ) - prod_grazed_no_seeds_dm <- grazed_no_seeds_primary |> + prod_grazed_no_seeds_n <- grazed_no_seeds_primary |> dplyr::left_join( biomass_coefs |> dplyr::select( Name_biomass, Product_kgDM_kgFM, - Residue_kgDM_kgFM + Product_kgN_kgDM, + Residue_kgDM_kgFM, + Residue_kgN_kgDM ), by = c("Biomass_match" = "Name_biomass") ) |> dplyr::mutate( + # Some residues (e.g. Straw) can miss residue-specific coefficients. + # In that case, fall back to product coefficients to avoid dropping + # production to zero. + Residue_kgDM_kgFM = dplyr::coalesce( + Residue_kgDM_kgFM, + Product_kgDM_kgFM + ), + Residue_kgN_kgDM = dplyr::coalesce( + Residue_kgN_kgDM, + Product_kgN_kgDM + ), conversion_dm = dplyr::if_else( prod_type %in% c( @@ -688,17 +826,7 @@ create_n_nat_destiny <- function(example = FALSE) { Residue_kgDM_kgFM, Product_kgDM_kgFM ), - production_dm = production_fm * conversion_dm - ) - - prod_grazed_no_seeds_n <- prod_grazed_no_seeds_dm |> - dplyr::left_join( - biomass_coefs |> - dplyr::select(Name_biomass, Product_kgN_kgDM, Residue_kgN_kgDM), - by = c("Biomass_match" = "Name_biomass") - ) |> - dplyr::mutate( - conversion_n = dplyr::if_else( + conversion_n_dm = dplyr::if_else( prod_type %in% c( "Residue", @@ -707,7 +835,7 @@ create_n_nat_destiny <- function(example = FALSE) { Residue_kgN_kgDM, Product_kgN_kgDM ), - production_n = production_dm * conversion_n + production_n = production_fm * conversion_dm * conversion_n_dm ) |> dplyr::select(-Name_biomass) |> dplyr::select( @@ -761,10 +889,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' #' @param feed_intake A dataframe with feed intake data in FM. #' -#' @return A list with two elements: `feed_intake` (a tibble with total -#' feed and pet food per year, province, and item) and -#' `feed_share_rum_mono` (a tibble with ruminant and monogastric feed -#' shares). +#' @return A dataframe with the total FM_Mg per year, province, and item. #' @keywords internal #' @noRd .add_feed <- function(feed_intake) { @@ -782,26 +907,34 @@ create_n_nat_destiny <- function(example = FALSE) { ) ~ "ruminant", Livestock_cat %in% - c("Pigs", "Poultry", "Rabbits", "Fur animals", "Other") ~ + c( + "Pigs", + "Poultry", + "Rabbits", + "Fur animals", + "Other", + "Other_birds" + ) ~ "monogastric", Livestock_cat == "Pets" ~ "pets", - Livestock_cat == "Aquaculture" ~ "aquaculture", - TRUE ~ "other" + TRUE ~ NA_character_ ) ) |> dplyr::summarise( - feed_amount = sum(FM_Mg, na.rm = TRUE), - .by = c("Year", "Province_name", "Item", "Livestock_type") + feed_amount = sum(intake_MgFM, na.rm = TRUE), + .by = c("Year", "Province_name", "item_cbs", "Livestock_type") ) |> tidyr::pivot_wider( names_from = Livestock_type, values_from = feed_amount, values_fill = 0 ) |> + .ensure_livestock_cols() |> dplyr::mutate( - # TODO: check if aquaculture belongs here - # aquaculture was already in feed before these changes, but "magically" - feed = ruminant + monogastric + aquaculture, + ruminant = dplyr::coalesce(ruminant, 0), + monogastric = dplyr::coalesce(monogastric, 0), + pets = dplyr::coalesce(pets, 0), + feed = ruminant + monogastric, food_pets = pets ) @@ -810,13 +943,14 @@ create_n_nat_destiny <- function(example = FALSE) { feed_total = feed, share_rum = dplyr::if_else(feed_total > 0, ruminant / feed_total, 0), share_mono = dplyr::if_else(feed_total > 0, monogastric / feed_total, 0) - # Note: Aquaculture share is implicit (1 - rum - mono) ) list( feed_intake = feed_wide |> + dplyr::rename(Item = item_cbs) |> dplyr::select(Year, Province_name, Item, feed, food_pets), feed_share_rum_mono = feed_share_rum_mono |> + dplyr::rename(Item = item_cbs) |> dplyr::select(Year, Province_name, Item, share_rum, share_mono) ) } @@ -912,6 +1046,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' @return A combined dataframe with food, feed, and other uses. #' @keywords internal #' @noRd +#' .combine_destinies <- function( grafs_prod_item, feed_intake, @@ -948,8 +1083,7 @@ create_n_nat_destiny <- function(example = FALSE) { .groups = "drop" ) - # Feed for pets is assigned to food, therefore we have for example DDGS in - # human consumption + # Feed for pets is assigned to food. grafs_prod_item_combined <- grafs_prod_item_sum |> dplyr::full_join( prov_food_other_uses_clean, @@ -965,15 +1099,21 @@ create_n_nat_destiny <- function(example = FALSE) { ) |> dplyr::mutate( food = dplyr::coalesce(food, 0) + dplyr::coalesce(food_pets, 0), - feed = dplyr::coalesce(feed, 0), other_uses = dplyr::coalesce(other_uses, 0), + feed = dplyr::coalesce(feed, 0), production_n = dplyr::coalesce(production_n, 0), production_total = dplyr::coalesce(production_total, 0) ) |> + dplyr::select(-food_pets) - # calculating production shares to distinguish between consumption of e.g. - # rainfed vs. irrigated crops + # Split consumption proportionally across all Box/Irrig_cat rows by their + # share of total item production. This handles both the irrigated/rainfed + # split within Cropland AND items that span multiple boxes (e.g. Cropland + + # semi_natural). Without this, the non-Cropland rows would each receive the + # full consumption value, causing overcounting that grows with production. + # When production_total = 0 (pure import items), there is only one row so + # production_share = 1 is correct. grafs_prod_item_combined <- grafs_prod_item_combined |> dplyr::mutate( production_share = dplyr::if_else( @@ -985,15 +1125,11 @@ create_n_nat_destiny <- function(example = FALSE) { feed = feed * production_share, other_uses = other_uses * production_share ) |> - dplyr::select( - -production_total, - -production_share - ) + dplyr::select(-production_total, -production_share) grafs_prod_item_combined } - #' @title Finalizing data #' @description Final merging of Item and Name_biomass and converting FM to DM, #' and DM to N. @@ -1008,7 +1144,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' @keywords internal #' @noRd .convert_to_items_n <- function( - grafs_prod_item_combined, + grafs_prod_item_combined = whep_read_file(""), codes_coefs_items_full = whep_read_file("codes_coefs_items_full"), biomass_coefs = whep_read_file("biomass_coefs") ) { @@ -1076,6 +1212,9 @@ create_n_nat_destiny <- function(example = FALSE) { #' Spain. It should be deactivated for provincial analysis #' #' @param grafs_prod_item_n A dataframe with N values (MgN) by destiny. +#' @param pie_full_destinies_fm A data frame with destiny data. +#' @param biomass_coefs A data frame with biomass coefficients. +#' @param codes_coefs_items_full A lookup table with coefficients. #' #' @return A dataframe with consumption, exports, and imports in MgN. #' @keywords internal @@ -1083,10 +1222,14 @@ create_n_nat_destiny <- function(example = FALSE) { .calculate_trade <- function(grafs_prod_item_n) { grafs_prod_item_n |> dplyr::mutate( - consumption = food + other_uses + feed, - net_trade = production_n - consumption, - export = ifelse(net_trade > 0, net_trade, 0), - import = ifelse(net_trade < 0, -net_trade, 0) + food = dplyr::coalesce(food, 0), + other_uses = dplyr::coalesce(other_uses, 0), + feed = dplyr::coalesce(feed, 0), + + demand_total = food + other_uses + feed, + + import = pmax(demand_total - production_n, 0), + export = pmax(production_n - demand_total, 0) ) |> dplyr::select( Year, @@ -1094,10 +1237,10 @@ create_n_nat_destiny <- function(example = FALSE) { Item, Box, Irrig_cat, + production_n, food, other_uses, feed, - production_n, export, import ) @@ -1120,6 +1263,7 @@ create_n_nat_destiny <- function(example = FALSE) { ) |> dplyr::mutate( group = dplyr::recode(group, "Additives" = "Agro-industry"), + Box = dplyr::case_when( Item == "Acorns" ~ "semi_natural_agroecosystems", is.na(Box) & Item == "Fallow" ~ "Cropland", @@ -1128,7 +1272,7 @@ create_n_nat_destiny <- function(example = FALSE) { "Cropland", is.na(Box) & group %in% c("Livestock products", "Livestock") ~ "Livestock", - group %in% c("Agro-industry", "Fish") ~ group, + is.na(Box) & group %in% c("Agro-industry", "Fish") ~ group, TRUE ~ Box ), Irrig_cat = dplyr::if_else(Box == "Cropland", Irrig_cat, NA_character_) @@ -1145,32 +1289,44 @@ create_n_nat_destiny <- function(example = FALSE) { #' other uses, and feed. #' @keywords internal #' @noRd -.calculate_consumption_shares <- function(grafs_prod_destiny_final) { - grafs_prod_destiny_final |> +.calculate_consumption_shares <- function(df) { + df |> dplyr::mutate( - consumption_total = food + other_uses + feed, - food_share = dplyr::if_else( - consumption_total > 0, - food / consumption_total, + demand_total = food + other_uses + feed, + + local_total = pmin(production_n, demand_total), + + food_local = dplyr::if_else( + demand_total > 0, + local_total * (food / demand_total), 0 ), - other_uses_share = dplyr::if_else( - consumption_total > 0, - other_uses / consumption_total, + other_local = dplyr::if_else( + demand_total > 0, + local_total * (other_uses / demand_total), 0 ), - feed_share = dplyr::if_else( - consumption_total > 0, - feed / consumption_total, + feed_local = dplyr::if_else( + demand_total > 0, + local_total * (feed / demand_total), 0 - ) + ), + + food_share = dplyr::if_else(local_total > 0, food_local / local_total, 0), + other_uses_share = dplyr::if_else( + local_total > 0, + other_local / local_total, + 0 + ), + feed_share = dplyr::if_else(local_total > 0, feed_local / local_total, 0) ) |> dplyr::select( Year, Province_name, Item, + Box, Irrig_cat, - consumption_total, + local_total, food_share, other_uses_share, feed_share @@ -1178,9 +1334,9 @@ create_n_nat_destiny <- function(example = FALSE) { } #' @title Split local consumption -#' @description Splits local consumption into population food, other uses, -#' and livestock. Livestock feed is split into livestock_rum (ruminants) and -#' livestock_mono (monogastric). +#' @description Splits local consumption proportionally according to demand +#' shares (food, other uses, feed). Feed is further split into +#' livestock_rum and livestock_mono. #' @param local_vs_import A dataset containing local and imported consumption. #' @param feed_share_rum_mono A dataset with feed shares between ruminants #' and monogastric animals. @@ -1195,12 +1351,46 @@ create_n_nat_destiny <- function(example = FALSE) { by = c("Year", "Province_name", "Item") ) |> dplyr::mutate( - population_food = local_consumption * food_share, - population_other_uses = local_consumption * other_uses_share, - livestock_rum = local_consumption * feed_share * share_rum, - livestock_mono = local_consumption * feed_share * share_mono, + share_rum = dplyr::coalesce(share_rum, 0), + share_mono = dplyr::coalesce(share_mono, 0), + share_total = share_rum + share_mono, + share_rum = dplyr::if_else(is.na(share_rum), 0, share_rum), + share_mono = dplyr::if_else(is.na(share_mono), 0, share_mono), + + local_food_raw = local_consumption * food_share, + local_other_raw = local_consumption * other_uses_share, + local_feed_raw = local_consumption * feed_share, + + total_local_alloc = local_food_raw + local_other_raw + local_feed_raw, + + scale_factor = dplyr::if_else( + total_local_alloc > local_consumption & total_local_alloc > 0, + local_consumption / total_local_alloc, + 1 + ), + + local_food = local_food_raw * scale_factor, + local_other_uses = local_other_raw * scale_factor, + local_feed = local_feed_raw * scale_factor, + + population_food = local_food, + population_other_uses = local_other_uses, + livestock_rum = local_feed * share_rum, + livestock_mono = local_feed * share_mono, + Origin = Box ) |> + dplyr::select( + -share_total, + -local_food_raw, + -local_other_raw, + -local_feed_raw, + -total_local_alloc, + -scale_factor, + -local_food, + -local_other_uses, + -local_feed + ) |> tidyr::pivot_longer( cols = c( population_food, @@ -1213,17 +1403,12 @@ create_n_nat_destiny <- function(example = FALSE) { ) } + #' @title Split imported consumption -#' @description Splits imports by consumption and assigns origins. -#' Livestock feed is split into livestock_rum (ruminants) and livestock_mono +#' @description Splits imported consumption as the residual demand after local +#' allocation with priority for food, then other uses, then feed. Livestock +#' feed is split into livestock_rum (ruminants) and livestock_mono #' (monogastric). -#' COMMENT: pmin prevents imported N for food and other uses from becoming -#' unrealistically high. -#' For human consumption, imports usually replace local supply instead of -#' adding to it. So I limited imported food and other uses to the smaller -#' value of imports or local use with pmin. Feed is treated differently because -#' imports can exceed local production. Fish and Agro-industry are excluded in -#' pmin because all of these values are considered as imports. #' @param local_vs_import A dataset containing local and import consumption. #' @param feed_share_rum_mono A dataset with feed shares split into ruminants #' and monogastric animals. @@ -1231,27 +1416,57 @@ create_n_nat_destiny <- function(example = FALSE) { #' livestock_rum, livestock_mono, and population_other_uses. #' @keywords internal #' @noRd -.split_import_consumption <- function(local_vs_import, feed_share_rum_mono) { +.split_import_consumption <- function( + local_vs_import, + feed_share_rum_mono, + shares_import_wide +) { local_vs_import |> dplyr::left_join( feed_share_rum_mono, by = c("Year", "Province_name", "Item") ) |> dplyr::mutate( - # Pre-calculate the base amount for food/other (capped or not) - base_amount_food_other = dplyr::if_else( - Box %in% c("Fish", "Agro-industry"), - import_consumption, - pmin(import_consumption, local_consumption) - ), - population_food = base_amount_food_other * food_share, - population_other_uses = base_amount_food_other * other_uses_share, - livestock_rum = import_consumption * feed_share * share_rum, - livestock_mono = import_consumption * feed_share * share_mono, + share_rum = dplyr::coalesce(share_rum, 0), + share_mono = dplyr::coalesce(share_mono, 0), + + food_local = local_consumption * food_share, + other_local = local_consumption * other_uses_share, + feed_local = local_consumption * feed_share, + + food_gap = pmax(food - food_local, 0), + other_gap = pmax(other_uses - other_local, 0), + feed_gap = pmax(feed - feed_local, 0), + + total_gap = food_gap + other_gap + feed_gap, + + share_food = dplyr::if_else(total_gap > 0, food_gap / total_gap, 0), + share_other = dplyr::if_else(total_gap > 0, other_gap / total_gap, 0), + share_feed = dplyr::if_else(total_gap > 0, feed_gap / total_gap, 0), + + population_food = import_consumption * share_food, + population_other_uses = import_consumption * share_other, + import_feed = import_consumption * share_feed, + + livestock_rum = import_feed * share_rum, + livestock_mono = import_feed * share_mono, + Origin = "Outside", Irrig_cat = NA_character_ ) |> - # Aggregate to remove duplicates caused by Irrig_cat becoming NA + dplyr::select( + -food_local, + -other_local, + -feed_local, + -food_gap, + -other_gap, + -feed_gap, + -total_gap, + -share_food, + -share_other, + -share_feed, + -import_feed + ) |> dplyr::summarise( population_food = sum(population_food, na.rm = TRUE), population_other_uses = sum(population_other_uses, na.rm = TRUE), @@ -1271,6 +1486,7 @@ create_n_nat_destiny <- function(example = FALSE) { ) } + #' @title Adding exports #' @description Adds exports to the final dataset. #' @param grafs_prod_destiny_final A dataset containing consumption and trade. @@ -1321,25 +1537,103 @@ create_n_nat_destiny <- function(example = FALSE) { n_soil_inputs, feed_share_rum_mono ) { + biomass_coefs <- whep_read_file("biomass_coefs") grafs_prod_destiny_final <- .prep_final_ds( grafs_prod_item_trade, codes_coefs_items_full - ) + ) |> + dplyr::group_by(Year, Province_name, Item, Box, Irrig_cat) |> + dplyr::summarise( + production_n = sum(production_n, na.rm = TRUE), + food = sum(food, na.rm = TRUE), + other_uses = sum(other_uses, na.rm = TRUE), + feed = sum(feed, na.rm = TRUE), + export = sum(export, na.rm = TRUE), + import = sum(import, na.rm = TRUE), + .groups = "drop" + ) + shares_import <- .calculate_consumption_shares(grafs_prod_destiny_final) + pie_imports_n <- whep_read_file("pie_full_destinies_fm") |> + dplyr::filter( + Element == "Import", + Destiny %in% c("Food", "Other_uses", "Feed") + ) |> + dplyr::group_by(Year, Item, Destiny) |> + dplyr::summarise( + value_fm = sum(Value_destiny, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::left_join( + codes_coefs_items_full |> + dplyr::select(item, Name_biomass), + by = c("Item" = "item") + ) |> + dplyr::left_join( + biomass_coefs |> + dplyr::select( + Name_biomass, + Product_kgDM_kgFM, + Product_kgN_kgDM, + Residue_kgDM_kgFM, + Residue_kgN_kgDM + ), + by = "Name_biomass" + ) |> + dplyr::mutate( + prod_type = dplyr::case_when( + Name_biomass %in% c("Grass", "Fallow") ~ "Grass", + Name_biomass == "Average wood" ~ "Residue", + TRUE ~ "Product" + ) + ) |> + dplyr::mutate( + value_n = dplyr::case_when( + prod_type %in% c("Residue", "Grass") ~ + value_fm * + dplyr::coalesce(Residue_kgDM_kgFM, Product_kgDM_kgFM) * + dplyr::coalesce(Residue_kgN_kgDM, Product_kgN_kgDM), + prod_type == "Product" ~ + value_fm * + Product_kgDM_kgFM * + Product_kgN_kgDM, + + TRUE ~ NA_real_ + ) + ) |> + dplyr::group_by(Year, Item) |> + dplyr::mutate( + total = sum(value_n, na.rm = TRUE), + share = dplyr::if_else(total > 0, value_n / total, 0) + ) |> + dplyr::ungroup() + + shares_import_wide <- pie_imports_n |> + dplyr::select(Year, Item, Destiny, share) |> + tidyr::pivot_wider( + names_from = Destiny, + values_from = share, + names_prefix = "share_" + ) + local_vs_import <- grafs_prod_destiny_final |> dplyr::left_join( shares_import, - by = c("Year", "Province_name", "Item", "Irrig_cat") + by = c("Year", "Province_name", "Item", "Box", "Irrig_cat") ) |> dplyr::mutate( - local_consumption = pmin(production_n, consumption_total), - import_consumption = consumption_total - local_consumption + local_consumption = pmin(production_n, food + other_uses + feed), + import_consumption = pmax((food + other_uses + feed) - production_n, 0) ) dplyr::bind_rows( .split_local_consumption(local_vs_import, feed_share_rum_mono), - .split_import_consumption(local_vs_import, feed_share_rum_mono), + .split_import_consumption( + local_vs_import, + feed_share_rum_mono, + shares_import_wide + ), .add_exports(grafs_prod_destiny_final) ) |> dplyr::filter(MgN > 0) @@ -1352,7 +1646,7 @@ create_n_nat_destiny <- function(example = FALSE) { #' #' @param grafs_prod_destiny_final A tibble from `.finalize_prod_destiny()` #' containing destinies. -#' @param soil_inputs A dataframe with soil inputs. +#' @param n_soil_inputs A dataframe with soil inputs. #' #' @return The input dataframe extended with soil N input flows. #' @keywords internal @@ -1388,3 +1682,8 @@ create_n_nat_destiny <- function(example = FALSE) { dplyr::filter(MgN != 0) |> dplyr::arrange(Year, Province_name, Item, Irrig_cat, Origin, Destiny) } + +.ensure_livestock_cols <- function(df) { + missing <- setdiff(c("ruminant", "monogastric", "pets"), names(df)) + dplyr::mutate(df, !!!purrr::map(rlang::set_names(missing), ~0)) +} diff --git a/R/n_soil_inputs_nue.R b/R/n_soil_inputs_nue.R index 4228381b..cbc0acf6 100644 --- a/R/n_soil_inputs_nue.R +++ b/R/n_soil_inputs_nue.R @@ -97,6 +97,23 @@ create_n_soil_inputs <- function(example = FALSE) { Box_semi_natural_agroecosystems = "semi_natural_agroecosystems" ) + n_balance_old <- whep_read_file("n_balance_ygpit_all_old") + + excreta_old <- n_balance_old |> + dplyr::group_by(Year, Province_name, Name_biomass, LandUse, Irrig_cat) |> + dplyr::summarise( + Excreta_old = sum(Excreta, na.rm = TRUE), + .groups = "drop" + ) + + n_balance_ygpit_all <- n_balance_ygpit_all |> + dplyr::left_join( + excreta_old, + by = c("Year", "Province_name", "Name_biomass", "LandUse", "Irrig_cat") + ) |> + dplyr::mutate(Excreta = dplyr::coalesce(Excreta_old, 0)) |> + dplyr::select(-Excreta_old) + # Combine all necessary n Inputs n_soil_inputs <- n_balance_ygpit_all |> dplyr::left_join(items, by = "Name_biomass") |> @@ -111,7 +128,7 @@ create_n_soil_inputs <- function(example = FALSE) { deposition = sum(Deposition, na.rm = TRUE), fixation = sum(BNF, na.rm = TRUE), synthetic = sum(Synthetic, na.rm = TRUE), - manure = sum(Solid + Liquid, na.rm = TRUE), + manure = sum(Excreta + Solid + Liquid, na.rm = TRUE), urban = sum(Urban, na.rm = TRUE), .by = c(Year, Province_name, Item, Irrig_cat, Box) ) |> @@ -150,8 +167,7 @@ create_n_production <- function(example = FALSE) { #' @title Calculate N Production #' @description Internal function to calculate nitrogen production. #' @param grafs_prod_destiny A data frame with consumption, export, import data. -#' @return A tibble with production values per year, province, item, -#' and box. +#' @return A tibble with production and import values. #' @keywords internal #' @noRd .calculate_n_production <- function(grafs_prod_destiny) { @@ -191,14 +207,22 @@ create_n_production <- function(example = FALSE) { #' @param example If `TRUE`, return a small example output without downloading #' remote data. Default is `FALSE`. #' -#' @return A tibble containing nitrogen use efficiency (NUE) for crops. +#' @returns +#' A tibble containing nitrogen input, production, and NUE data. #' It includes the following columns: #' - `year`: Year. #' - `province_name`: The Spanish province. #' - `item`: The item which was produced, defined in `names_biomass_cb`. #' - `box`: One of the two systems of the GRAFS model: cropland or -#' semi-natural agroecosystems. -#' - `nue`: Nitrogen Use Efficiency as a percentage (%). +#' semi-natural agroecosystems. +#' - `deposition`: Atmospheric nitrogen deposition in megagrams (Mg). +#' - `fixation`: Nitrogen fixation in megagrams (Mg). +#' - `synthetic`: Synthetic nitrogen fertilizer applied to the land in +#' megagrams (Mg). +#' - `manure`: Nitrogen in manure applied to the land in megagrams (Mg). +#' - `urban`: Nitrogen in wastewater from human sources in megagrams (Mg). +#' - `prod`: Produced nitrogen in megagrams (Mg). +#' - `inputs`: Total nitrogen inputs in megagrams (Mg). #' #' @export #' @@ -282,30 +306,32 @@ calculate_nue_livestock <- function(example = FALSE) { return(.ex_calc_nue_livestock()) } intake_n <- whep_read_file("intake_ygiac") |> - dplyr::filter(Livestock_cat != "Pets") |> - dplyr::group_by(Year, Province_name, Livestock_cat) |> + dplyr::rename_with(tolower) |> + dplyr::rename(n_mg_n = n_mgn) |> + dplyr::filter(livestock_cat != "Pets") |> + dplyr::group_by(year, province_name, livestock_cat) |> dplyr::summarise( - feed_n = sum(N_MgN, na.rm = TRUE), + feed_n = sum(n_mg_n, na.rm = TRUE), .groups = "drop" - ) |> - dplyr::rename_with(tolower) + ) - prod_n <- whep_read_file("livestock_prod_ygps") |> - dplyr::filter(!is.na(Prod_MgN)) |> - dplyr::group_by(Year, Province_name, Livestock_cat, Item) |> + prod_n <- whep_read_file("stock_prod_ygps") |> + dplyr::rename_with(tolower) |> + dplyr::filter(!is.na(prod_mgn)) |> + dplyr::group_by(year, province_name, livestock_cat, item) |> dplyr::summarise( - prod_n = sum(Prod_MgN, na.rm = TRUE), + prod_n = sum(prod_mgn, na.rm = TRUE), .groups = "drop" - ) |> - dplyr::rename_with(tolower) + ) excretion_n <- whep_read_file("n_excretion_ygs") |> - dplyr::group_by(Year, Province_name, Livestock_cat) |> + dplyr::rename_with(tolower) |> + dplyr::filter(livestock_cat != "Pets") |> + dplyr::group_by(year, province_name, livestock_cat) |> dplyr::summarise( - excretion_n = sum(Excr_MgN, na.rm = TRUE), + excretion_n = sum(n_excr_mgn, na.rm = TRUE), .groups = "drop" - ) |> - dplyr::rename_with(tolower) + ) nue_livestock <- intake_n |> dplyr::inner_join( @@ -380,14 +406,14 @@ calculate_system_nue <- function( total_outputs <- dplyr::bind_rows( whep_read_file("n_balance_ygpit_all"), - whep_read_file("livestock_prod_ygps") + whep_read_file("stock_prod_ygps") ) |> - dplyr::group_by(Year, Province_name) |> + dplyr::rename_with(tolower) |> + dplyr::group_by(year, province_name) |> dplyr::summarise( - total_prod = sum(Prod_MgN, na.rm = TRUE), + total_prod = sum(prod_mgn, na.rm = TRUE), .groups = "drop" - ) |> - dplyr::rename_with(tolower) + ) system_nue <- total_outputs |> dplyr::left_join(n_soil_inputs, by = c("year", "province_name")) |> diff --git a/R/read_raw_inputs.R b/R/read_raw_inputs.R index b40d6e5c..85315995 100644 --- a/R/read_raw_inputs.R +++ b/R/read_raw_inputs.R @@ -110,17 +110,18 @@ .download_pin_paths <- function(file_alias) { file_info <- .fetch_file_info(file_alias, whep::whep_inputs) version <- .choose_version(file_info$version, NULL) + pin_name <- if (!is.na(file_info$pin_name)) file_info$pin_name else file_alias tryCatch( .get_local_board() |> - pins::pin_download(file_alias, version = version), + pins::pin_download(pin_name, version = version), error = function(e) { tryCatch( file_info |> .get_remote_board() |> - pins::pin_download(file_alias, version = version), + pins::pin_download(pin_name, version = version), error = function(e) { - .get_cache_paths(file_info, file_alias, version, e) + .get_cache_paths(file_info, pin_name, version, e) } ) } diff --git a/R/typologies_kgN_ha.R b/R/typologies_kgN_ha.R new file mode 100644 index 00000000..d8b54be9 --- /dev/null +++ b/R/typologies_kgN_ha.R @@ -0,0 +1,226 @@ +typology_area_stacked_bars <- function() { + indicators <- create_typo_ts_plot() + npp_ygpit <- whep_read_file("npp_ygpit") + + typologies_df <- indicators |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + Typology = dplyr::first(Typology_base), + .groups = "drop" + ) + + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems (intensive)" = "#F7DD5A", + "Specialized cropping systems (extensive)" = "#FFF7C2", + "Specialized livestock systems (intensive)" = "#b3001b", + "Specialized livestock systems (extensive)" = "#C94F6B", + "Connected crop-livestock systems (intensive)" = "#7A4F20", + "Connected crop-livestock systems (extensive)" = "#AF814B", + "Disconnected crop-livestock systems (intensive)" = "#E67E00", + "Disconnected crop-livestock systems (extensive)" = "#F6A640" + ) + + df <- .build_area_totals( + .sum_area_by_prov(npp_ygpit), typologies_df, typology_colors + ) + + year_breaks <- df$Year |> unique() |> sort() + year_breaks <- year_breaks[year_breaks %% 20 == 0] + + p_total <- .plot_area_stacked( + df, year_breaks, "Mha", + "Total land area by typology", "Total area (Mha)", + typology_colors + ) + p_pct <- .plot_area_stacked( + df, year_breaks, "Percent_ha", + "Land area by typology (%)", "Share of total area (%)", + typology_colors + ) + + print(p_total) + print(p_pct) + + list(df = df, p_total = p_total, p_pct = p_pct) +} + +typology_kgha_lines <- function() { + indicators <- create_typo_ts_plot() + npp_ygpit <- whep_read_file("npp_ygpit") + + typologies_df <- indicators |> + dplyr::select(year, province_name, Typology_base) |> + dplyr::rename(Typology = Typology_base) + + n_balance <- whep_read_file("n_balance_ygpit_all") + + soil_agri <- n_balance |> + dplyr::filter( + LandUse %in% c("Cropland", "Dehesa", "Pasture_Shrubland") + ) |> + dplyr::mutate( + Total_N_Mg = Deposition + BNF + Synthetic + Solid + Liquid + Urban + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Total_N_Mg = sum(Total_N_Mg, na.rm = TRUE), + .groups = "drop" + ) + + soil_all <- n_balance |> + dplyr::mutate( + Total_N_Mg = Deposition + BNF + Synthetic + Solid + Liquid + Urban + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Total_N_Mg = sum(Total_N_Mg, na.rm = TRUE), + .groups = "drop" + ) + + area_agri <- npp_ygpit |> + dplyr::filter( + LandUse %in% c("Cropland", "Pasture_Shrubland", "Dehesa") + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + area_all <- npp_ygpit |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + build_kgha <- function(area_df, soil_df) { + soil_df |> + dplyr::left_join(area_df, by = c("Year", "Province_name")) |> + dplyr::left_join( + typologies_df, + by = c("Year" = "year", "Province_name" = "province_name") + ) |> + dplyr::mutate( + kgN_ha = (Total_N_Mg * 1000) / Area_ha, + + Typology = gsub(" \\(intensive\\)| \\(extensive\\)", "", Typology) + ) |> + dplyr::filter(!is.na(kgN_ha), Area_ha > 0) |> + dplyr::group_by(Year, Typology) |> + dplyr::summarise( + mean_kgN = mean(kgN_ha, na.rm = TRUE), + sd_kgN = sd(kgN_ha, na.rm = TRUE), + .groups = "drop" + ) + } + + df_agri <- build_kgha(area_agri, soil_agri) + df_all <- build_kgha(area_all, soil_all) + + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems" = "#F7DD5A", + "Specialized livestock systems" = "#b3001b", + "Connected crop-livestock systems" = "#7A4F20", + "Disconnected crop-livestock systems" = "#E67E00" + ) + + df_agri$Typology <- factor(df_agri$Typology, levels = names(typology_colors)) + df_all$Typology <- factor(df_all$Typology, levels = names(typology_colors)) + + plot_fun <- function(df, title_text) { + ggplot2::ggplot( + df, + ggplot2::aes( + x = Year, + y = mean_kgN, + color = Typology, + fill = Typology + ) + ) + + ggplot2::geom_ribbon( + ggplot2::aes( + ymin = mean_kgN - sd_kgN, + ymax = mean_kgN + sd_kgN + ), + alpha = 0.2, + colour = NA + ) + + ggplot2::geom_line(linewidth = 1.2) + + ggplot2::scale_color_manual(values = typology_colors, drop = TRUE) + + ggplot2::scale_fill_manual(values = typology_colors, drop = TRUE) + + ggplot2::labs( + title = title_text, + x = "Year", + y = "kg N per ha", + color = "Typology", + fill = "Typology" + ) + + ggplot2::theme_minimal() + } + + p1 <- plot_fun(df_agri, "Nitrogen Inputs per ha (cropland and grassland)") + p2 <- plot_fun(df_all, "Nitrogen Inputs per ha (total land area)") + + print(p1) + print(p2) + + list( + p1 = p1, + p2 = p2, + agricultural_land = df_agri, + total_land = df_all + ) +} + +.sum_area_by_prov <- function(npp_ygpit, land_uses = NULL) { + df <- npp_ygpit + if (!is.null(land_uses)) df <- dplyr::filter(df, LandUse %in% land_uses) + df |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) +} + +.build_area_totals <- function(area_by_prov, typologies_df, typology_colors) { + area_by_prov |> + dplyr::left_join( + typologies_df, + by = c("Year" = "year", "Province_name" = "province_name") + ) |> + dplyr::group_by(Year, Typology) |> + dplyr::summarise( + Total_ha = sum(Area_ha, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::group_by(Year) |> + dplyr::mutate( + Mha = Total_ha / 1e6, + Percent_ha = Total_ha / sum(Total_ha) * 100 + ) |> + dplyr::ungroup() |> + dplyr::mutate( + Typology = factor(Typology, levels = names(typology_colors)) + ) +} + +.plot_area_stacked <- function(df, year_breaks, y_var, title, y_label, colors) { + ggplot2::ggplot( + df, + ggplot2::aes(x = factor(Year), y = .data[[y_var]], fill = Typology) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::scale_x_discrete(breaks = as.character(year_breaks)) + + ggplot2::scale_fill_manual(values = colors) + + ggplot2::labs( + title = title, x = "Year", y = y_label, fill = "Typology" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + axis.text.x = ggplot2::element_text(angle = 45, hjust = 1) + ) +} diff --git a/R/typologies_spain.R b/R/typologies_spain.R new file mode 100644 index 00000000..2cc18c69 --- /dev/null +++ b/R/typologies_spain.R @@ -0,0 +1,386 @@ +#' +#' +create_typologies_spain <- function( + n_prov_destiny = NULL, + make_map = TRUE, + shapefile_path = "C:/PhD/GRAFS/Production Boxes/Final Files/Inputs/ne_10m_admin_1_states_provinces.shp", + map_year = 1860 +) { + if (is.null(n_prov_destiny)) { + n_prov_destiny <- create_n_prov_destiny() + } + + livestock_prod_ygps <- whep_read_file("stock_prod_ygps") + npp_ygpit <- whep_read_file("npp_ygpit") + + lu_mapping <- tibble::tribble( + ~Animal_class, ~LU_head, + "Dairy_cows", 1, + "Cattle", 0.8, + "Sheep_goats", 0.1, + "Equines", 0.8, + "Pigs", 0.3, + "Hogs", 0.5, + "Broilers", 0.007, + "Hens", 0.014, + "Other_birds", 0.03, + "Turkeys", 0.03, + "Ducks", 0.01, + "Geese", 0.02, + "Ostriches", 0.35, + "Small_birds", 0.001, + "Rabbits", 0.02, + "Bees", 0.01 + ) + + livestockcat_to_class <- tibble::tribble( + ~Livestock_cat, ~Animal_class, + "Cattle_milk", "Dairy_cows", + "Cattle_meat", "Cattle", + "Sheep", "Sheep_goats", + "Goats", "Sheep_goats", + "Donkeys_mules", "Equines", + "Horses", "Equines", + "Pigs", "Pigs", + "Hogs", "Hogs", + "Poultry", "Hens", + "Rabbits", "Rabbits", + "Bees", "Bees" + ) + + lu_df <- livestock_prod_ygps |> + dplyr::select(Year, Province_name, Livestock_cat, Stock_Number) |> + dplyr::left_join(livestockcat_to_class, by = "Livestock_cat") |> + dplyr::left_join(lu_mapping, by = "Animal_class") |> + dplyr::mutate( + LU_head = tidyr::replace_na(LU_head, 0), + Stock_Number = tidyr::replace_na(Stock_Number, 0), + LU_total_row = Stock_Number * LU_head + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + LU_total = sum(LU_total_row, na.rm = TRUE), + .groups = "drop" + ) + + area_df <- npp_ygpit |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + livestock_density_df <- lu_df |> + dplyr::left_join(area_df, by = c("Year", "Province_name")) |> + dplyr::mutate( + # avoid division by zero + Area_ha = ifelse(Area_ha == 0 | is.na(Area_ha), NA_real_, Area_ha), + Livestock_density = LU_total / Area_ha + ) |> + dplyr::select(Year, Province_name, LU_total, Area_ha, Livestock_density) + + productivity_df <- npp_ygpit |> + dplyr::filter(LandUse == "Cropland") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Prod_MgN_total = sum(Prod_MgN, na.rm = TRUE), + Area_ha_crops = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + crop_productivity = Prod_MgN_total / Area_ha_crops * 1000 + ) + + n_agg <- n_prov_destiny |> + dplyr::group_by(year, province_name, origin, destiny, box) |> + dplyr::summarise(mg_n = sum(mg_n, na.rm = TRUE), .groups = "drop") + + indicators <- n_agg |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + production_crops = sum(mg_n[origin == "Cropland"], na.rm = TRUE), + production_seminatural = sum( + mg_n[origin == "semi_natural_agroecosystems"], + na.rm = TRUE + ), + production_total = sum( + mg_n[ + origin %in% + c("Cropland", "Livestock", "semi_natural_agroecosystems") & + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_mono", + "livestock_rum", + "export" + ) + ], + na.rm = TRUE + ), + pop_consumption = sum( + mg_n[destiny == "population_food" & origin != "Fish"], + na.rm = TRUE + ), + + animal_ingestion = sum( + mg_n[destiny %in% c("livestock_mono", "livestock_rum")], + na.rm = TRUE + ), + imported_feed_share = sum( + mg_n[ + origin == "Outside" & + destiny %in% c("livestock_mono", "livestock_rum") + ], + na.rm = TRUE + ) / + sum( + mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ] + ), + local_feed_share = sum( + mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems") & + destiny %in% c("livestock_mono", "livestock_rum") + ], + na.rm = TRUE + ) / + sum( + mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ], + na.rm = TRUE + ), + feed_from_seminatural_share = sum( + mg_n[ + origin == "semi_natural_agroecosystems" & + destiny %in% c("livestock_mono", "livestock_rum") + ], + na.rm = TRUE + ) / + sum( + mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ] + ), + Manure_share = sum( + mg_n[ + origin == "Livestock" & + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ], + na.rm = TRUE + ) / + sum( + mg_n[ + origin %in% + c("Livestock", "Synthetic", "Fixation", "Deposition") & + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ], + na.rm = TRUE + ), + synthetic_share = sum( + mg_n[origin == "Synthetic" & destiny == "Cropland"], + na.rm = TRUE + ) / + sum( + mg_n[ + origin %in% + c("Synthetic", "Livestock", "Fixation", "Deposition", "People") & + destiny == "Cropland" + ], + na.rm = TRUE + ) + ) + + indicators <- indicators |> + dplyr::left_join( + livestock_density_df, + by = c("year" = "Year", "province_name" = "Province_name") + ) |> + dplyr::left_join( + productivity_df, + by = c("year" = "Year", "province_name" = "Province_name") + ) |> + dplyr::mutate( + Livestock_density = tidyr::replace_na(Livestock_density, 0), + synthetic_share = tidyr::replace_na(synthetic_share, 0), + crop_productivity = tidyr::replace_na(crop_productivity, 0), + dplyr::across(dplyr::where(is.numeric), ~ tidyr::replace_na(., 0)), + year = as.numeric(year) + ) + + indicators <- indicators |> + dplyr::mutate( + Typology_base = dplyr::case_when( + production_seminatural > production_crops ~ + "Semi-natural agroecosystems", + production_crops > animal_ingestion & + synthetic_share > 0.4 & + crop_productivity >= 10 ~ + "Specialized cropping systems (intensive)", + production_crops > animal_ingestion & + synthetic_share <= 0.4 & + crop_productivity < 8 ~ + "Specialized cropping systems (extensive)", + Livestock_density > 1.3 & + imported_feed_share > 0.6 & + feed_from_seminatural_share < 0.4 ~ + "Specialized livestock systems (intensive)", + Livestock_density > 1 & + Livestock_density <= 1.3 & + imported_feed_share > 0.6 & + feed_from_seminatural_share < 0.4 ~ + "Specialized livestock systems (extensive)", + local_feed_share > 0.3 & Manure_share > 0.3 & crop_productivity >= 30 ~ + "Connected crop-livestock systems (intensive)", + local_feed_share > 0.3 & Manure_share > 0.3 & crop_productivity < 30 ~ + "Connected crop-livestock systems (extensive)", + local_feed_share < 0.6 & Manure_share < 0.6 ~ + "Disconnected crop-livestock systems (intensive)", + TRUE ~ "Disconnected crop-livestock systems (extensive)" + ) + ) + + indicators <- indicators |> + dplyr::mutate( + Typology = dplyr::case_when( + pop_consumption > production_total ~ "Urban systems", + TRUE ~ Typology_base + ) + ) + + if (make_map) { + layer_name <- tools::file_path_sans_ext(basename(shapefile_path)) + sf_provinces <- sf::st_read( + shapefile_path, + query = paste0( + "SELECT * FROM ", + layer_name, + " WHERE iso_a2 = 'ES'" + ), + quiet = TRUE + ) + + province_col <- intersect( + c("NAME_1", "name", "NAME", "province"), + colnames(sf_provinces) + )[1] + + sf_provinces <- sf_provinces |> + dplyr::mutate( + name_clean = stringi::stri_trans_general( + .data[[province_col]], + "Latin-ASCII" + ), + name_clean = gsub(" ", "_", name_clean) + ) + + sf_provinces$name_clean[sf_provinces$name_clean == "La_Rioja"] <- "Rioja" + sf_provinces$name_clean[sf_provinces$name_clean == "Alava"] <- "Araba" + sf_provinces$name_clean[sf_provinces$name_clean == "Lerida"] <- "Lleida" + sf_provinces$name_clean[ + sf_provinces$name_clean == "Castellon" + ] <- "Castello" + sf_provinces$name_clean[ + sf_provinces$name_clean == "La_Coruna" + ] <- "A_Coruna" + sf_provinces$name_clean[sf_provinces$name_clean == "Orense"] <- "Ourense" + sf_provinces$name_clean[sf_provinces$name_clean == "Gerona"] <- "Girona" + + sf_provinces <- sf_provinces[ + !sf_provinces$name_clean %in% c("Las_Palmas", "Tenerife"), + ] + + filtered_indicators <- indicators |> + dplyr::filter(year == map_year) |> + dplyr::select(province_name, Typology, Typology_base) |> + dplyr::mutate( + pattern_type = ifelse(Typology == "Urban systems", "stripe", "none"), + pattern_fill = "Urban systems" + ) + + typologies_map <- sf_provinces |> + dplyr::inner_join( + filtered_indicators, + by = c("name_clean" = "province_name") + ) + + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + + "Specialized cropping systems (intensive)" = "#F7DD5A", + "Specialized cropping systems (extensive)" = "#FFF7C2", + + "Specialized livestock systems (intensive)" = "#b3001b", + "Specialized livestock systems (extensive)" = "#C94F6B", + + "Connected crop-livestock systems (intensive)" = "#7A4F20", + "Connected crop-livestock systems (extensive)" = "#AF814B", + + "Disconnected crop-livestock systems (intensive)" = "#E67E00", + "Disconnected crop-livestock systems (extensive)" = "#F6A640", + + "Urban systems" = "#6A5ACD" + ) + + typologies_map$Typology_base <- factor( + typologies_map$Typology_base, + levels = c( + "Semi-natural agroecosystems", + "Specialized cropping systems (intensive)", + "Specialized cropping systems (extensive)", + "Specialized livestock systems (intensive)", + "Specialized livestock systems (extensive)", + "Connected crop-livestock systems (intensive)", + "Connected crop-livestock systems (extensive)", + "Disconnected crop-livestock systems (intensive)", + "Disconnected crop-livestock systems (extensive)" + ) + ) + + ggplot2::ggplot(typologies_map) + + ggpattern::geom_sf_pattern( + ggplot2::aes( + fill = Typology_base, + pattern = pattern_type, + pattern_fill = pattern_fill + ), + color = "black", + pattern_angle = 45, + pattern_density = 0.5, + pattern_spacing = 0.03 + ) + + ggplot2::scale_fill_manual( + values = typology_colors[names(typology_colors) != "Urban systems"], + name = "Typologies", + drop = TRUE, + guide = ggplot2::guide_legend( + order = 1, + override.aes = list(pattern = "none") + ) + ) + + ggpattern::scale_pattern_fill_manual( + values = c("Urban systems" = typology_colors["Urban systems"]), + na.value = NA, + guide = ggplot2::guide_legend( + title = NULL, + override.aes = list( + fill = "white", + pattern = "stripe" + ), + order = 2 + ) + ) + + ggplot2::labs(title = paste("Typologies in Spain for", map_year)) + + ggplot2::theme_minimal() + } + indicators +} diff --git a/R/typologies_spain_plot.R b/R/typologies_spain_plot.R new file mode 100644 index 00000000..987ca389 --- /dev/null +++ b/R/typologies_spain_plot.R @@ -0,0 +1,453 @@ +create_typo_ts_plot <- function( + n_prov_destiny = NULL, + shapefile_path = "C:/PhD/GRAFS/Production Boxes/Final Files/Inputs/ne_10m_admin_1_states_provinces.shp", + benchmark_years = seq(1860, 2020, by = 20) +) { + if (is.null(n_prov_destiny)) { + n_prov_destiny <- create_n_prov_destiny() + } + + livestock_prod_ygps <- whep_read_file("stock_prod_ygps") + npp_ygpit <- whep_read_file("npp_ygpit") + + lu_mapping <- tibble::tribble( + ~Animal_class, ~LU_head, + "Dairy_cows", 1, "Cattle", 0.8, "Sheep_goats", 0.1, "Equines", 0.8, + "Pigs", 0.3, "Hogs", 0.5, "Broilers", 0.007, "Hens", 0.014, + "Other_birds", 0.03, "Turkeys", 0.03, "Ducks", 0.01, "Geese", 0.02, + "Ostriches", 0.35, "Small_birds", 0.001, "Rabbits", 0.02, "Bees", 0.01 + ) + + livestockcat_to_class <- tibble::tribble( + ~Livestock_cat, ~Animal_class, + "Cattle_milk", "Dairy_cows", + "Cattle_meat", "Cattle", + "Sheep", "Sheep_goats", + "Goats", "Sheep_goats", + "Donkeys_mules", "Equines", + "Horses", "Equines", + "Pigs", "Pigs", + "Hogs", "Hogs", + "Poultry", "Hens", + "Rabbits", "Rabbits", + "Bees", "Bees" + ) + + lu_df <- livestock_prod_ygps |> + dplyr::left_join(livestockcat_to_class, by = "Livestock_cat") |> + dplyr::left_join(lu_mapping, by = "Animal_class") |> + dplyr::mutate( + LU_head = tidyr::replace_na(LU_head, 0), + LU_total_row = Stock_Number * LU_head + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise(LU_total = sum(LU_total_row), .groups = "drop") + + area_df <- npp_ygpit |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise(Area_ha = sum(Area_ygpit_ha), .groups = "drop") + + livestock_density_df <- lu_df |> + dplyr::left_join(area_df, by = c("Year", "Province_name")) |> + dplyr::mutate( + Area_ha = dplyr::na_if(Area_ha, 0), + Livestock_density = LU_total / Area_ha + ) + + productivity_df <- npp_ygpit |> + dplyr::filter(LandUse == "Cropland") |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + Prod_MgN_total = sum(Prod_MgN), + Area_ha_crops = sum(Area_ygpit_ha), + .groups = "drop" + ) |> + dplyr::mutate(crop_productivity = Prod_MgN_total / Area_ha_crops * 1000) + + n_agg <- n_prov_destiny |> + dplyr::group_by(year, province_name, origin, destiny, box) |> + dplyr::summarise(mg_n = sum(mg_n), .groups = "drop") + + indicators <- n_agg |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + production_crops = sum(mg_n[origin == "Cropland"]), + production_seminatural = sum(mg_n[ + origin == "semi_natural_agroecosystems" + ]), + animal_ingestion = sum(mg_n[ + destiny %in% c("livestock_mono", "livestock_rum") + ]), + pop_consumption = sum(mg_n[destiny == "population_food"]), + production_total = sum(mg_n[ + origin %in% + c("Cropland", "Livestock", "semi_natural_agroecosystems") & + destiny %in% + c( + "population_food", + "population_other_uses", + "livestock_mono", + "livestock_rum", + "export" + ) + ]), + imported_feed_share = sum(mg_n[ + origin == "Outside" & + destiny %in% c("livestock_mono", "livestock_rum") + ]) / + sum(mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ]), + local_feed_share = sum(mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems") & + destiny %in% c("livestock_mono", "livestock_rum") + ]) / + sum(mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ]), + feed_from_seminatural_share = sum(mg_n[ + origin == "semi_natural_agroecosystems" & + destiny %in% c("livestock_mono", "livestock_rum") + ]) / + sum(mg_n[ + origin %in% + c("Cropland", "semi_natural_agroecosystems", "Outside") & + destiny %in% c("livestock_mono", "livestock_rum") + ]), + Manure_share = sum(mg_n[origin == "Livestock"]) / + sum(mg_n[ + origin %in% c("Livestock", "Synthetic", "Fixation", "Deposition") + ]), + synthetic_share = sum(mg_n[origin == "Synthetic"]) / + sum(mg_n[ + origin %in% c("Synthetic", "Livestock", "Fixation", "Deposition") + ]), + .groups = "drop" + ) |> + tidyr::replace_na(list( + imported_feed_share = 0, + local_feed_share = 0, + feed_from_seminatural_share = 0, + Manure_share = 0, + synthetic_share = 0 + )) + + indicators <- indicators |> + dplyr::left_join( + livestock_density_df, + by = c("year" = "Year", "province_name" = "Province_name") + ) |> + dplyr::left_join( + productivity_df, + by = c("year" = "Year", "province_name" = "Province_name") + ) |> + dplyr::mutate(dplyr::across( + dplyr::where(is.numeric), + ~ tidyr::replace_na(., 0) + )) + + indicators <- indicators |> + dplyr::mutate( + Typology_base = dplyr::case_when( + production_seminatural > production_crops ~ + "Semi-natural agroecosystems", + production_crops > animal_ingestion & + synthetic_share > 0.4 & + crop_productivity >= 10 ~ + "Specialized cropping systems (intensive)", + production_crops > animal_ingestion & + synthetic_share <= 0.4 & + crop_productivity < 8 ~ + "Specialized cropping systems (extensive)", + Livestock_density > 1.3 & + imported_feed_share > 0.6 & + feed_from_seminatural_share < 0.4 ~ + "Specialized livestock systems (intensive)", + Livestock_density > 1 & + Livestock_density <= 1.3 & + imported_feed_share > 0.6 & + feed_from_seminatural_share < 0.4 ~ + "Specialized livestock systems (extensive)", + local_feed_share > 0.3 & Manure_share > 0.3 & crop_productivity >= 30 ~ + "Connected crop-livestock systems (intensive)", + local_feed_share > 0.3 & Manure_share > 0.3 & crop_productivity < 30 ~ + "Connected crop-livestock systems (extensive)", + local_feed_share < 0.6 & Manure_share < 0.6 ~ + "Disconnected crop-livestock systems (intensive)", + TRUE ~ "Disconnected crop-livestock systems (extensive)" + ), + Typology = dplyr::case_when( + pop_consumption > production_total ~ "Urban systems", + TRUE ~ Typology_base + ) + ) + + layer_name <- tools::file_path_sans_ext(basename(shapefile_path)) + sf_provinces <- sf::st_read( + shapefile_path, + query = paste0("SELECT * FROM ", layer_name, " WHERE iso_a2='ES'"), + quiet = TRUE + ) + + province_col <- intersect( + c("NAME_1", "name", "NAME", "province"), + colnames(sf_provinces) + )[1] + + sf_provinces <- sf_provinces |> + dplyr::mutate( + name_clean = stringi::stri_trans_general( + .data[[province_col]], + "Latin-ASCII" + ), + name_clean = gsub(" ", "_", name_clean) + ) + + sf_provinces$name_clean[sf_provinces$name_clean == "La_Rioja"] <- "Rioja" + sf_provinces$name_clean[sf_provinces$name_clean == "Alava"] <- "Araba" + sf_provinces$name_clean[sf_provinces$name_clean == "Lerida"] <- "Lleida" + sf_provinces$name_clean[sf_provinces$name_clean == "Castellon"] <- "Castello" + sf_provinces$name_clean[sf_provinces$name_clean == "La_Coruna"] <- "A_Coruna" + sf_provinces$name_clean[sf_provinces$name_clean == "Orense"] <- "Ourense" + sf_provinces$name_clean[sf_provinces$name_clean == "Gerona"] <- "Girona" + + sf_provinces <- sf_provinces[ + !sf_provinces$name_clean %in% c("Las_Palmas", "Tenerife"), + ] + + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems (intensive)" = "#F7DD5A", + "Specialized cropping systems (extensive)" = "#FFF7C2", + "Specialized livestock systems (intensive)" = "#b3001b", + "Specialized livestock systems (extensive)" = "#C94F6B", + "Connected crop-livestock systems (intensive)" = "#7A4F20", + "Connected crop-livestock systems (extensive)" = "#AF814B", + "Disconnected crop-livestock systems (intensive)" = "#E67E00", + "Disconnected crop-livestock systems (extensive)" = "#F6A640", + "Urban systems" = "#6A5ACD" + ) + + typology_levels <- names(typology_colors) + + map_list <- list() + + for (yr in benchmark_years) { + df_yr <- indicators |> + dplyr::filter(year == yr) |> + dplyr::mutate( + pattern_type = ifelse(Typology == "Urban systems", "stripe", "none"), + pattern_fill = "Urban systems" + ) + + typology_map <- sf_provinces |> + dplyr::inner_join(df_yr, by = c("name_clean" = "province_name")) + + p <- ggplot2::ggplot(typology_map) + + ggpattern::geom_sf_pattern( + ggplot2::aes( + fill = Typology_base, + pattern = pattern_type, + pattern_fill = pattern_fill + ), + color = "black", + pattern_angle = 45, + pattern_density = 0.5, + pattern_spacing = 0.05 + ) + + ggplot2::scale_fill_manual( + values = typology_colors[names(typology_colors) != "Urban systems"], + drop = TRUE + ) + + ggpattern::scale_pattern_fill_manual( + values = c("Urban systems" = typology_colors["Urban systems"]), + na.value = NA + ) + + ggplot2::labs(title = paste("Year", yr)) + + ggplot2::theme_minimal() + + ggplot2::theme( + legend.position = "none", + plot.title = ggplot2::element_text(size = 9, face = "plain") + ) + + map_list[[as.character(yr)]] <- p + } + + block_width <- 0.002 + block_height <- 0.002 + block_gap <- 0.0006 + title_y <- 0.505 + + n <- length(typology_levels) + + y_top <- seq(from = 0.5, by = -(block_height + block_gap), length.out = n) + y_bottom <- y_top - block_height + + legend_df <- data.frame( + Typology = typology_levels, + Color = unname(typology_colors), + is_urban = typology_levels == "Urban systems", + ymin = y_bottom, + ymax = y_top + ) + + n_stripes <- 10 + stripe_df <- subset(legend_df, is_urban) + + stripe_lines <- do.call( + rbind, + lapply(seq_len(nrow(stripe_df)), function(i) { + xs <- seq(0, block_width, length.out = n_stripes) + data.frame( + x = xs, + xend = xs, + y = rep(stripe_df$ymin[i], n_stripes), + yend = rep(stripe_df$ymax[i], n_stripes) + ) + }) + ) + + legend_plot <- ggplot2::ggplot(legend_df) + + ggplot2::annotate( + "text", + x = 0, + y = title_y, + label = "Typologies", + hjust = 0, + vjust = 0, + size = 5 + ) + + ggplot2::geom_rect( + ggplot2::aes( + xmin = 0, + xmax = block_width, + ymin = ymin, + ymax = ymax, + fill = ifelse(is_urban, "white", Color) + ), + color = "black" + ) + + ggplot2::geom_segment( + data = stripe_lines, + ggplot2::aes( + x = x, + xend = xend, + y = y, + yend = yend + ), + color = "#6A5ACD", + linewidth = 0.28 + ) + + ggplot2::geom_text( + ggplot2::aes( + x = block_width + 0.001, + y = (ymin + ymax) / 2, + label = Typology + ), + hjust = 0, + size = 5 + ) + + ggplot2::scale_fill_identity() + + ggplot2::scale_x_continuous( + limits = c(0, 0.05), + expand = c(0, 0) + ) + + ggplot2::scale_y_continuous( + limits = c(min(y_bottom) - 0.005, max(y_top) + 0.01), + expand = c(0, 0) + ) + + ggplot2::coord_fixed(ratio = 1, clip = "off") + + ggplot2::theme_void() + + ggplot2::theme( + plot.margin = ggplot2::margin(2, 2, 2, 2) + ) + + combined <- patchwork::wrap_plots(map_list, ncol = 3) + + final_plot <- (combined | legend_plot) + + patchwork::plot_annotation( + title = "Typologies in Spain (1860-2020)" + ) & + ggplot2::theme( + plot.title = ggplot2::element_text( + size = 18, + margin = ggplot2::margin(t = 0, b = 8) + ) + ) + + grid::grid.newpage() + print(final_plot) + + output_path <- "C:/PhD/Typologies/Typologies_spain/new_typologies/typologies_spain.png" + + ggplot2::ggsave( + filename = output_path, + plot = final_plot, + width = 16, + height = 10, + dpi = 300 + ) + + indicators +} + +create_typology_decision_tree <- function() { + DiagrammeR::grViz( + ' + digraph decision_tree { + graph [rankdir=TB, fontname="Arial", nodesep=0.05, ranksep=0.5] + node [shape=rectangle, style=filled, fillcolor=white, + fontname="Arial", fontsize=10, margin="0.1,0.08"] + edge [fontname="Arial", fontsize=9] + + q_urban [label="Human consumption > Total agricultural production"] + q_semi [label="Semi-natural production > Crop production x 0.6"] + q_crop [label="Crop production > Animal ingestion"] + q_crop_int [label="Synthetic share > 40% & Crop productivity > 10 kg N/ha"] + q_ls_cond [label="Livestock density > 1 LU/ha & Imported feed share > 60%\n& Feed from semi-natural share < 40%"] + q_ls_dens [label="Livestock density > 1.3 LU/ha"] + q_conn [label="Local feed share > 30% & Manure share > 30%"] + q_conn_pr [label="Crop productivity > 30 kg N/ha"] + q_disc [label="Local feed share < 50% & Manure share < 50%\n& Synthetic share > 10%"] + + r_urban [label="Urban system", fillcolor=white, fontcolor="#6A5ACD", color="#6A5ACD", style="dashed,filled", penwidth=2] + r_semi [label="Semi-natural\nagroecosystem", fillcolor="#66a61e", fontcolor=white] + r_cr_int [label="Specialized cropping\n(intensive)", fillcolor="#F7DD5A", fontcolor=black] + r_cr_ext [label="Specialized cropping\n(extensive)", fillcolor="#FFF7C2", fontcolor=black] + r_ls_int [label="Specialized livestock\n(intensive)", fillcolor="#b3001b", fontcolor=white] + r_ls_ext [label="Specialized livestock\n(extensive)", fillcolor="#C94F6B", fontcolor=white] + r_co_int [label="Connected crop-livestock\n(intensive)", fillcolor="#7A4F20", fontcolor=white] + r_co_ext [label="Connected crop-livestock\n(extensive)", fillcolor="#AF814B", fontcolor=white] + r_di_int [label="Disconnected crop-livestock\n(intensive)",fillcolor="#E67E00", fontcolor=white] + r_di_ext [label="Disconnected crop-livestock\n(extensive)",fillcolor="#F6A640", fontcolor=black] + + {rank=same; r_semi; r_cr_int; r_cr_ext; r_ls_int; r_ls_ext; r_co_int; r_co_ext; r_di_int; r_di_ext; r_urban} + r_semi -> r_cr_int -> r_cr_ext -> r_ls_int -> r_ls_ext -> r_co_int -> r_co_ext -> r_di_int -> r_di_ext -> r_urban [style=invis] + + q_urban -> r_urban [label="Yes"] + q_urban -> q_semi [label="No"] + q_semi -> r_semi [label="Yes"] + q_semi -> q_crop [label="No"] + q_crop -> q_crop_int [label="Yes"] + q_crop -> q_ls_cond [label="No"] + q_crop_int -> r_cr_int [label="Yes"] + q_crop_int -> r_cr_ext [label="No"] + q_ls_cond -> q_ls_dens [label="Yes"] + q_ls_dens -> r_ls_int [label="Yes"] + q_ls_dens -> r_ls_ext [label="No"] + q_ls_cond -> q_conn [label="No"] + q_conn -> q_conn_pr [label="Yes"] + q_conn_pr -> r_co_int [label="Yes"] + q_conn_pr -> r_co_ext [label="No"] + q_conn -> q_disc [label="No"] + q_disc -> r_di_int [label="Yes"] + q_disc -> r_di_ext [label="No"] + } + ' + ) +} diff --git a/R/typologies_spain_stacked_bar.R b/R/typologies_spain_stacked_bar.R new file mode 100644 index 00000000..30e542bf --- /dev/null +++ b/R/typologies_spain_stacked_bar.R @@ -0,0 +1,136 @@ +typology_stacked_bars <- function() { + indicators <- create_typo_ts_plot() + n_prov_destiny <- create_n_prov_destiny() + + typologies_df <- indicators |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + Typology = dplyr::first(Typology_base), + .groups = "drop" + ) + + soil_inputs <- n_prov_destiny |> + dplyr::filter( + origin %in% + c("Deposition", "Fixation", "Synthetic"), + destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) + + import_inputs <- n_prov_destiny |> + dplyr::filter( + origin == "Outside", + destiny %in% + c( + "livestock_mono", + "livestock_rum", + "population_food", + "population_other_uses" + ) + ) + + n_inputs <- dplyr::bind_rows(soil_inputs, import_inputs) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + Total_N_input = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + df_total <- n_inputs |> + dplyr::left_join(typologies_df, by = c("year", "province_name")) |> + dplyr::group_by(year, Typology) |> + dplyr::summarise( + Total_N_input = sum(Total_N_input, na.rm = TRUE) / 1000, + .groups = "drop" + ) + + typology_colors <- c( + "Semi-natural agroecosystems" = "#66a61e", + "Specialized cropping systems (intensive)" = "#F7DD5A", + "Specialized cropping systems (extensive)" = "#FFF7C2", + "Specialized livestock systems (intensive)" = "#b3001b", + "Specialized livestock systems (extensive)" = "#C94F6B", + "Connected crop-livestock systems (intensive)" = "#7A4F20", + "Connected crop-livestock systems (extensive)" = "#AF814B", + "Disconnected crop-livestock systems (intensive)" = "#E67E00", + "Disconnected crop-livestock systems (extensive)" = "#F6A640" + ) + + df_total$Typology <- factor( + df_total$Typology, + levels = names(typology_colors) + ) + + year_breaks <- df_total$year |> + unique() |> + sort() + year_breaks <- year_breaks[year_breaks %% 20 == 0] + + p_total <- ggplot2::ggplot( + df_total, + ggplot2::aes( + x = factor(year), + y = Total_N_input, + fill = Typology + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::scale_x_discrete(breaks = year_breaks) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + title = "Total N inputs by typology", + x = "Year", + y = "Total N input (Gg N)", + fill = "Typology" + ) + + ggplot2::theme_minimal() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + df_pct <- n_inputs |> + dplyr::left_join(typologies_df, by = c("year", "province_name")) |> + dplyr::group_by(year, Typology) |> + dplyr::summarise( + Total_N_input = sum(Total_N_input, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::group_by(year) |> + dplyr::mutate( + Total_all = sum(Total_N_input, na.rm = TRUE), + Percent_N_input = Total_N_input / Total_all * 100 + ) |> + dplyr::ungroup() + + df_pct$Typology <- factor( + df_pct$Typology, + levels = names(typology_colors) + ) + + year_breaks_pct <- df_pct$year |> + unique() |> + sort() + year_breaks_pct <- year_breaks_pct[year_breaks_pct %% 20 == 0] + + p_pct <- ggplot2::ggplot( + df_pct, + ggplot2::aes( + x = factor(year), + y = Percent_N_input, + fill = Typology + ) + ) + + ggplot2::geom_bar(stat = "identity") + + ggplot2::scale_x_discrete(breaks = year_breaks_pct) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + title = "Nitrogen inputs by typology (%)", + x = "Year", + y = "Share of total N input (%)", + fill = "Typology" + ) + + ggplot2::theme_minimal() + + ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1)) + + print(p_total) + print(p_pct) + + list(total = df_total, pct = df_pct, p_total = p_total, p_pct = p_pct) +} diff --git a/R/utils.R b/R/utils.R index ea2b4be6..4c681ddc 100644 --- a/R/utils.R +++ b/R/utils.R @@ -49,6 +49,9 @@ utils::globalVariables( ".v_end", ".v_start", "", + "{AN_LS}", + "{AN_LS_OTH}", + "{AN_OTH}", "", "", "", @@ -585,6 +588,91 @@ utils::globalVariables( "is_tradeable", "other_mean", "qc_carry_forward", - "scale_new_old" + "scale_new_old", + # typology and intensification/specialization variables + "A", + "Area_ha_crops", + "Color", + "H", + "HHI", + "H_norm", + "Input", + "LU_total_row", + "MgN_dep", + "MgN_fix", + "MgN_manure", + "MgN_syn", + "MgN_urban", + "NRR", + "NUE", + "Percent_N_input", + "Synthetic_fertilizer", + "Total_N_Mg", + "Total_N_input_all", + "Total_all", + "Typology_base", + "area_ygpit_ha", + "bnf", + "box_destiny", + "circularity", + "conversion_n_dm", + "crop_N", + "crop_input", + "crop_output", + "crop_productivity", + "n_excr_mgn", + "external", + "external_GgN", + "external_dependency", + "feed_import", + "fertilizer", + "fertilizer_N", + "fertilizer_dependency", + "imported_mg_n", + "indicator", + "input_type", + "intensification", + "intensification_index", + "intensity", + "internal", + "internal_GgN", + "is_urban", + "kgN_ha", + "land_productivity", + "landuse", + "liquid", + "local_mg_n", + "mean_kgN", + "n_groups", + "n_mg_n", + "n_mgn", + "name_clean", + "nitrogen_intensity", + "organic", + "organic_share", + "pattern_fill", + "pattern_type", + "pop_mpeop_yg", + "population", + "prod_mgn", + "prod_source", + "province", + "recycled", + "sd_kgN", + "solid", + "specialization", + "specialization_index", + "surplus", + "synthetic_share", + "total_GgN", + "total_input_mg", + "total_inputs", + "value_MgN", + "x", + "xend", + "y", + "yend", + "ymax", + "ymin" ) ) diff --git a/R/whep_typologies_spain.R b/R/whep_typologies_spain.R new file mode 100644 index 00000000..89292563 --- /dev/null +++ b/R/whep_typologies_spain.R @@ -0,0 +1,217 @@ +#' @title Create WHEP typologies for Spain +#' +#' @description Calculates all decision variables for the WHEP typology +#' using only production and consumption data (no import/export). +#' +#' @param prod_destiny Data frame with consumption data +#' (food, feed, other_uses). +#' @param prod_n Data frame with production data (production_n) from +#' grafs_prod_item_n. +#' @param years Numeric vector of years to include (default = 2020). +#' +#' @return A data frame with Year, Province_name, decision variables, +#' and Category. +#' @export +create_typologies_whep <- function( + prod_destiny = create_n_prov_destiny()$prod_destiny, + prod_n = create_n_prov_destiny()$grafs_prod_item_n, + years = 2020 +) { + prod_destiny <- prod_destiny |> + dplyr::filter( + year %in% years, + province_name != "Sea", + box != "Fish", + box != "Agro-industry" + ) + + prod_n <- prod_n |> + dplyr::filter(year %in% years, province_name != "Sea") + + # --- Feed-Import ------------------------------------------------ + destiny_shares <- prod_destiny |> + dplyr::filter(destiny %in% c("food", "feed", "other_uses")) |> + dplyr::group_by(year, province_name, item) |> + dplyr::summarise( + total_consumption = sum(mg_n, na.rm = TRUE), + feed_share = sum(mg_n[destiny == "feed"], na.rm = TRUE) / + sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + import_feed_df <- prod_destiny |> + dplyr::filter(destiny == "import") |> + dplyr::left_join(destiny_shares, by = c("year", "province_name", "item")) |> + dplyr::mutate( + import_feed = mg_n * feed_share + ) |> + dplyr::select(year, province_name, item, box, import_feed) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + feed_import = sum(import_feed, na.rm = TRUE), + .groups = "drop" + ) + + # --- 1. Human share ------------------------------------------------------ + food_consumption <- prod_destiny |> + dplyr::filter(destiny %in% c("food", "other_uses")) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + food_consumption = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + production <- prod_n |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + production = sum(production_n, na.rm = TRUE), + .groups = "drop" + ) + + human_share <- food_consumption |> + dplyr::left_join(production, by = c("year", "province_name")) |> + dplyr::mutate( + human_share = ifelse(production > 0, food_consumption / production, NA) + ) + + # --- 2. Woody share -------------------------------------------------------- + woody_share <- prod_destiny |> + dplyr::filter(box %in% c("Cropland", "Semi_natural_agroecosystems")) |> + dplyr::mutate( + woody = ifelse(item %in% c("Firewood", "Acorns"), mg_n, 0) + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + woody_prod = sum(woody, na.rm = TRUE), + total_prod = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) |> + dplyr::mutate( + woody_share = ifelse(total_prod > 0, woody_prod / total_prod, NA) + ) + + # --- 3. Cropland production ------------------------------------------------ + crop_feed_list <- prod_n |> + dplyr::filter(box == "Cropland") |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + crop_prod = sum(production_n, na.rm = TRUE), + .groups = "drop" + ) + + # --- 4. Animal ingestion / livestock feed -------------------------------- + animal_ingestion <- prod_destiny |> + dplyr::filter(destiny == "feed") |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + animal_ingestion = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # --- 5. Grass vs crop feed ------------------------------------------------- + grass_feed <- prod_destiny |> + dplyr::filter( + box == "Semi_natural_agroecosystems", + item == "Grassland", + destiny == "feed" + ) |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + grass_feed_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + crop_feed <- prod_destiny |> + dplyr::filter(box == "Cropland", destiny == "feed") |> + dplyr::group_by(year, province_name) |> + dplyr::summarise( + crop_feed_N = sum(mg_n, na.rm = TRUE), + .groups = "drop" + ) + + # --- Combine all decision variables --------------------------------------- + whep_typologies <- human_share |> + dplyr::left_join(woody_share, by = c("year", "province_name")) |> + dplyr::left_join(crop_feed_list, by = c("year", "province_name")) |> + dplyr::left_join(animal_ingestion, by = c("year", "province_name")) |> + dplyr::left_join(import_feed_df, by = c("year", "province_name")) |> + dplyr::left_join(grass_feed, by = c("year", "province_name")) |> + dplyr::left_join(crop_feed, by = c("year", "province_name")) + + # --- Assign WHEP Typologies ------------------------------------------------ + whep_typologies <- whep_typologies |> + dplyr::mutate( + import_share = feed_import / animal_ingestion, + Category = dplyr::case_when( + human_share > 0.9 ~ "Urban System", + woody_share > 0.1 ~ "Woody-based system", + import_share > 0.6 ~ "Imported feed-based system", + crop_prod > animal_ingestion ~ "Cropland-based system", + grass_feed_N > crop_feed_N ~ "Local grass-based livestock system", + TRUE ~ "Local crop-based livestock system" + ) + ) + + whep_typologies +} + + +#' @title Plot of WHEP typology classification +#' +#' @description Generates a plot of province typologies over time based on +#' WHEP typology classification. +#' +#' @param whep_typologies A data frame returned by `create_typologies_whep()`. +#' +#' @return A plot showing province typology evolution from 1860 to 2020. +#' @noRd +.plot_whep_typologies <- function(whep_typologies = NULL) { + if (is.null(whep_typologies)) { + whep_typologies <- create_typologies_whep() + } + whep_typologies <- whep_typologies |> + dplyr::filter(province_name != "Sea") |> + dplyr::mutate( + human_share = tidyr::replace_na(human_share, 0), + woody_share = tidyr::replace_na(woody_share, 0), + province_name = factor( + province_name, + levels = sort(unique(province_name), decreasing = TRUE) + ) + ) + + typology_colors <- c( + "Urban System" = "#1E90FF", + "Woody-based system" = "#8B4513", + "Cropland-based system" = "#FFD700", + "Local grass-based livestock system" = "#2E8B57", + "Local crop-based livestock system" = "#C2B280", + "Imported feed-based system" = "#FF6666" + ) + + ggplot2::ggplot( + whep_typologies, + ggplot2::aes(x = year, y = province_name, fill = Category) + ) + + ggplot2::geom_tile() + + ggplot2::scale_x_continuous( + breaks = seq( + min(whep_typologies$year), + max(whep_typologies$year), + by = 20 + ), + expand = c(0, 0) + ) + + ggplot2::scale_fill_manual(values = typology_colors) + + ggplot2::labs( + x = "Year", + y = "Province", + fill = "Typology", + title = "Province type classification evolution (1860-2020)" + ) + + ggplot2::theme_minimal() + + ggplot2::theme( + axis.text.x = ggplot2::element_text(angle = 90, vjust = 0.5, hjust = 1), + axis.text.y = ggplot2::element_text(size = 8) + ) +} diff --git a/Rplot.png b/Rplot.png new file mode 100644 index 00000000..736ce5c5 Binary files /dev/null and b/Rplot.png differ diff --git a/_pkgdown.yml b/_pkgdown.yml index 6a36b4b4..35d1738b 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -11,7 +11,7 @@ home: href: https://lbm364dl.github.io/follow-the-workflow authors: - European Research Council: + European Research Council: html: "European Research Council" href: "https://erc.europa.eu/homepage" @@ -111,7 +111,25 @@ reference: Get a tidy dataframe with the found sources for different data. contents: - expand_trade_sources - + + +- title: Nitrogen typologies Spain + desc: > + Functions to generate data for N inputs, outputs, production, destinies, + typologies, etc. + contents: + - create_typologies_of_josette + - create_typologies_grafs_spain + - create_alfredos_typologies + - create_typologies_whep + - create_grafs_plot_df + - create_n_prov_destiny + - create_n_soil_inputs + - create_n_production + - calculate_nue_crops + - calculate_nue_livestock + - calculate_system_nue + - create_n_nat_destiny - title: Gap filling functions desc: > diff --git a/data/whep_inputs.rda b/data/whep_inputs.rda index 853275f5..f7dd15dd 100644 Binary files a/data/whep_inputs.rda and b/data/whep_inputs.rda differ diff --git a/inst/extdata/whep_inputs.csv b/inst/extdata/whep_inputs.csv index 6a3af9e2..d63aa2e8 100644 --- a/inst/extdata/whep_inputs.csv +++ b/inst/extdata/whep_inputs.csv @@ -1,44 +1,46 @@ -alias,board_url,version -commodity_balance_sheet,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123343Z-114b5 -bilateral_trade,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123347Z-2c392 -processing_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123348Z-06c63 -feed_intake,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123349Z-660d7 -primary_prod,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123350Z-74e7f -crop_residues,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123350Z-1d7be -read_example,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250721T152756Z-f00da -luh2_v2h_states,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20251006T152247Z-942cc -faostat-production,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T111448Z-4fffe -faostat-production-old,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113330Z-572c9 -eu-agridb-fodder,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113350Z-145f3 -faostat-emissions-livestock,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113403Z-23bf8 -faostat-trade,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120525Z-7b85f -luh2-areas,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T112205Z-4d7cb -international-yields,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T112220Z-b6167 -historical-trade-exports,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T121518Z-d548c -historical-trade-imports,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T121523Z-72a63 -gdp-population,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T085548Z-ec5a9 -faostat-production-processed,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113346Z-5637e -faostat-fbs-new,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113807Z-b184a -faostat-fbs-old,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T114920Z-874a4 -faostat-cbs-old-animal,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T115537Z-9e898 -faostat-cbs-old-crops,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120222Z-3d77b -faostat-cbs-new,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120441Z-ff6fe -fishstat-trade,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260330T095440Z-8bd94 -grafs_prod_destiny,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T110924Z-0508f -biomass_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T082553Z-f2fac -codes_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T143014Z-a83d1 -crop_area_npp_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T100115Z-8cb10 -crop_area_npp_ygpitr_no_fallow,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T093400Z-9bdb1 -feed_avail_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T120137Z-6239a -livestock_prod_ygps,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T103307Z-1d546 -n_balance_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T064859Z-8dedf -n_excretion_ygs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250814T093633Z-c429a -pie_full_destinies_fm,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T101639Z-96c11 -population_yg,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T102929Z-b4f44 -processed_prov_fixed,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T140654Z-23fcb -codes_coefs_items_full,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T081525Z-c6479 -npp_ygpit,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T084811Z-9e48b -intake_ygiac,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T085614Z-4981f -grafs_crop_categories,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260127T114257Z-afabb -livestock_units,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260128T120326Z-40647 -n_fix_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260202T110154Z-2383a +alias,board_url,version,pin_name +commodity_balance_sheet,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123343Z-114b5, +bilateral_trade,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123347Z-2c392, +processing_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123348Z-06c63, +feed_intake,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123349Z-660d7, +primary_prod,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123350Z-74e7f, +crop_residues,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250714T123350Z-1d7be, +read_example,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20250721T152756Z-f00da, +luh2_v2h_states,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20251006T152247Z-942cc, +faostat-production,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T111448Z-4fffe, +faostat-production-old,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113330Z-572c9, +eu-agridb-fodder,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113350Z-145f3, +faostat-emissions-livestock,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113403Z-23bf8, +faostat-trade-totals,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120525Z-7b85f, +luh2-areas,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T112205Z-4d7cb, +international-yields,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T112220Z-b6167, +historical-trade-exports,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T121518Z-d548c, +historical-trade-imports,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T121523Z-72a63, +gdp-population,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T085548Z-ec5a9, +faostat-production-processed,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113346Z-5637e, +faostat-fbs-new,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T113807Z-b184a, +faostat-fbs-old,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T114920Z-874a4, +faostat-cbs-old-animal,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T115537Z-9e898, +faostat-cbs-old-crops,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120222Z-3d77b, +faostat-cbs-new,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260325T120441Z-ff6fe, +fishstat-trade,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/world/_pins.yaml,20260330T095440Z-8bd94, +grafs_prod_destiny,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T110924Z-0508f, +biomass_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T082553Z-f2fac, +codes_coefs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T143014Z-a83d1, +crop_area_npp_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260427T074140Z-bd4e6, +crop_area_npp_ygpitr_no_fallow,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260427T075016Z-e340e, +feed_avail_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T120137Z-6239a, +livestock_prod_ygps,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260427T084010Z-7a1f7, +n_excretion_ygs,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260508T073306Z-485cb, +pie_full_destinies_fm,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260427T091304Z-76d13, +population_yg,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T102929Z-b4f44, +processed_prov_fixed,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250724T140654Z-23fcb, +codes_coefs_items_full,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20250728T081525Z-c6479, +npp_ygpit,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260508T072412Z-feb09, +intake_ygiac,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260508T072034Z-f0f0b, +grafs_crop_categories,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260127T114257Z-afabb, +livestock_units,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260128T120326Z-40647, +n_balance_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260508T072851Z-2464a, +n_fix_ygpit_all,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260427T085738Z-89b87, +n_balance_ygpit_all_old,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260422T070109Z-5c638, +stock_prod_ygps,https://saco.csic.es/public.php/dav/files/nrJ3JGPZyZeQMW8/Model%20inputs/nitrogen_spain/_pins.yaml,20260428T141215Z-9a171, diff --git a/inst/scripts/explain_intensification_specialization_indicators.R b/inst/scripts/explain_intensification_specialization_indicators.R new file mode 100644 index 00000000..31a050d3 --- /dev/null +++ b/inst/scripts/explain_intensification_specialization_indicators.R @@ -0,0 +1,409 @@ +#!/usr/bin/env Rscript + +suppressPackageStartupMessages({ + library(dplyr) + library(ggplot2) + library(readr) + library(tidyr) + library(stringr) +}) + +if (!requireNamespace("devtools", quietly = TRUE)) { + stop("Package 'devtools' is required to run this script.") +} + +devtools::load_all(".") + + +build_indicator_data <- function() { + flows <- create_n_prov_destiny() + npp_ygpit <- whep_read_file("npp-ygpit") + + area_df <- npp_ygpit |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + area_ha = sum(Area_ygpit_ha, na.rm = TRUE), + .groups = "drop" + ) + + n_inputs <- flows |> + dplyr::filter( + Origin %in% c("Synthetic", "Deposition", "Fixation"), + Destiny %in% c("Cropland", "semi_natural_agroecosystems") + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + total_new_input_mgn = sum(MgN, na.rm = TRUE), + .groups = "drop" + ) + + soil_input_shares <- flows |> + dplyr::filter( + Destiny %in% c("Cropland", "semi_natural_agroecosystems"), + Origin %in% + c( + "Synthetic", + "Livestock", + "Fixation", + "Deposition", + "People" + ) + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + synthetic_share = dplyr::if_else( + sum(MgN, na.rm = TRUE) > 0, + sum(MgN[Origin == "Synthetic"], na.rm = TRUE) / + sum(MgN, na.rm = TRUE), + NA_real_ + ), + .groups = "drop" + ) + + feed_import_share <- flows |> + dplyr::filter( + Destiny %in% c("livestock_mono", "livestock_rum"), + Origin %in% c("Cropland", "semi_natural_agroecosystems", "Outside") + ) |> + dplyr::group_by(Year, Province_name) |> + dplyr::summarise( + feed_import_share = dplyr::if_else( + sum(MgN, na.rm = TRUE) > 0, + sum(MgN[Origin == "Outside"], na.rm = TRUE) / + sum(MgN, na.rm = TRUE), + NA_real_ + ), + .groups = "drop" + ) + + typology_df <- create_typo_ts_plot() |> + dplyr::select(Year, Province_name, Typology_base) |> + dplyr::rename(Typology = Typology_base) |> + dplyr::mutate( + Typology = stringr::str_remove( + Typology, + " \\((intensive|extensive)\\)" + ) + ) + + n_inputs |> + dplyr::left_join(area_df, by = c("Year", "Province_name")) |> + dplyr::left_join(soil_input_shares, by = c("Year", "Province_name")) |> + dplyr::left_join(feed_import_share, by = c("Year", "Province_name")) |> + dplyr::left_join(typology_df, by = c("Year", "Province_name")) |> + dplyr::mutate( + intensification_kg_ha = dplyr::if_else( + area_ha > 0, + (total_new_input_mgn * 1000) / area_ha, + NA_real_ + ), + specialization_max = pmax(synthetic_share, feed_import_share), + specialization_mean = (synthetic_share + feed_import_share) / 2, + specialization_l2 = sqrt( + (synthetic_share^2 + feed_import_share^2) / 2 + ), + Period = dplyr::case_when( + Year < 1900 ~ "1860-1900", + Year < 1950 ~ "1900-1950", + Year < 1990 ~ "1950-1990", + TRUE ~ "1990-2021" + ) + ) |> + dplyr::filter( + !is.na(Typology), + !is.na(intensification_kg_ha), + !is.na(synthetic_share), + !is.na(feed_import_share) + ) +} + +build_toy_indicator_data <- function() { + set.seed(42) + + years <- seq(1860, 2020, by = 10) + provinces <- paste0("Province_", seq_len(12)) + typologies <- c( + "Semi-natural agroecosystems", + "Specialized cropping systems", + "Specialized livestock systems", + "Connected crop-livestock systems", + "Disconnected crop-livestock systems" + ) + + tidyr::crossing( + Year = years, + Province_name = provinces + ) |> + dplyr::mutate( + Typology = sample(typologies, dplyr::n(), replace = TRUE), + synthetic_share = pmin( + 0.95, + pmax( + 0.02, + 0.15 + (Year - 1860) / 260 + stats::rnorm(dplyr::n(), 0, 0.08) + ) + ), + feed_import_share = pmin( + 0.95, + pmax( + 0.02, + 0.10 + (Year - 1860) / 300 + stats::rnorm(dplyr::n(), 0, 0.10) + ) + ), + intensification_kg_ha = pmax( + 5, + 25 + (Year - 1860) * 0.8 + stats::rnorm(dplyr::n(), 0, 25) + ), + specialization_max = pmax(synthetic_share, feed_import_share), + specialization_mean = (synthetic_share + feed_import_share) / 2, + specialization_l2 = sqrt( + (synthetic_share^2 + feed_import_share^2) / 2 + ), + Period = dplyr::case_when( + Year < 1900 ~ "1860-1900", + Year < 1950 ~ "1900-1950", + Year < 1990 ~ "1950-1990", + TRUE ~ "1990-2021" + ) + ) +} + +plot_intens_vs_spec <- function(df, out_dir) { + p <- ggplot2::ggplot( + df, + ggplot2::aes( + x = intensification_kg_ha, + y = specialization_max, + color = Typology + ) + ) + + ggplot2::geom_point(alpha = 0.30, size = 1.2) + + ggplot2::facet_wrap(~Period) + + ggplot2::labs( + title = "Intensification vs specialization (max definition)", + subtitle = "Specialization = max(synthetic share, feed import share)", + x = "Intensification (kg N / ha)", + y = "Specialization (0-1)", + color = "Typology" + ) + + ggplot2::theme_minimal(base_size = 11) + + ggplot2::ggsave( + filename = file.path(out_dir, "01_intensification_vs_specialization.png"), + plot = p, + width = 11, + height = 7, + dpi = 300 + ) +} + +plot_component_shares <- function(df, out_dir) { + ts_df <- df |> + dplyr::group_by(Year) |> + dplyr::summarise( + synthetic_share = mean(synthetic_share, na.rm = TRUE), + feed_import_share = mean(feed_import_share, na.rm = TRUE), + specialization_max = mean(specialization_max, na.rm = TRUE), + intensification_kg_ha = mean(intensification_kg_ha, na.rm = TRUE), + .groups = "drop" + ) + + share_long <- ts_df |> + dplyr::select( + Year, + synthetic_share, + feed_import_share, + specialization_max + ) |> + tidyr::pivot_longer( + cols = -Year, + names_to = "indicator", + values_to = "value" + ) + + p <- ggplot2::ggplot( + share_long, + ggplot2::aes(x = Year, y = value, color = indicator) + ) + + ggplot2::geom_line(linewidth = 1) + + ggplot2::scale_color_manual( + values = c( + synthetic_share = "#E41A1C", + feed_import_share = "#377EB8", + specialization_max = "#4DAF4A" + ), + labels = c( + synthetic_share = "Synthetic share", + feed_import_share = "Feed import share", + specialization_max = "Specialization (max)" + ) + ) + + ggplot2::labs( + title = "Specialization components over time", + subtitle = "National means across provinces", + x = "Year", + y = "Share (0-1)", + color = "Series" + ) + + ggplot2::theme_minimal(base_size = 11) + + ggplot2::ggsave( + filename = file.path(out_dir, "02_specialization_components.png"), + plot = p, + width = 11, + height = 7, + dpi = 300 + ) +} + +plot_spec_definitions <- function(df, out_dir) { + compare_df <- df |> + dplyr::group_by(Year) |> + dplyr::summarise( + specialization_max = mean(specialization_max, na.rm = TRUE), + specialization_mean = mean(specialization_mean, na.rm = TRUE), + specialization_l2 = mean(specialization_l2, na.rm = TRUE), + .groups = "drop" + ) |> + tidyr::pivot_longer( + cols = -Year, + names_to = "definition", + values_to = "value" + ) + + p <- ggplot2::ggplot( + compare_df, + ggplot2::aes(x = Year, y = value, color = definition) + ) + + ggplot2::geom_line(linewidth = 1) + + ggplot2::scale_color_manual( + values = c( + specialization_max = "#1B9E77", + specialization_mean = "#D95F02", + specialization_l2 = "#7570B3" + ), + labels = c( + specialization_max = "max(synthetic, feed import)", + specialization_mean = "(synthetic + feed import) / 2", + specialization_l2 = "sqrt((synthetic^2 + feed import^2)/2)" + ) + ) + + ggplot2::labs( + title = "Specialization definition sensitivity", + subtitle = "How the specialization trend changes with formula choice", + x = "Year", + y = "Specialization (0-1)", + color = "Definition" + ) + + ggplot2::theme_minimal(base_size = 11) + + ggplot2::ggsave( + filename = file.path(out_dir, "03_specialization_definitions.png"), + plot = p, + width = 11, + height = 7, + dpi = 300 + ) +} + +write_explanation <- function(df, out_dir) { + summary_df <- df |> + dplyr::group_by(Year) |> + dplyr::summarise( + intensification_kg_ha = mean(intensification_kg_ha, na.rm = TRUE), + synthetic_share = mean(synthetic_share, na.rm = TRUE), + feed_import_share = mean(feed_import_share, na.rm = TRUE), + specialization_max = mean(specialization_max, na.rm = TRUE), + specialization_mean = mean(specialization_mean, na.rm = TRUE), + specialization_l2 = mean(specialization_l2, na.rm = TRUE), + .groups = "drop" + ) + + readr::write_csv( + summary_df, + file.path(out_dir, "indicator_summary_timeseries.csv") + ) + + txt <- c( + "Intensification and specialization indicators: interpretation guide", + "", + "1) Intensification", + " Formula: intensification = new N inputs / area", + " In code: (Synthetic + Deposition + Fixation) in Mg N, converted to kg", + " and divided by area (ha).", + "", + "2) Specialization components", + " synthetic_share = Synthetic / total soil N inputs", + " feed_import_share = Outside feed N / total feed N to livestock", + "", + "3) Specialization in current plot code", + " specialization_max = max(synthetic_share, feed_import_share)", + " Interpretation: a province is classified as specialized when either", + " synthetic dependency OR imported feed dependency is high.", + "", + "4) Why confusion happens", + " The repository currently uses multiple specialization formulas in", + " different functions (max, average, and diversity-based metrics).", + " These are not numerically equivalent and can show different trends.", + "", + "5) Suggested rule", + " Keep one primary definition for reporting (e.g. specialization_max)", + " and show alternatives only in sensitivity checks.", + "", + "Generated files:", + " 01_intensification_vs_specialization.png", + " 02_specialization_components.png", + " 03_specialization_definitions.png", + " indicator_summary_timeseries.csv" + ) + + writeLines(txt, con = file.path(out_dir, "indicator_explanation.txt")) +} + +run_indicator_explainer <- function( + out_dir = "inst/scripts/output/indicator_explainer", + use_toy_data = FALSE +) { + if (!dir.exists(out_dir)) { + dir.create(out_dir, recursive = TRUE) + } + + indicator_df <- if (isTRUE(use_toy_data)) { + build_toy_indicator_data() + } else { + build_indicator_data() + } + + if (nrow(indicator_df) == 0) { + stop( + paste0( + "No rows available for plotting after data preparation. ", + "Common causes are: missing typology join values, zero denominators ", + "in share calculations, or zero area. Try `use_toy_data = TRUE` to ", + "verify plotting and inspect intermediate joins in `build_indicator_data()`." + ) + ) + } + + message( + "Plotting ", + nrow(indicator_df), + " rows across years ", + min(indicator_df$Year, na.rm = TRUE), + "-", + max(indicator_df$Year, na.rm = TRUE), + "." + ) + + plot_intens_vs_spec(indicator_df, out_dir) + plot_component_shares(indicator_df, out_dir) + plot_spec_definitions(indicator_df, out_dir) + write_explanation(indicator_df, out_dir) + + message("Created indicator explainer outputs in: ", out_dir) + invisible(indicator_df) +} + +if (sys.nframe() == 0) { + run_indicator_explainer(use_toy_data = TRUE) +} diff --git a/inst/scripts/output/indicator_explainer/01_intensification_vs_specialization.png b/inst/scripts/output/indicator_explainer/01_intensification_vs_specialization.png new file mode 100644 index 00000000..9d3b7c6a Binary files /dev/null and b/inst/scripts/output/indicator_explainer/01_intensification_vs_specialization.png differ diff --git a/inst/scripts/output/indicator_explainer/02_specialization_components.png b/inst/scripts/output/indicator_explainer/02_specialization_components.png new file mode 100644 index 00000000..26d26e72 Binary files /dev/null and b/inst/scripts/output/indicator_explainer/02_specialization_components.png differ diff --git a/inst/scripts/output/indicator_explainer/03_specialization_definitions.png b/inst/scripts/output/indicator_explainer/03_specialization_definitions.png new file mode 100644 index 00000000..83d652a3 Binary files /dev/null and b/inst/scripts/output/indicator_explainer/03_specialization_definitions.png differ diff --git a/inst/scripts/output/indicator_explainer/indicator_explanation.txt b/inst/scripts/output/indicator_explainer/indicator_explanation.txt new file mode 100644 index 00000000..ee8d9418 --- /dev/null +++ b/inst/scripts/output/indicator_explainer/indicator_explanation.txt @@ -0,0 +1,30 @@ +Intensification and specialization indicators: interpretation guide + +1) Intensification + Formula: intensification = new N inputs / area + In code: (Synthetic + Deposition + Fixation) in Mg N, converted to kg + and divided by area (ha). + +2) Specialization components + synthetic_share = Synthetic / total soil N inputs + feed_import_share = Outside feed N / total feed N to livestock + +3) Specialization in current plot code + specialization_max = max(synthetic_share, feed_import_share) + Interpretation: a province is classified as specialized when either + synthetic dependency OR imported feed dependency is high. + +4) Why confusion happens + The repository currently uses multiple specialization formulas in + different functions (max, average, and diversity-based metrics). + These are not numerically equivalent and can show different trends. + +5) Suggested rule + Keep one primary definition for reporting (e.g. specialization_max) + and show alternatives only in sensitivity checks. + +Generated files: + 01_intensification_vs_specialization.png + 02_specialization_components.png + 03_specialization_definitions.png + indicator_summary_timeseries.csv diff --git a/inst/scripts/output/indicator_explainer/indicator_summary_timeseries.csv b/inst/scripts/output/indicator_explainer/indicator_summary_timeseries.csv new file mode 100644 index 00000000..cd7c66f6 --- /dev/null +++ b/inst/scripts/output/indicator_explainer/indicator_summary_timeseries.csv @@ -0,0 +1,18 @@ +Year,intensification_kg_ha,synthetic_share,feed_import_share,specialization_max,specialization_mean,specialization_l2 +1860,23.41817275428791,0.163839428148114,0.13246678613384305,0.20633795137368208,0.1481531071409785,0.1614583108845931 +1870,27.47434275417909,0.19580832233159964,0.1528665506990492,0.24410349350303112,0.1743374365153244,0.19188889914623083 +1880,45.97563268379502,0.19600653287977343,0.19912242015543305,0.23187645823519687,0.19756447651760325,0.2013759182868726 +1890,50.71105769802687,0.28105302165530643,0.1962634592912962,0.30314530015078744,0.23865824047330134,0.2500434224050141 +1900,62.25151079317907,0.30807910912584446,0.18487027608588316,0.3162618509063544,0.2464746926058638,0.2627118750892945 +1910,69.34318780500827,0.32908265965506567,0.26375165584985766,0.3406446423422281,0.29641715775246164,0.30221594844964583 +1920,63.05953714133314,0.3859363402137666,0.31475172106197813,0.40343313699828065,0.35034403063787234,0.35655976589576105 +1930,77.74123541606596,0.4317820258921973,0.3605429741330136,0.45602788112918813,0.39616250001260545,0.402854156201596 +1940,94.86535838021788,0.45150518985954463,0.3625436075214301,0.4786998992119103,0.40702439869048734,0.4163473496230744 +1950,90.43654885400824,0.49549705029899727,0.3406137894488268,0.511271508897392,0.4180554198739121,0.43121158985215907 +1960,87.92015524923055,0.5342017634431041,0.4168057755698774,0.5398629812270468,0.47550376950649076,0.4833896111547185 +1970,103.95098032949726,0.5811256449618459,0.4718785626312063,0.5890827495330805,0.5265021037965261,0.5326154118053386 +1980,125.25881309840365,0.6036583004008548,0.5156112274861594,0.6262945829115101,0.5596347639435071,0.5654207847474901 +1990,128.24177607993187,0.6543745932709056,0.5421561452702045,0.6871781485241437,0.5982653692705551,0.6070108479345027 +2000,130.08604510524125,0.6636787336941777,0.5592420015055215,0.664311769943315,0.6114603675998496,0.6152547055031983 +2010,136.07972885709125,0.7342280770282131,0.6444518554838397,0.7439048676305174,0.6893399662560264,0.6932596575613177 +2020,154.44019471038916,0.7387937497048239,0.6190619943187623,0.7389553285064933,0.678927872011793,0.6845903835398709 diff --git a/inst/scripts/prepare_upload.R b/inst/scripts/prepare_upload.R index aab945d9..80364094 100644 --- a/inst/scripts/prepare_upload.R +++ b/inst/scripts/prepare_upload.R @@ -77,7 +77,6 @@ prepare_for_upload <- function(input_path, data_name, ...) { } prepare_for_upload( - "C:/PhD/GRAFS/Inputs_SACO/grafs_lookups.xlsx", - "livestock_units", - sheet = "lu_factors" + "C:/PhD/GRAFS/Inputs_SACO/inputs_saco_new/n_balance_ygpit_all.csv", + "n_balance_ygpit_all" ) diff --git a/man/calculate_nue_crops.Rd b/man/calculate_nue_crops.Rd index cf4ad425..20d0243a 100644 --- a/man/calculate_nue_crops.Rd +++ b/man/calculate_nue_crops.Rd @@ -11,7 +11,7 @@ calculate_nue_crops(example = FALSE) remote data. Default is \code{FALSE}.} } \value{ -A tibble containing nitrogen use efficiency (NUE) for crops. +A tibble containing nitrogen input, production, and NUE data. It includes the following columns: \itemize{ \item \code{year}: Year. @@ -19,7 +19,14 @@ It includes the following columns: \item \code{item}: The item which was produced, defined in \code{names_biomass_cb}. \item \code{box}: One of the two systems of the GRAFS model: cropland or semi-natural agroecosystems. -\item \code{nue}: Nitrogen Use Efficiency as a percentage (\%). +\item \code{deposition}: Atmospheric nitrogen deposition in megagrams (Mg). +\item \code{fixation}: Nitrogen fixation in megagrams (Mg). +\item \code{synthetic}: Synthetic nitrogen fertilizer applied to the land in +megagrams (Mg). +\item \code{manure}: Nitrogen in manure applied to the land in megagrams (Mg). +\item \code{urban}: Nitrogen in wastewater from human sources in megagrams (Mg). +\item \code{prod}: Produced nitrogen in megagrams (Mg). +\item \code{inputs}: Total nitrogen inputs in megagrams (Mg). } } \description{ diff --git a/man/create_alfredos_typologies.Rd b/man/create_alfredos_typologies.Rd new file mode 100644 index 00000000..3cc98bc4 --- /dev/null +++ b/man/create_alfredos_typologies.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/alfredo_typologies.R +\name{create_alfredos_typologies} +\alias{create_alfredos_typologies} +\title{Alfredo's typology classification} +\usage{ +create_alfredos_typologies( + soil_inputs = NULL, + prod_destiny = NULL, + years = 1860:2020 +) +} +\arguments{ +\item{soil_inputs}{A data frame containing soil nitrogen inputs.} + +\item{prod_destiny}{A data frame containing production and destiny data.} + +\item{years}{Years between 1860 and 2020.} +} +\value{ +A data frame with the columns: Year, Province_name, grass_N, +fertilizer_N, feed_import_N, woody, herbaceous, woody_share, and Category. +} +\description{ +Calculates typologies for provinces based on grassland, +fertilizer, imported feed, and woody/herbaceous shares. +} diff --git a/man/create_grafs_plot_df.Rd b/man/create_grafs_plot_df.Rd new file mode 100644 index 00000000..f0ad1c95 --- /dev/null +++ b/man/create_grafs_plot_df.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/grafs_plot_df.R +\name{create_grafs_plot_df} +\alias{create_grafs_plot_df} +\title{Create GRAFS plot dataset} +\usage{ +create_grafs_plot_df() +} +\value{ +A tibble containing province, year, label, data, and alignment. +} +\description{ +Combines land input data and N flows from crops, livestock, imports, and +exports to generate a dataset of nitrogen (MgN) by province and year, to +create a GRAFS plot, offered by Alfredo Rodríguez. +} diff --git a/man/create_n_nat_destiny.Rd b/man/create_n_nat_destiny.Rd index cd359404..0a1062d6 100644 --- a/man/create_n_nat_destiny.Rd +++ b/man/create_n_nat_destiny.Rd @@ -2,32 +2,12 @@ % Please edit documentation in R/n_prov_destiny.R \name{create_n_nat_destiny} \alias{create_n_nat_destiny} -\title{GRAFS Nitrogen (N) flows at Spain national level} +\title{GRAFS Nitrogen (N) flows – National Spain} \usage{ -create_n_nat_destiny(example = FALSE) -} -\arguments{ -\item{example}{If \code{TRUE}, return a small example output without downloading -remote data. Default is \code{FALSE}.} +create_n_nat_destiny() } \value{ A final tibble containing national N flow data by origin and destiny. -It includes the following columns: -\itemize{ -\item \code{year}: The year in which the recorded event occurred. -\item \code{item}: The item which was produced, defined in \code{names_biomass_cb}. -\item \code{irrig_cat}: Irrigation form (irrigated or rainfed) -\item \code{box}: One of the GRAFS model systems: cropland, -Semi-natural agroecosystems, Livestock, Fish, or Agro-industry. -\item \code{origin}: The origin category of N: Cropland, -Semi-natural agroecosystems, Livestock, Fish, Agro-industry, Deposition, -Fixation, Synthetic, People (waste water), Livestock (manure). -\item \code{destiny}: The destiny category of N: population_food, -population_other_uses, livestock_mono, livestock_rum (feed), export, -Cropland (for N soil inputs). -\item \code{mg_n}: Nitrogen amount in megagrams (Mg). -\item \code{province_name}: Set to "Spain" for all national-level rows. -} } \description{ Provides N flows of the Spanish agro-food system on a national level @@ -37,6 +17,3 @@ internal trade between provinces. All production, consumption and soil inputs are aggregated nationally before calculating trade with the outside. } -\examples{ -create_n_nat_destiny(example = TRUE) -} diff --git a/man/create_n_prov_destiny.Rd b/man/create_n_prov_destiny.Rd index 63d9eab9..b59f5fc6 100644 --- a/man/create_n_prov_destiny.Rd +++ b/man/create_n_prov_destiny.Rd @@ -4,11 +4,7 @@ \alias{create_n_prov_destiny} \title{GRAFS Nitrogen (N) flows} \usage{ -create_n_prov_destiny(example = FALSE) -} -\arguments{ -\item{example}{If \code{TRUE}, return a small example output without downloading -remote data. Default is \code{FALSE}.} +create_n_prov_destiny() } \value{ A final tibble containing N flow data by origin and destiny. @@ -26,7 +22,7 @@ Fixation, Synthetic, People (waste water), Livestock (manure). \item \code{destiny}: The destiny category of N: population_food, population_other_uses, livestock_mono, livestock_rum (feed), export, Cropland (for N soil inputs). -\item \code{mg_n}: Nitrogen amount in megagrams (Mg). +\item \code{MgN}: Nitrogen amount in megagrams (Mg). } } \description{ @@ -39,6 +35,3 @@ where N goes to, which includes export, population food, population other uses and feed or cropland (in case of N soil inputs). Processed items, residues, woody crops, grazed weeds are taken into account. } -\examples{ -create_n_prov_destiny(example = TRUE) -} diff --git a/man/create_typologies_grafs_spain.Rd b/man/create_typologies_grafs_spain.Rd new file mode 100644 index 00000000..e6a12acb --- /dev/null +++ b/man/create_typologies_grafs_spain.Rd @@ -0,0 +1,49 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Typologies_Julia.R +\name{create_typologies_grafs_spain} +\alias{create_typologies_grafs_spain} +\title{Typologies of Julia} +\usage{ +create_typologies_grafs_spain( + make_map = TRUE, + shapefile_path = paste0("C:/PhD/GRAFS/Production Boxes/", + "Final Files/Inputs/ne_10m_admin_1_states_provinces.shp"), + map_year = 1980 +) +} +\arguments{ +\item{make_map}{If TRUE a map of the typologies will be created.} + +\item{shapefile_path}{Path to the shapefile used for mapping provinces.} + +\item{map_year}{The year for which the typology map is created.} +} +\value{ +A tibble with the classification of Spanish provinces into typologies. +It contains the following columns: +\itemize{ +\item \code{year}: The year in which the classification is performed. +\item \code{province_name}: The name of the Spanish province. +\item \code{livestock_density}: Livestock units (LU) per hectare of agricultural area +(UAA). Reflects the intensity of animal farming. +\item \code{productivity_kgN_ha}: Crop N productivity, in kilograms of N harvested +per hectare of cropland. +\item \code{semi_nat_share}: Share of total feed coming from semi-natural +agroecosystems. Expressed between 0 and 1. +\item \code{feed_imported_share}: Share of feed that is imported. Expressed between +0 and 1. +\item \code{typology}: Assigned typology category for each province. +This is based on thresholds in livestock density, crop +productivity, and feed patterns. The typologies include: +- \verb{Specialized cropping system} +- \verb{Extensive cropping system} +- \verb{Extensive mixed crop-livestock system} +- \verb{Intensive mixed crop-livestock system} +- \verb{Specialized livestock-farming system} +} +} +\description{ +Typologies of provinces in Spain based on nitrogen (N) production +data of crops and livestock, using various input datasets and generating +classification maps and data frames. +} diff --git a/man/create_typologies_of_josette.Rd b/man/create_typologies_of_josette.Rd new file mode 100644 index 00000000..a52fef67 --- /dev/null +++ b/man/create_typologies_of_josette.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Typologies_Josette.R +\name{create_typologies_of_josette} +\alias{create_typologies_of_josette} +\title{Typologies of Josette} +\usage{ +create_typologies_of_josette( + make_map = TRUE, + shapefile_path = paste0("C:/PhD/GRAFS/Production Boxes/", + "Final Files/Inputs/ne_10m_admin_1_states_provinces.shp"), + map_year = 1980 +) +} +\arguments{ +\item{make_map}{If TRUE a map of the typologies will be created.} + +\item{shapefile_path}{Path to the shapefile used for mapping provinces.} + +\item{map_year}{The year for which the typology map is created.} +} +\value{ +A tibble with the typology classification per year and province. +} +\description{ +Typologies of provinces in Spain based on nitrogen (N) production +data of crops and livestock, considering multiple data inputs and +producing classification maps and data frames. +} diff --git a/man/create_typologies_whep.Rd b/man/create_typologies_whep.Rd new file mode 100644 index 00000000..7903b98f --- /dev/null +++ b/man/create_typologies_whep.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/whep_typologies_spain.R +\name{create_typologies_whep} +\alias{create_typologies_whep} +\title{Create WHEP typologies for Spain} +\usage{ +create_typologies_whep( + prod_destiny = create_n_prov_destiny()$prod_destiny, + prod_n = create_n_prov_destiny()$grafs_prod_item_n, + years = 2020 +) +} +\arguments{ +\item{prod_destiny}{Data frame with consumption data +(food, feed, other_uses).} + +\item{prod_n}{Data frame with production data (production_n) from +grafs_prod_item_n.} + +\item{years}{Numeric vector of years to include (default = 2020).} +} +\value{ +A data frame with Year, Province_name, decision variables, +and Category. +} +\description{ +Calculates all decision variables for the WHEP typology +using only production and consumption data (no import/export). +} diff --git a/man/plot_input_output.Rd b/man/plot_input_output.Rd new file mode 100644 index 00000000..3dee2503 --- /dev/null +++ b/man/plot_input_output.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/input_output_plots.R +\name{plot_input_output} +\alias{plot_input_output} +\title{Plot N inputs, production and surplus} +\usage{ +plot_input_output(system = c("Cropland", "semi_natural_agroecosystems")) +} +\arguments{ +\item{system}{Character. One of "Cropland" or +"semi_natural_agroecosystems".} +} +\value{ +A ggplot object. +} +\description{ +Plot N inputs, production and surplus +} diff --git a/man/plot_input_output_livestock.Rd b/man/plot_input_output_livestock.Rd new file mode 100644 index 00000000..400592f8 --- /dev/null +++ b/man/plot_input_output_livestock.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/input_output_plots.R +\name{plot_input_output_livestock} +\alias{plot_input_output_livestock} +\title{Plot N inputs, production and surplus for Livestock system} +\usage{ +plot_input_output_livestock() +} +\value{ +A ggplot object. +} +\description{ +Plot N inputs, production and surplus for Livestock system +} diff --git a/man/plot_input_output_system.Rd b/man/plot_input_output_system.Rd new file mode 100644 index 00000000..fad8db44 --- /dev/null +++ b/man/plot_input_output_system.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/input_output_plots.R +\name{plot_input_output_system} +\alias{plot_input_output_system} +\title{Plot N inputs and uses for full agro-food system (system level)} +\usage{ +plot_input_output_system() +} +\value{ +A ggplot object. +} +\description{ +Plot N inputs and uses for full agro-food system (system level) +} diff --git a/tree.html b/tree.html new file mode 100644 index 00000000..d0a46b78 --- /dev/null +++ b/tree.html @@ -0,0 +1,1248 @@ + + + + +grViz + + + + + + + + +
+
+
+ + + +