- Updated all CI maps to use tm_scale_continuous() for proper tmap v4 compatibility - Added fixed color scale limits (1-8 for CI, -3 to +3 for differences) for consistent field comparison - Fixed YAML header formatting issues in CI_report_dashboard_planet.Rmd - Positioned RGB map before CI overview map as requested - Removed all obsolete use_breaks parameter references - Enhanced error handling and logging throughout the pipeline - Added new experimental analysis scripts and improvements to mosaic creation
513 lines
18 KiB
R
513 lines
18 KiB
R
})
|
|
# Verify farm_health_data exists and has content
|
|
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
|
|
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
|
|
# Create minimal fallback data
|
|
tryCatch({
|
|
# Get fields from boundaries
|
|
fields <- unique(AllPivots0$field)
|
|
# Create basic data frame with just field names
|
|
farm_health_data <- data.frame(
|
|
field = fields,
|
|
mean_ci = rep(NA, length(fields)),
|
|
ci_change = rep(NA, length(fields)),
|
|
ci_uniformity = rep(NA, length(fields)),
|
|
status = rep("Unknown", length(fields)),
|
|
anomaly_type = rep("Unknown", length(fields)),
|
|
priority_level = rep(5, length(fields)), # Low priority
|
|
age_weeks = rep(NA, length(fields)),
|
|
harvest_readiness = rep("Unknown", length(fields)),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
safe_log("Created fallback farm_health_data with basic field information")
|
|
}, error = function(e) {
|
|
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
|
|
farm_health_data <<- data.frame(
|
|
field = character(),
|
|
mean_ci = numeric(),
|
|
ci_change = numeric(),
|
|
ci_uniformity = numeric(),
|
|
status = character(),
|
|
anomaly_type = character(),
|
|
priority_level = numeric(),
|
|
age_weeks = numeric(),
|
|
harvest_readiness = character(),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
})
|
|
}
|
|
# Calculate farm health summary metrics
|
|
tryCatch({
|
|
# Generate farm health summary data
|
|
farm_health_data <- generate_farm_health_summary(
|
|
field_boundaries = AllPivots0,
|
|
ci_current = CI,
|
|
ci_previous = CI_m1,
|
|
harvesting_data = harvesting_data
|
|
)
|
|
# Log the summary data
|
|
safe_log(paste("Generated farm health summary with", nrow(farm_health_data), "fields"))
|
|
}, error = function(e) {
|
|
safe_log(paste("Error in farm health calculation:", e$message), "ERROR")
|
|
# Create empty dataframe if calculation failed
|
|
farm_health_data <- data.frame(
|
|
field = character(),
|
|
mean_ci = numeric(),
|
|
ci_change = numeric(),
|
|
ci_uniformity = numeric(),
|
|
status = character(),
|
|
anomaly_type = character(),
|
|
priority_level = numeric(),
|
|
age_weeks = numeric(),
|
|
harvest_readiness = character(),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
})
|
|
# Render the velocity and acceleration indicators
|
|
tryCatch({
|
|
# Create and display the indicators using the imported utility function
|
|
velocity_plot <- create_velocity_acceleration_indicator(
|
|
health_data = farm_health_data,
|
|
ci_current = CI,
|
|
ci_prev1 = CI_m1,
|
|
ci_prev2 = CI_m2,
|
|
ci_prev3 = CI_m3,
|
|
field_boundaries = AllPivots0
|
|
)
|
|
# Print the visualization
|
|
print(velocity_plot)
|
|
# Create a table of fields with significant velocity changes
|
|
field_ci_metrics <- list()
|
|
# Process each field to get metrics
|
|
fields <- unique(AllPivots0$field)
|
|
for (field_name in fields) {
|
|
tryCatch({
|
|
# Get field boundary
|
|
field_shape <- AllPivots0 %>% dplyr::filter(field == field_name)
|
|
if (nrow(field_shape) == 0) next
|
|
# Extract CI values
|
|
ci_curr_values <- terra::extract(CI, field_shape)
|
|
ci_prev1_values <- terra::extract(CI_m1, field_shape)
|
|
# Calculate metrics
|
|
mean_ci_curr <- mean(ci_curr_values$CI, na.rm = TRUE)
|
|
mean_ci_prev1 <- mean(ci_prev1_values$CI, na.rm = TRUE)
|
|
velocity <- mean_ci_curr - mean_ci_prev1
|
|
# Store in list
|
|
field_ci_metrics[[field_name]] <- list(
|
|
field = field_name,
|
|
ci_current = mean_ci_curr,
|
|
ci_prev1 = mean_ci_prev1,
|
|
velocity = velocity
|
|
)
|
|
}, error = function(e) {
|
|
safe_log(paste("Error processing field", field_name, "for velocity table:", e$message), "WARNING")
|
|
})
|
|
}
|
|
# Convert list to data frame
|
|
velocity_df <- do.call(rbind, lapply(field_ci_metrics, function(x) {
|
|
data.frame(
|
|
field = x$field,
|
|
ci_current = round(x$ci_current, 2),
|
|
ci_prev1 = round(x$ci_prev1, 2),
|
|
velocity = round(x$velocity, 2),
|
|
direction = ifelse(x$velocity >= 0, "Improving", "Declining")
|
|
)
|
|
}))
|
|
# Select top 5 positive and top 5 negative velocity fields
|
|
top_positive <- velocity_df %>%
|
|
dplyr::filter(velocity > 0) %>%
|
|
dplyr::arrange(desc(velocity)) %>%
|
|
dplyr::slice_head(n = 5)
|
|
top_negative <- velocity_df %>%
|
|
dplyr::filter(velocity < 0) %>%
|
|
dplyr::arrange(velocity) %>%
|
|
dplyr::slice_head(n = 5)
|
|
# Display the tables if we have data
|
|
if (nrow(top_positive) > 0) {
|
|
cat("<h4>Fields with Fastest Improvement</h4>")
|
|
knitr::kable(top_positive %>%
|
|
dplyr::select(Field = field,
|
|
`Current CI` = ci_current,
|
|
`Previous CI` = ci_prev1,
|
|
`Weekly Change` = velocity))
|
|
}
|
|
if (nrow(top_negative) > 0) {
|
|
cat("<h4>Fields with Fastest Decline</h4>")
|
|
knitr::kable(top_negative %>%
|
|
dplyr::select(Field = field,
|
|
`Current CI` = ci_current,
|
|
`Previous CI` = ci_prev1,
|
|
`Weekly Change` = velocity))
|
|
}
|
|
}, error = function(e) {
|
|
safe_log(paste("Error rendering velocity visualization:", e$message), "ERROR")
|
|
cat("<div class='alert alert-danger'>Error generating velocity visualization.</div>")
|
|
})
|
|
# Generate anomaly timeline visualization
|
|
tryCatch({
|
|
# Use the imported function to create the anomaly timeline
|
|
anomaly_timeline <- create_anomaly_timeline(
|
|
field_boundaries = AllPivots0,
|
|
ci_data = CI_quadrant,
|
|
days_to_include = 90 # Show last 90 days of data
|
|
)
|
|
# Display the timeline
|
|
print(anomaly_timeline)
|
|
}, error = function(e) {
|
|
safe_log(paste("Error generating anomaly timeline:", e$message), "ERROR")
|
|
cat("<div class='alert alert-danger'>Error generating anomaly timeline visualization.</div>")
|
|
})
|
|
# Verify farm_health_data exists and has content
|
|
if (!exists("farm_health_data") || nrow(farm_health_data) == 0) {
|
|
safe_log("farm_health_data not found or empty, generating default data", "WARNING")
|
|
# Create minimal fallback data
|
|
tryCatch({
|
|
# Get fields from boundaries
|
|
fields <- unique(AllPivots0$field)
|
|
# Create basic data frame with just field names
|
|
farm_health_data <- data.frame(
|
|
field = fields,
|
|
mean_ci = rep(NA, length(fields)),
|
|
ci_change = rep(NA, length(fields)),
|
|
ci_uniformity = rep(NA, length(fields)),
|
|
status = rep("Unknown", length(fields)),
|
|
anomaly_type = rep("Unknown", length(fields)),
|
|
priority_level = rep(5, length(fields)), # Low priority
|
|
age_weeks = rep(NA, length(fields)),
|
|
harvest_readiness = rep("Unknown", length(fields)),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
safe_log("Created fallback farm_health_data with basic field information")
|
|
}, error = function(e) {
|
|
safe_log(paste("Error creating fallback farm_health_data:", e$message), "ERROR")
|
|
farm_health_data <<- data.frame(
|
|
field = character(),
|
|
mean_ci = numeric(),
|
|
ci_change = numeric(),
|
|
ci_uniformity = numeric(),
|
|
status = character(),
|
|
anomaly_type = character(),
|
|
priority_level = numeric(),
|
|
age_weeks = numeric(),
|
|
harvest_readiness = character(),
|
|
stringsAsFactors = FALSE
|
|
)
|
|
})
|
|
}
|
|
# ADVANCED ANALYTICS FUNCTIONS
|
|
# Note: These functions are now imported from executive_report_utils.R
|
|
# The utility file contains functions for velocity/acceleration indicators,
|
|
# anomaly timeline creation, age cohort mapping, and cohort performance charts
|
|
safe_log("Using analytics functions from executive_report_utils.R")
|
|
# Chunk 1: setup_parameters
|
|
# Set up basic report parameters from input values
|
|
report_date <- params$report_date
|
|
mail_day <- params$mail_day
|
|
borders <- params$borders
|
|
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
|
|
# Environment setup notes (commented out)
|
|
# # Activeer de renv omgeving
|
|
# renv::activate()
|
|
# renv::deactivate()
|
|
# # Optioneel: Herstel de omgeving als dat nodig is
|
|
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
|
|
# renv::restore()
|
|
# Chunk 2: load_libraries
|
|
# Configure knitr options
|
|
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
|
|
# Path management
|
|
library(here)
|
|
# Spatial data libraries
|
|
library(sf)
|
|
library(terra)
|
|
library(exactextractr)
|
|
# library(raster) - Removed as it's no longer maintained
|
|
# Data manipulation and visualization
|
|
library(tidyverse) # Includes dplyr, ggplot2, etc.
|
|
library(tmap)
|
|
library(lubridate)
|
|
library(zoo)
|
|
# Machine learning
|
|
library(rsample)
|
|
library(caret)
|
|
library(randomForest)
|
|
library(CAST)
|
|
# Load custom utility functions
|
|
tryCatch({
|
|
source("report_utils.R")
|
|
}, error = function(e) {
|
|
message(paste("Error loading report_utils.R:", e$message))
|
|
# Try alternative path if the first one fails
|
|
tryCatch({
|
|
source(here::here("r_app", "report_utils.R"))
|
|
}, error = function(e) {
|
|
stop("Could not load report_utils.R from either location: ", e$message)
|
|
})
|
|
})
|
|
# Load executive report utilities
|
|
tryCatch({
|
|
source("executive_report_utils.R")
|
|
}, error = function(e) {
|
|
message(paste("Error loading executive_report_utils.R:", e$message))
|
|
# Try alternative path if the first one fails
|
|
tryCatch({
|
|
source(here::here("r_app", "executive_report_utils.R"))
|
|
}, error = function(e) {
|
|
stop("Could not load executive_report_utils.R from either location: ", e$message)
|
|
})
|
|
})
|
|
# Chunk 1: setup_parameters
|
|
# Set up basic report parameters from input values
|
|
report_date <- params$report_date
|
|
mail_day <- params$mail_day
|
|
borders <- params$borders
|
|
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
|
|
# Environment setup notes (commented out)
|
|
# # Activeer de renv omgeving
|
|
# renv::activate()
|
|
# renv::deactivate()
|
|
# # Optioneel: Herstel de omgeving als dat nodig is
|
|
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
|
|
# renv::restore()
|
|
# Chunk 2: load_libraries
|
|
# Configure knitr options
|
|
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
|
|
# Path management
|
|
library(here)
|
|
# Spatial data libraries
|
|
library(sf)
|
|
library(terra)
|
|
library(exactextractr)
|
|
# library(raster) - Removed as it's no longer maintained
|
|
# Data manipulation and visualization
|
|
library(tidyverse) # Includes dplyr, ggplot2, etc.
|
|
library(tmap)
|
|
library(lubridate)
|
|
library(zoo)
|
|
# Machine learning
|
|
library(rsample)
|
|
library(caret)
|
|
library(randomForest)
|
|
library(CAST)
|
|
# Load custom utility functions
|
|
tryCatch({
|
|
source("report_utils.R")
|
|
}, error = function(e) {
|
|
message(paste("Error loading report_utils.R:", e$message))
|
|
# Try alternative path if the first one fails
|
|
tryCatch({
|
|
source(here::here("r_app", "report_utils.R"))
|
|
}, error = function(e) {
|
|
stop("Could not load report_utils.R from either location: ", e$message)
|
|
})
|
|
})
|
|
# Load executive report utilities
|
|
tryCatch({
|
|
source("executive_report_utils.R")
|
|
}, error = function(e) {
|
|
message(paste("Error loading executive_report_utils.R:", e$message))
|
|
# Try alternative path if the first one fails
|
|
tryCatch({
|
|
source(here::here("r_app", "executive_report_utils.R"))
|
|
}, error = function(e) {
|
|
stop("Could not load executive_report_utils.R from either location: ", e$message)
|
|
})
|
|
})
|
|
# Chunk 1: setup_parameters
|
|
# Set up basic report parameters from input values
|
|
report_date <- params$report_date
|
|
mail_day <- params$mail_day
|
|
borders <- params$borders
|
|
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
|
|
# Environment setup notes (commented out)
|
|
# # Activeer de renv omgeving
|
|
# renv::activate()
|
|
# renv::deactivate()
|
|
# # Optioneel: Herstel de omgeving als dat nodig is
|
|
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
|
|
# renv::restore()
|
|
# Chunk 2: load_libraries
|
|
# Configure knitr options
|
|
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
|
|
# Path management
|
|
library(here)
|
|
# Spatial data libraries
|
|
library(sf)
|
|
library(terra)
|
|
library(exactextractr)
|
|
# library(raster) - Removed as it's no longer maintained
|
|
# Data manipulation and visualization
|
|
library(tidyverse) # Includes dplyr, ggplot2, etc.
|
|
library(tmap)
|
|
library(lubridate)
|
|
library(zoo)
|
|
# Machine learning
|
|
library(rsample)
|
|
library(caret)
|
|
library(randomForest)
|
|
library(CAST)
|
|
# Load custom utility functions
|
|
# tryCatch({
|
|
# source("report_utils.R")
|
|
# }, error = function(e) {
|
|
# message(paste("Error loading report_utils.R:", e$message))
|
|
# # Try alternative path if the first one fails
|
|
# tryCatch({
|
|
source(here::here("r_app", "report_utils.R"))
|
|
# }, error = function(e) {
|
|
# stop("Could not load report_utils.R from either location: ", e$message)
|
|
# })
|
|
# })
|
|
# Load executive report utilities
|
|
# tryCatch({
|
|
# source("executive_report_utils.R")
|
|
# }, error = function(e) {
|
|
# message(paste("Error loading executive_report_utils.R:", e$message))
|
|
# # Try alternative path if the first one fails
|
|
# tryCatch({
|
|
source(here::here("r_app", "executive_report_utils.R"))
|
|
# Chunk 1: setup_parameters
|
|
# Set up basic report parameters from input values
|
|
report_date <- params$report_date
|
|
mail_day <- params$mail_day
|
|
borders <- params$borders
|
|
use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum in visualizations
|
|
# Environment setup notes (commented out)
|
|
# # Activeer de renv omgeving
|
|
# renv::activate()
|
|
# renv::deactivate()
|
|
# # Optioneel: Herstel de omgeving als dat nodig is
|
|
# # Je kunt dit commentaar geven als je het normaal niet wilt uitvoeren
|
|
# renv::restore()
|
|
# Chunk 2: load_libraries
|
|
# Configure knitr options
|
|
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
|
|
# Path management
|
|
library(here)
|
|
# Spatial data libraries
|
|
library(sf)
|
|
library(terra)
|
|
library(exactextractr)
|
|
# library(raster) - Removed as it's no longer maintained
|
|
# Data manipulation and visualization
|
|
library(tidyverse) # Includes dplyr, ggplot2, etc.
|
|
library(tmap)
|
|
library(lubridate)
|
|
library(zoo)
|
|
# Machine learning
|
|
library(rsample)
|
|
library(caret)
|
|
library(randomForest)
|
|
library(CAST)
|
|
# Load custom utility functions
|
|
# tryCatch({
|
|
# source("report_utils.R")
|
|
# }, error = function(e) {
|
|
# message(paste("Error loading report_utils.R:", e$message))
|
|
# # Try alternative path if the first one fails
|
|
# tryCatch({
|
|
source(here::here("r_app", "report_utils.R"))
|
|
# }, error = function(e) {
|
|
# stop("Could not load report_utils.R from either location: ", e$message)
|
|
# })
|
|
# })
|
|
# Load executive report utilities
|
|
# tryCatch({
|
|
# source("executive_report_utils.R")
|
|
# }, error = function(e) {
|
|
# message(paste("Error loading executive_report_utils.R:", e$message))
|
|
# # Try alternative path if the first one fails
|
|
# tryCatch({
|
|
source(here::here("r_app", "exec_dashboard", "executive_report_utils.R"))
|
|
# }, error = function(e) {
|
|
# stop("Could not load executive_report_utils.R from either location: ", e$message)
|
|
# })
|
|
# })
|
|
# Chunk 3: initialize_project_config
|
|
# Set the project directory from parameters
|
|
project_dir <- params$data_dir
|
|
# Source project parameters with error handling
|
|
# tryCatch({
|
|
source(here::here("r_app", "parameters_project.R"))
|
|
# }, error = function(e) {
|
|
# stop("Error loading parameters_project.R: ", e$message)
|
|
# })
|
|
# Log initial configuration
|
|
safe_log("Starting the R Markdown script")
|
|
safe_log(paste("mail_day params:", params$mail_day))
|
|
safe_log(paste("report_date params:", params$report_date))
|
|
safe_log(paste("mail_day variable:", mail_day))
|
|
# Set locale for consistent date formatting
|
|
Sys.setlocale("LC_TIME", "C")
|
|
# Initialize date variables from parameters
|
|
today <- as.character(report_date)
|
|
mail_day_as_character <- as.character(mail_day)
|
|
# Calculate week days
|
|
report_date_as_week_day <- weekdays(lubridate::ymd(today))
|
|
days_of_week <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
|
|
# Calculate initial week number
|
|
week <- lubridate::week(today)
|
|
safe_log(paste("Initial week calculation:", week, "today:", today))
|
|
# Calculate previous dates for comparisons
|
|
today_minus_1 <- as.character(lubridate::ymd(today) - 7)
|
|
today_minus_2 <- as.character(lubridate::ymd(today) - 14)
|
|
today_minus_3 <- as.character(lubridate::ymd(today) - 21)
|
|
# Log the weekday calculations for debugging
|
|
safe_log(paste("Report date weekday:", report_date_as_week_day))
|
|
safe_log(paste("Weekday index:", which(days_of_week == report_date_as_week_day)))
|
|
safe_log(paste("Mail day:", mail_day_as_character))
|
|
safe_log(paste("Mail day index:", which(days_of_week == mail_day_as_character)))
|
|
# Adjust week calculation based on mail day
|
|
if (which(days_of_week == report_date_as_week_day) > which(days_of_week == mail_day_as_character)) {
|
|
safe_log("Adjusting weeks because of mail day")
|
|
week <- lubridate::week(today) + 1
|
|
today_minus_1 <- as.character(lubridate::ymd(today))
|
|
today_minus_2 <- as.character(lubridate::ymd(today) - 7)
|
|
today_minus_3 <- as.character(lubridate::ymd(today) - 14)
|
|
}
|
|
# Generate subtitle for report
|
|
subtitle_var <- paste("Report generated on", Sys.Date())
|
|
# Calculate week numbers for previous weeks
|
|
week_minus_1 <- week - 1
|
|
week_minus_2 <- week - 2
|
|
week_minus_3 <- week - 3
|
|
# Format current week with leading zeros
|
|
week <- sprintf("%02d", week)
|
|
# Get years for each date
|
|
year <- lubridate::year(today)
|
|
year_1 <- lubridate::year(today_minus_1)
|
|
year_2 <- lubridate::year(today_minus_2)
|
|
year_3 <- lubridate::year(today_minus_3)
|
|
# Load CI index data with error handling
|
|
tryCatch({
|
|
CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds"))
|
|
safe_log("Successfully loaded CI quadrant data")
|
|
}, error = function(e) {
|
|
stop("Error loading CI quadrant data: ", e$message)
|
|
})
|
|
# Get file paths for different weeks using the utility function
|
|
tryCatch({
|
|
path_to_week_current = get_week_path(weekly_CI_mosaic, today, 0)
|
|
path_to_week_minus_1 = get_week_path(weekly_CI_mosaic, today, -1)
|
|
path_to_week_minus_2 = get_week_path(weekly_CI_mosaic, today, -2)
|
|
path_to_week_minus_3 = get_week_path(weekly_CI_mosaic, today, -3)
|
|
# Log the calculated paths
|
|
safe_log("Required mosaic paths:")
|
|
safe_log(paste("Path to current week:", path_to_week_current))
|
|
safe_log(paste("Path to week minus 1:", path_to_week_minus_1))
|
|
safe_log(paste("Path to week minus 2:", path_to_week_minus_2))
|
|
safe_log(paste("Path to week minus 3:", path_to_week_minus_3))
|
|
# Validate that files exist
|
|
if (!file.exists(path_to_week_current)) warning("Current week mosaic file does not exist: ", path_to_week_current)
|
|
if (!file.exists(path_to_week_minus_1)) warning("Week minus 1 mosaic file does not exist: ", path_to_week_minus_1)
|
|
if (!file.exists(path_to_week_minus_2)) warning("Week minus 2 mosaic file does not exist: ", path_to_week_minus_2)
|
|
if (!file.exists(path_to_week_minus_3)) warning("Week minus 3 mosaic file does not exist: ", path_to_week_minus_3)
|
|
# Load raster data with terra functions
|
|
CI <- terra::rast(path_to_week_current)$CI
|
|
CI_m1 <- terra::rast(path_to_week_minus_1)$CI
|
|
CI_m2 <- terra::rast(path_to_week_minus_2)$CI
|
|
CI_m3 <- terra::rast(path_to_week_minus_3)$CI
|
|
}, error = function(e) {
|
|
stop("Error loading raster data: ", e$message)
|
|
})
|