diff --git a/.Rhistory b/.Rhistory
index 19c58ab..696d5a7 100644
--- a/.Rhistory
+++ b/.Rhistory
@@ -1,210 +1,416 @@
-})
-# 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
-)
-})
+ggplot2::labs(title = "Model Performance: \nPredicted vs Actual Tonnage/ha",
+x = "Actual tonnage/ha (Tcha)",
+y = "Predicted tonnage/ha (Tcha)") +
+ggplot2::theme_minimal()
}
-# 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("
Fields with Fastest Improvement
")
-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("Fields with Fastest Decline
")
-knitr::kable(top_negative %>%
-dplyr::select(Field = field,
-`Current CI` = ci_current,
-`Previous CI` = ci_prev1,
-`Weekly Change` = velocity))
+if (nrow(pred_rf_current_season) > 0) {
+# Plot predicted yields by age
+ggplot2::ggplot(pred_rf_current_season, ggplot2::aes(x = Age_days, y = predicted_Tcha)) +
+ggplot2::geom_point(size = 2, alpha = 0.6) +
+ggplot2::labs(title = "Predicted Yields for Fields Over 300 Days \nOld Yet to Be Harvested",
+x = "Age (days)",
+y = "Predicted tonnage/ha (Tcha)") +
+ggplot2::scale_y_continuous(limits = c(0, 200)) +
+ggplot2::theme_minimal()
+# Display prediction table
+knitr::kable(pred_rf_current_season,
+digits = 0,
+caption = "Predicted Tonnage/ha for Fields Over 300 Days Old")
+} else {
+cat("No fields over 300 days old without harvest data available for yield prediction.")
}
}, error = function(e) {
-safe_log(paste("Error rendering velocity visualization:", e$message), "ERROR")
-cat("Error generating velocity visualization.
")
+safe_log(paste("Error in yield prediction visualization:", e$message), "ERROR")
+cat("Error generating yield prediction visualizations. See log for details.")
})
-# Generate anomaly timeline visualization
+# Load and prepare yield prediction data with error handling
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("Error generating anomaly timeline visualization.
")
-})
-# 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
-)
-})
+# Load CI quadrant data and fill missing values
+CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds")) %>%
+dplyr::group_by(model) %>%
+tidyr::fill(field, sub_field, .direction = "downup") %>%
+dplyr::ungroup()
+# Check if tonnage_ha is empty
+if (all(is.na(harvesting_data$tonnage_ha))) {
+safe_log("Lacking historic harvest data, please provide for yield prediction calculation", "WARNING")
+knitr::knit_exit() # Exit the chunk if tonnage_ha is empty
}
-# 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")
+# Rename year column to season for consistency
+harvesting_data <- harvesting_data %>% dplyr::rename(season = year)
+# Join CI and yield data
+CI_and_yield <- dplyr::left_join(CI_quadrant, harvesting_data, by = c("field", "sub_field", "season")) %>%
+dplyr::group_by(sub_field, season) %>%
+dplyr::slice(which.max(DOY)) %>%
+dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
+dplyr::mutate(CI_per_day = cumulative_CI / DOY)
+# Define predictors and response variables
+predictors <- c("cumulative_CI", "DOY", "CI_per_day")
+response <- "tonnage_ha"
+# Prepare test and validation datasets
+CI_and_yield_test <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(!is.na(tonnage_ha))
+CI_and_yield_validation <- CI_and_yield_test
+# Prepare prediction dataset (fields without harvest data)
+prediction_yields <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(is.na(tonnage_ha))
+# Configure model training parameters
+ctrl <- caret::trainControl(
+method = "cv",
+savePredictions = TRUE,
+allowParallel = TRUE,
+number = 5,
+verboseIter = TRUE
+)
+# Train the model with feature selection
+set.seed(202) # For reproducibility
+model_ffs_rf <- CAST::ffs(
+CI_and_yield_test[, predictors],
+CI_and_yield_test[, response],
+method = "rf",
+trControl = ctrl,
+importance = TRUE,
+withinSE = TRUE,
+tuneLength = 5,
+na.rm = TRUE
+)
+# Function to prepare predictions with consistent naming and formatting
+prepare_predictions <- function(predictions, newdata) {
+return(predictions %>%
+as.data.frame() %>%
+dplyr::rename(predicted_Tcha = ".") %>%
+dplyr::mutate(
+sub_field = newdata$sub_field,
+field = newdata$field,
+Age_days = newdata$DOY,
+total_CI = round(newdata$cumulative_CI, 0),
+predicted_Tcha = round(predicted_Tcha, 0),
+season = newdata$season
+) %>%
+dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
+dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
+)
+}
+# Predict yields for the validation dataset
+pred_ffs_rf <- prepare_predictions(stats::predict(model_ffs_rf, newdata = CI_and_yield_validation), CI_and_yield_validation)
+# Predict yields for the current season (focus on mature fields over 300 days)
+pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
+dplyr::filter(Age_days > 1) %>%
+dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
+safe_log("Successfully completed yield prediction calculations")
+}, error = function(e) {
+safe_log(paste("Error in yield prediction:", e$message), "ERROR")
+# Create empty dataframes to prevent errors in subsequent chunks
+pred_ffs_rf <- data.frame()
+pred_rf_current_season <- data.frame()
+})
+# Load and prepare yield prediction data with error handling
+tryCatch({
+# Load CI quadrant data and fill missing values
+CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds")) %>%
+dplyr::group_by(model) %>%
+tidyr::fill(field, sub_field, .direction = "downup") %>%
+dplyr::ungroup()
+# Check if tonnage_ha is empty
+if (all(is.na(harvesting_data$tonnage_ha))) {
+safe_log("Lacking historic harvest data, please provide for yield prediction calculation", "WARNING")
+knitr::knit_exit() # Exit the chunk if tonnage_ha is empty
+}
+# Rename year column to season for consistency
+harvesting_data <- harvesting_data %>% dplyr::rename(season = year)
+# Join CI and yield data
+CI_and_yield <- dplyr::left_join(CI_quadrant, harvesting_data, by = c("field", "sub_field", "season")) %>%
+dplyr::group_by(sub_field, season) %>%
+dplyr::slice(which.max(DOY)) %>%
+dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
+dplyr::mutate(CI_per_day = cumulative_CI / DOY)
+# Define predictors and response variables
+predictors <- c("cumulative_CI", "DOY", "CI_per_day")
+response <- "tonnage_ha"
+# Prepare test and validation datasets
+CI_and_yield_test <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(!is.na(tonnage_ha))
+CI_and_yield_validation <- CI_and_yield_test
+# Prepare prediction dataset (fields without harvest data)
+prediction_yields <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(is.na(tonnage_ha))
+# Configure model training parameters
+ctrl <- caret::trainControl(
+method = "cv",
+savePredictions = TRUE,
+allowParallel = TRUE,
+number = 5,
+verboseIter = TRUE
+)
+# Train the model with feature selection
+set.seed(202) # For reproducibility
+model_ffs_rf <- CAST::ffs(
+CI_and_yield_test[, predictors],
+CI_and_yield_test[, response],
+method = "rf",
+trControl = ctrl,
+importance = TRUE,
+withinSE = TRUE,
+tuneLength = 5,
+na.rm = TRUE
+)
+# Function to prepare predictions with consistent naming and formatting
+prepare_predictions <- function(predictions, newdata) {
+return(predictions %>%
+as.data.frame() %>%
+dplyr::rename(predicted_Tcha = ".") %>%
+dplyr::mutate(
+sub_field = newdata$sub_field,
+field = newdata$field,
+Age_days = newdata$DOY,
+total_CI = round(newdata$cumulative_CI, 0),
+predicted_Tcha = round(predicted_Tcha, 0),
+season = newdata$season
+) %>%
+dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
+dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
+)
+}
+# Predict yields for the validation dataset
+pred_ffs_rf <- prepare_predictions(stats::predict(model_ffs_rf, newdata = CI_and_yield_validation), CI_and_yield_validation)
+# Predict yields for the current season (focus on mature fields over 300 days)
+pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
+dplyr::filter(Age_days > 1) %>%
+dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
+safe_log("Successfully completed yield prediction calculations")
+}, error = function(e) {
+safe_log(paste("Error in yield prediction:", e$message), "ERROR")
+# Create empty dataframes to prevent errors in subsequent chunks
+pred_ffs_rf <- data.frame()
+pred_rf_current_season <- data.frame()
+})
+# Load CI quadrant data and fill missing values
+CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds")) %>%
+dplyr::group_by(model) %>%
+tidyr::fill(field, sub_field, .direction = "downup") %>%
+dplyr::ungroup()
+# Check if tonnage_ha is empty
+if (all(is.na(harvesting_data$tonnage_ha))) {
+safe_log("Lacking historic harvest data, please provide for yield prediction calculation", "WARNING")
+knitr::knit_exit() # Exit the chunk if tonnage_ha is empty
+}
+# Rename year column to season for consistency
+harvesting_data <- harvesting_data %>% dplyr::rename(season = year)
+# Join CI and yield data
+CI_and_yield <- dplyr::left_join(CI_quadrant, harvesting_data, by = c("field", "sub_field", "season")) %>%
+dplyr::group_by(sub_field, season) %>%
+dplyr::slice(which.max(DOY)) %>%
+dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
+dplyr::mutate(CI_per_day = cumulative_CI / DOY)
+# Define predictors and response variables
+predictors <- c("cumulative_CI", "DOY", "CI_per_day")
+response <- "tonnage_ha"
+# Prepare test and validation datasets
+CI_and_yield_test <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(!is.na(tonnage_ha))
+CI_and_yield_validation <- CI_and_yield_test
+# Prepare prediction dataset (fields without harvest data)
+prediction_yields <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(is.na(tonnage_ha))
+# Configure model training parameters
+ctrl <- caret::trainControl(
+method = "cv",
+savePredictions = TRUE,
+allowParallel = TRUE,
+number = 5,
+verboseIter = TRUE
+)
+# Train the model with feature selection
+set.seed(202) # For reproducibility
+model_ffs_rf <- CAST::ffs(
+CI_and_yield_test[, predictors],
+CI_and_yield_test[, response],
+method = "rf",
+trControl = ctrl,
+importance = TRUE,
+withinSE = TRUE,
+tuneLength = 5,
+na.rm = TRUE
+)
+# Function to prepare predictions with consistent naming and formatting
+prepare_predictions <- function(predictions, newdata) {
+return(predictions %>%
+as.data.frame() %>%
+dplyr::rename(predicted_Tcha = ".") %>%
+dplyr::mutate(
+sub_field = newdata$sub_field,
+field = newdata$field,
+Age_days = newdata$DOY,
+total_CI = round(newdata$cumulative_CI, 0),
+predicted_Tcha = round(predicted_Tcha, 0),
+season = newdata$season
+) %>%
+dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
+dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
+)
+}
+# Predict yields for the validation dataset
+pred_ffs_rf <- prepare_predictions(stats::predict(model_ffs_rf, newdata = CI_and_yield_validation), CI_and_yield_validation)
+# Predict yields for the current season (focus on mature fields over 300 days)
+pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
+dplyr::filter(Age_days > 300) %>%
+dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
+# Predict yields for the current season (focus on mature fields over 300 days)
+pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
+dplyr::filter(Age_days > 1) %>%
+dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
+safe_log("Successfully completed yield prediction calculations")
+# Load and prepare yield prediction data with error handling
+tryCatch({
+# Load CI quadrant data and fill missing values
+CI_quadrant <- readRDS(here::here(cumulative_CI_vals_dir, "All_pivots_Cumulative_CI_quadrant_year_v2.rds")) %>%
+dplyr::group_by(model) %>%
+tidyr::fill(field, sub_field, .direction = "downup") %>%
+dplyr::ungroup()
+# Check if tonnage_ha is empty
+if (all(is.na(harvesting_data$tonnage_ha))) {
+safe_log("Lacking historic harvest data, please provide for yield prediction calculation", "WARNING")
+knitr::knit_exit() # Exit the chunk if tonnage_ha is empty
+}
+# Rename year column to season for consistency
+harvesting_data <- harvesting_data %>% dplyr::rename(season = year)
+# Join CI and yield data
+CI_and_yield <- dplyr::left_join(CI_quadrant, harvesting_data, by = c("field", "sub_field", "season")) %>%
+dplyr::group_by(sub_field, season) %>%
+dplyr::slice(which.max(DOY)) %>%
+dplyr::select(field, sub_field, tonnage_ha, cumulative_CI, DOY, season, sub_area) %>%
+dplyr::mutate(CI_per_day = cumulative_CI / DOY)
+# Define predictors and response variables
+predictors <- c("cumulative_CI", "DOY", "CI_per_day")
+response <- "tonnage_ha"
+# Prepare test and validation datasets
+CI_and_yield_test <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(!is.na(tonnage_ha))
+CI_and_yield_validation <- CI_and_yield_test
+# Prepare prediction dataset (fields without harvest data)
+prediction_yields <- CI_and_yield %>%
+as.data.frame() %>%
+dplyr::filter(is.na(tonnage_ha))
+# Configure model training parameters
+ctrl <- caret::trainControl(
+method = "cv",
+savePredictions = TRUE,
+allowParallel = TRUE,
+number = 5,
+verboseIter = TRUE
+)
+# Train the model with feature selection
+set.seed(202) # For reproducibility
+model_ffs_rf <- CAST::ffs(
+CI_and_yield_test[, predictors],
+CI_and_yield_test[, response],
+method = "rf",
+trControl = ctrl,
+importance = TRUE,
+withinSE = TRUE,
+tuneLength = 5,
+na.rm = TRUE
+)
+# Function to prepare predictions with consistent naming and formatting
+prepare_predictions <- function(predictions, newdata) {
+return(predictions %>%
+as.data.frame() %>%
+dplyr::rename(predicted_Tcha = ".") %>%
+dplyr::mutate(
+sub_field = newdata$sub_field,
+field = newdata$field,
+Age_days = newdata$DOY,
+total_CI = round(newdata$cumulative_CI, 0),
+predicted_Tcha = round(predicted_Tcha, 0),
+season = newdata$season
+) %>%
+dplyr::select(field, sub_field, Age_days, total_CI, predicted_Tcha, season) %>%
+dplyr::left_join(., newdata, by = c("field", "sub_field", "season"))
+)
+}
+# Predict yields for the validation dataset
+pred_ffs_rf <- prepare_predictions(stats::predict(model_ffs_rf, newdata = CI_and_yield_validation), CI_and_yield_validation)
+# Predict yields for the current season (focus on mature fields over 300 days)
+pred_rf_current_season <- prepare_predictions(stats::predict(model_ffs_rf, newdata = prediction_yields), prediction_yields) %>%
+dplyr::filter(Age_days > 1) %>%
+dplyr::mutate(CI_per_day = round(total_CI / Age_days, 1))
+safe_log("Successfully completed yield prediction calculations")
+}, error = function(e) {
+safe_log(paste("Error in yield prediction:", e$message), "ERROR")
+# Create empty dataframes to prevent errors in subsequent chunks
+pred_ffs_rf <- data.frame()
+pred_rf_current_season <- data.frame()
+})
+# Display yield prediction visualizations with error handling
+tryCatch({
+if (nrow(pred_ffs_rf) > 0) {
+# Plot model performance (predicted vs actual)
+ggplot2::ggplot(pred_ffs_rf, ggplot2::aes(y = predicted_Tcha, x = tonnage_ha)) +
+ggplot2::geom_point(size = 2, alpha = 0.6) +
+ggplot2::geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "red") +
+ggplot2::scale_x_continuous(limits = c(0, 200)) +
+ggplot2::scale_y_continuous(limits = c(0, 200)) +
+ggplot2::labs(title = "Model Performance: \nPredicted vs Actual Tonnage/ha",
+x = "Actual tonnage/ha (Tcha)",
+y = "Predicted tonnage/ha (Tcha)") +
+ggplot2::theme_minimal()
+}
+if (nrow(pred_rf_current_season) > 0) {
+# Plot predicted yields by age
+ggplot2::ggplot(pred_rf_current_season, ggplot2::aes(x = Age_days, y = predicted_Tcha)) +
+ggplot2::geom_point(size = 2, alpha = 0.6) +
+ggplot2::labs(title = "Predicted Yields for Fields Over 300 Days \nOld Yet to Be Harvested",
+x = "Age (days)",
+y = "Predicted tonnage/ha (Tcha)") +
+ggplot2::scale_y_continuous(limits = c(0, 200)) +
+ggplot2::theme_minimal()
+# Display prediction table
+knitr::kable(pred_rf_current_season,
+digits = 0,
+caption = "Predicted Tonnage/ha for Fields Over 300 Days Old")
+} else {
+cat("No fields over 300 days old without harvest data available for yield prediction.")
+}
+}, error = function(e) {
+safe_log(paste("Error in yield prediction visualization:", e$message), "ERROR")
+cat("Error generating yield prediction visualizations. See log for details.")
+})
+AllPivots_merged$field[1:10]
+CI
+path_to_week_current
+terra::rast(path_to_week_current)
+map <- tmap::tm_shape(last_week_dif_raster_abs, unit = "m") # Add raster layer with continuous spectrum (centered at 0 for difference maps)
+map <- map + tmap::tm_raster(col.scale = tm_scale_continuous(values = "RdYlGn",
+midpoint = 0),
+col.legend = tm_legend(title = "Chlorophyll Index (CI) Change",
+orientation = "landscape",
+position = tm_pos_out("center", "bottom")))
+# Complete the map with layout and other elements
+map <- map +
+tmap::tm_scalebar(position = c("right", "bottom"), text.color = "black") +
+tmap::tm_compass(position = c("right", "bottom"), text.color = "black") +
+tmap::tm_shape(AllPivots0) +
+tmap::tm_borders(col = "black") +
+tmap::tm_text("sub_field", size = 0.6, col = "black")
+# Print the map
+print(map)
+# Get versions of specific packages you're using
+packages <- c("here", "sf", "terra", "exactextractr", "tidyverse",
+"tmap", "lubridate", "magrittr", "dplyr", "readr",
+"readxl", "knitr", "rmarkdown", "officedown", "officer")
+package_versions <- sapply(packages, function(x) as.character(packageVersion(x)))
+sessionInfo()
# 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()
@@ -215,23 +421,21 @@ use_breaks <- params$use_breaks # Whether to use breaks or continuous spectrum
# Chunk 2: load_libraries
# Configure knitr options
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
-# Path management
+# Load all packages at once with suppressPackageStartupMessages
+suppressPackageStartupMessages({
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(tidyverse)
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")
@@ -244,199 +448,21 @@ source(here::here("r_app", "report_utils.R"))
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({
+tryCatch({
source(here::here("r_app", "parameters_project.R"))
-# }, error = function(e) {
-# stop("Error loading parameters_project.R: ", e$message)
-# })
+}, 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))
+# Chunk 4: calculate_dates_and_weeks
# Set locale for consistent date formatting
Sys.setlocale("LC_TIME", "C")
# Initialize date variables from parameters
@@ -465,8 +491,6 @@ 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
@@ -478,35 +502,11 @@ 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)
-})
+sessionInfo()
+source("r_app/extract_current_versions.R")
+source("r_app/package_manager.R")
+source("r_app/package_manager.R")
+source("r_app/package_manager.R")
+source("r_app/package_manager.R")
+source("r_app/package_manager.R")
+source("r_app/package_manager.R")
diff --git a/PACKAGE_MANAGEMENT.md b/PACKAGE_MANAGEMENT.md
new file mode 100644
index 0000000..f0965bc
--- /dev/null
+++ b/PACKAGE_MANAGEMENT.md
@@ -0,0 +1,94 @@
+# SmartCane Project - Package Management
+
+## Quick Start
+
+### For New Team Members
+1. Open R/RStudio
+2. Set working directory to the project root: `setwd("path/to/smartcane")`
+3. Run: `source("r_app/package_manager.R")`
+4. Type `y` when prompted
+5. Wait for completion ✅
+
+### For Existing Team Members (After Git Pull)
+Same steps as above - the script will check for updates automatically.
+
+## What This Script Does
+
+1. **Initializes renv** - Creates isolated package environment
+2. **Checks package versions** - Compares installed vs required
+3. **Installs/Updates packages** - Only if needed
+4. **Creates lockfile** - `renv.lock` for exact reproducibility
+5. **Generates reports** - Console output + `package_manager.log`
+
+## Key Features
+
+- ✅ **Minimum version requirements** (allows patch updates)
+- ✅ **Critical package locking** (tmap v4 for new syntax)
+- ✅ **Automatic installation** of missing packages
+- ✅ **Console + Log output** for debugging
+- ✅ **Cross-platform compatibility**
+
+## Required Packages & Versions
+
+| Package | Min Version | Purpose |
+|---------|-------------|---------|
+| tmap | 4.0.0 | **CRITICAL** - New syntax used |
+| tidyverse | 2.0.0 | Data manipulation |
+| sf | 1.0.0 | Spatial data |
+| terra | 1.7.0 | Raster processing |
+| rmarkdown | 2.21.0 | Report generation |
+
+## Workflow
+
+### Development Workflow
+```
+1. 👨💻 Developer: Make changes → run package_manager.R → test → commit + push
+2. 👥 Teammate: Pull → run package_manager.R → test
+3. 🚀 Production: Pull → run package_manager.R → deploy
+```
+
+### Files Created
+- `renv.lock` - Exact package versions (commit this!)
+- `package_manager.log` - Installation log (don't commit)
+- `renv/` folder - Package cache (don't commit)
+
+## Troubleshooting
+
+### "Package failed to install"
+- Check internet connection
+- Update R to latest version
+- Install system dependencies (varies by OS)
+
+### "Version conflicts"
+- Delete `renv/` folder
+- Run script again for clean install
+
+### "renv not working"
+- Install manually: `install.packages("renv")`
+- Restart R session
+- Run script again
+
+## Team Guidelines
+
+1. **Always run** `package_manager.R` after pulling changes
+2. **Commit** `renv.lock` to git (not `renv/` folder)
+3. **Don't modify** package versions in scripts - use this manager
+4. **Report issues** in the log file to team
+
+## Advanced Usage
+
+### Restore from lockfile only:
+```r
+renv::restore()
+```
+
+### Add new package requirement:
+1. Edit `REQUIRED_PACKAGES` in `package_manager.R`
+2. Run the script
+3. Commit updated `renv.lock`
+
+### Check status without changes:
+```r
+source("r_app/package_manager.R")
+# Then just read the log or run generate_package_report()
+```
diff --git a/generated_package_config.R b/generated_package_config.R
new file mode 100644
index 0000000..a5c98ec
--- /dev/null
+++ b/generated_package_config.R
@@ -0,0 +1,42 @@
+# Package requirements with your current working versions
+REQUIRED_PACKAGES <- list(
+ # Core data manipulation
+ "dplyr" = "1.1.4",
+ "here" = "1.0.1",
+ "lubridate" = "1.9.4",
+ "readr" = "2.1.5",
+ "readxl" = "1.4.5",
+ "stringr" = "1.5.1",
+ "tidyr" = "1.3.1",
+
+ # Spatial data
+ "exactextractr" = "0.10.0",
+ "raster" = "3.6.32",
+ "sf" = "1.0.19",
+ "terra" = "1.8.43", # CRITICAL: for raster processing
+
+ # Visualization
+ "ggplot2" = "3.5.1",
+ "tmap" = "4.0", # CRITICAL: for tm_scale_continuous() syntax
+
+ # Reporting
+ "knitr" = "1.50",
+
+ # Tidyverse
+ "purrr" = "1.0.2",
+ "tidyverse" = "2.0.0",
+
+ # Other packages
+ "caret" = "7.0.1",
+ "CAST" = "1.0.3",
+ "furrr" = "0.3.1",
+ "future" = "1.40.0",
+ "gridExtra" = "2.3",
+ "parallel" = "4.4.2",
+ "progressr" = "0.15.1",
+ "randomForest" = "4.7.1.2",
+ "reshape2" = "1.4.4",
+ "rsample" = "1.3.0",
+ "tools" = "4.4.2",
+ "zoo" = "1.8.13"
+)
diff --git a/package_manager.log b/package_manager.log
new file mode 100644
index 0000000..64f5048
--- /dev/null
+++ b/package_manager.log
@@ -0,0 +1,111 @@
+[INFO] 2025-06-24 14:49:29 - SmartCane Project - Package Manager Started
+[INFO] 2025-06-24 14:49:29 - Working directory: C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane
+[INFO] 2025-06-24 14:49:29 - Checking renv initialization...
+[INFO] 2025-06-24 14:49:29 - ✓ renv already initialized
+[INFO] 2025-06-24 14:49:29 - ✓ renv already active
+[INFO] 2025-06-24 14:49:29 -
+=== INITIAL STATE ===
+[INFO] 2025-06-24 14:49:29 - === PACKAGE REPORT ===
+[INFO] 2025-06-24 14:49:29 - dplyr | Required: >= 1.1.4 | Installed: 1.1.4 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - here | Required: >= 1.0.1 | Installed: 1.0.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - lubridate | Required: >= 1.9.4 | Installed: 1.9.4 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - readr | Required: >= 2.1.5 | Installed: 2.1.5 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - readxl | Required: >= 1.4.5 | Installed: 1.4.5 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - stringr | Required: >= 1.5.1 | Installed: 1.5.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - tidyr | Required: >= 1.3.1 | Installed: 1.3.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - purrr | Required: >= 1.0.2 | Installed: 1.0.2 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - magrittr | Required: >= 2.0.0 | Installed: 2.0.3 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - exactextractr | Required: >= 0.10.0 | Installed: 0.10.0 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - raster | Required: >= 3.6.32 | Installed: 3.6.32 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - sf | Required: >= 1.0.19 | Installed: 1.0.19 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - terra | Required: >= 1.8.43 | Installed: 1.8.43 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - ggplot2 | Required: >= 3.5.1 | Installed: 3.5.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - tmap | Required: >= 4.0 | Installed: 4.0 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - gridExtra | Required: >= 2.3 | Installed: 2.3 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - knitr | Required: >= 1.50 | Installed: 1.50 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - rmarkdown | Required: >= 2.21.0 | Installed: 2.29 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - tidyverse | Required: >= 2.0.0 | Installed: 2.0.0 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - caret | Required: >= 7.0.1 | Installed: 7.0.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - CAST | Required: >= 1.0.3 | Installed: 1.0.3 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - randomForest | Required: >= 4.7.1.2 | Installed: 4.7.1.2 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - rsample | Required: >= 1.3.0 | Installed: 1.3.0 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - furrr | Required: >= 0.3.1 | Installed: 0.3.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - future | Required: >= 1.40.0 | Installed: 1.40.0 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - progressr | Required: >= 0.15.1 | Installed: 0.15.1 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - reshape2 | Required: >= 1.4.4 | Installed: 1.4.4 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - zoo | Required: >= 1.8.13 | Installed: 1.8.13 | ✅ OK
+[INFO] 2025-06-24 14:49:29 - === END PACKAGE REPORT ===
+[INFO] 2025-06-24 14:49:29 -
+=== PACKAGE INSTALLATION/UPDATES ===
+[INFO] 2025-06-24 14:49:29 - === PACKAGE MANAGEMENT STARTED ===
+[INFO] 2025-06-24 14:49:29 - R version: R version 4.4.2 (2024-10-31 ucrt)
+[INFO] 2025-06-24 14:49:29 - ✓ dplyr version 1.1.4 meets requirement (>= 1.1.4)
+[INFO] 2025-06-24 14:49:29 - ✓ here version 1.0.1 meets requirement (>= 1.0.1)
+[INFO] 2025-06-24 14:49:29 - ✓ lubridate version 1.9.4 meets requirement (>= 1.9.4)
+[INFO] 2025-06-24 14:49:29 - ✓ readr version 2.1.5 meets requirement (>= 2.1.5)
+[INFO] 2025-06-24 14:49:29 - ✓ readxl version 1.4.5 meets requirement (>= 1.4.5)
+[INFO] 2025-06-24 14:49:29 - ✓ stringr version 1.5.1 meets requirement (>= 1.5.1)
+[INFO] 2025-06-24 14:49:29 - ✓ tidyr version 1.3.1 meets requirement (>= 1.3.1)
+[INFO] 2025-06-24 14:49:29 - ✓ purrr version 1.0.2 meets requirement (>= 1.0.2)
+[INFO] 2025-06-24 14:49:29 - ✓ magrittr version 2.0.3 meets requirement (>= 2.0.0)
+[INFO] 2025-06-24 14:49:29 - ✓ exactextractr version 0.10.0 meets requirement (>= 0.10.0)
+[INFO] 2025-06-24 14:49:29 - ✓ raster version 3.6.32 meets requirement (>= 3.6.32)
+[INFO] 2025-06-24 14:49:29 - ✓ sf version 1.0.19 meets requirement (>= 1.0.19)
+[INFO] 2025-06-24 14:49:29 - ✓ terra version 1.8.43 meets requirement (>= 1.8.43)
+[INFO] 2025-06-24 14:49:29 - ✓ ggplot2 version 3.5.1 meets requirement (>= 3.5.1)
+[INFO] 2025-06-24 14:49:29 - ✓ tmap version 4.0 meets requirement (>= 4.0)
+[INFO] 2025-06-24 14:49:29 - ✓ gridExtra version 2.3 meets requirement (>= 2.3)
+[INFO] 2025-06-24 14:49:29 - ✓ knitr version 1.50 meets requirement (>= 1.50)
+[INFO] 2025-06-24 14:49:29 - ✓ rmarkdown version 2.29 meets requirement (>= 2.21.0)
+[INFO] 2025-06-24 14:49:29 - ✓ tidyverse version 2.0.0 meets requirement (>= 2.0.0)
+[INFO] 2025-06-24 14:49:29 - ✓ caret version 7.0.1 meets requirement (>= 7.0.1)
+[INFO] 2025-06-24 14:49:29 - ✓ CAST version 1.0.3 meets requirement (>= 1.0.3)
+[INFO] 2025-06-24 14:49:29 - ✓ randomForest version 4.7.1.2 meets requirement (>= 4.7.1.2)
+[INFO] 2025-06-24 14:49:29 - ✓ rsample version 1.3.0 meets requirement (>= 1.3.0)
+[INFO] 2025-06-24 14:49:29 - ✓ furrr version 0.3.1 meets requirement (>= 0.3.1)
+[INFO] 2025-06-24 14:49:29 - ✓ future version 1.40.0 meets requirement (>= 1.40.0)
+[INFO] 2025-06-24 14:49:29 - ✓ progressr version 0.15.1 meets requirement (>= 0.15.1)
+[INFO] 2025-06-24 14:49:29 - ✓ reshape2 version 1.4.4 meets requirement (>= 1.4.4)
+[INFO] 2025-06-24 14:49:29 - ✓ zoo version 1.8.13 meets requirement (>= 1.8.13)
+[INFO] 2025-06-24 14:49:29 - Package management complete: 28 success, 0 failures
+[INFO] 2025-06-24 14:49:29 - Updating renv lockfile...
+[ERROR] 2025-06-24 14:49:33 - ✗ Failed to update lockfile: aborting snapshot due to pre-flight validation failure
+[INFO] 2025-06-24 14:49:33 -
+=== FINAL STATE ===
+[INFO] 2025-06-24 14:49:33 - === PACKAGE REPORT ===
+[INFO] 2025-06-24 14:49:33 - dplyr | Required: >= 1.1.4 | Installed: 1.1.4 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - here | Required: >= 1.0.1 | Installed: 1.0.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - lubridate | Required: >= 1.9.4 | Installed: 1.9.4 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - readr | Required: >= 2.1.5 | Installed: 2.1.5 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - readxl | Required: >= 1.4.5 | Installed: 1.4.5 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - stringr | Required: >= 1.5.1 | Installed: 1.5.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - tidyr | Required: >= 1.3.1 | Installed: 1.3.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - purrr | Required: >= 1.0.2 | Installed: 1.0.2 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - magrittr | Required: >= 2.0.0 | Installed: 2.0.3 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - exactextractr | Required: >= 0.10.0 | Installed: 0.10.0 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - raster | Required: >= 3.6.32 | Installed: 3.6.32 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - sf | Required: >= 1.0.19 | Installed: 1.0.19 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - terra | Required: >= 1.8.43 | Installed: 1.8.43 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - ggplot2 | Required: >= 3.5.1 | Installed: 3.5.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - tmap | Required: >= 4.0 | Installed: 4.0 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - gridExtra | Required: >= 2.3 | Installed: 2.3 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - knitr | Required: >= 1.50 | Installed: 1.50 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - rmarkdown | Required: >= 2.21.0 | Installed: 2.29 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - tidyverse | Required: >= 2.0.0 | Installed: 2.0.0 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - caret | Required: >= 7.0.1 | Installed: 7.0.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - CAST | Required: >= 1.0.3 | Installed: 1.0.3 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - randomForest | Required: >= 4.7.1.2 | Installed: 4.7.1.2 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - rsample | Required: >= 1.3.0 | Installed: 1.3.0 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - furrr | Required: >= 0.3.1 | Installed: 0.3.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - future | Required: >= 1.40.0 | Installed: 1.40.0 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - progressr | Required: >= 0.15.1 | Installed: 0.15.1 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - reshape2 | Required: >= 1.4.4 | Installed: 1.4.4 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - zoo | Required: >= 1.8.13 | Installed: 1.8.13 | ✅ OK
+[INFO] 2025-06-24 14:49:33 - === END PACKAGE REPORT ===
+[INFO] 2025-06-24 14:49:33 - Package management completed in 7.72 seconds
+[INFO] 2025-06-24 14:49:33 - Log saved to: C:/Users/timon/Resilience BV/4020 SCane ESA DEMO - Documenten/General/4020 SCDEMO Team/4020 TechnicalData/WP3/smartcane/package_manager.log
+[SUCCESS] 2025-06-24 14:49:33 - 🎉 All packages successfully managed!
+[INFO] 2025-06-24 14:49:33 - 📋 Next steps:
+[INFO] 2025-06-24 14:49:33 - 1. Test your R scripts to ensure everything works
+[INFO] 2025-06-24 14:49:33 - 2. Commit renv.lock to version control
+[INFO] 2025-06-24 14:49:33 - 3. Share this script with your team
diff --git a/r_app/CI_report_dashboard_planet.Rmd b/r_app/CI_report_dashboard_planet.Rmd
index cb59c18..86f3aef 100644
--- a/r_app/CI_report_dashboard_planet.Rmd
+++ b/r_app/CI_report_dashboard_planet.Rmd
@@ -9,8 +9,8 @@ params:
output:
# html_document:
# toc: yes
- # df_print: paged
- word_document:
+ # df_print: paged
+ word_document:
reference_docx: !expr file.path("word-styles-reference-var1.docx")
toc: no
editor_options:
@@ -270,7 +270,7 @@ Report Date: **`r report_date_formatted`**
- Total Fields Monitored: `r total_fields`
- Total Area: `r total_area_ha` hectares
-**Report Generated:** `r format(Sys.Date(), "%B %d, %Y at %H:%M")`
+**Report Generated:** `r format(Sys.Date(), "%B %d, %Y")`
---
@@ -291,13 +291,40 @@ This automated report provides weekly analysis of sugarcane crop health using sa
-\pagebreak
+\newpage
+
+
+```{=openxml}
+
+
+
+
+
+
+
+
+
+
+ TABLE OF CONTENTS
+
+
+```
+
+```{=openxml}
+
+
+ Update this field to generate table of contents
+
+
+```
+
+\newpage
-\pagebreak
+\newpage
# Explanation of the Report
This report provides a detailed analysis of your sugarcane fields based on satellite imagery, helping you monitor crop health and development throughout the growing season. The data is processed weekly to give you timely insights for optimal farm management decisions.
@@ -332,7 +359,7 @@ Use these insights to identify areas that may need irrigation, fertilization, or
\pagebreak
# RGB Satellite Image - Current Week (if available)
-```{r render_rgb_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
+```{r render_rgb_map, echo=FALSE, fig.height=7, fig.width=10, message=FALSE, warning=FALSE}
# Check if RGB bands are available and create RGB map
tryCatch({
# Load the full raster to check available bands
@@ -382,12 +409,11 @@ tryCatch({
# Extract RGB bands
rgb_raster <- c(full_raster[[red_band]], full_raster[[green_band]], full_raster[[blue_band]])
names(rgb_raster) <- c("red", "green", "blue")
-
- # Create RGB map
+ # Create RGB map
map <- tmap::tm_shape(rgb_raster, unit = "m") +
tmap::tm_rgb() +
- tmap::tm_scalebar(position = c("right", "bottom"), text.color = "white") +
- tmap::tm_compass(position = c("right", "bottom"), text.color = "white") +
+ tmap::tm_scalebar(position = tm_pos_out("right", "bottom"), text.color = "black") +
+ tmap::tm_compass(position = tm_pos_out("right", "bottom"), text.color = "black") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "white", lwd = 2) +
tmap::tm_text("sub_field", size = 0.6, col = "white") +
@@ -413,22 +439,20 @@ tryCatch({
})
```
-\pagebreak
+\newpage
# Chlorophyll Index (CI) Overview Map - Current Week
-```{r render_ci_overview_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
+```{r render_ci_overview_map, echo=FALSE, fig.height=7, fig.width=10, message=FALSE, warning=FALSE}
# Create overview chlorophyll index map
tryCatch({ # Base shape
map <- tmap::tm_shape(CI, unit = "m") # Add raster layer with continuous spectrum (fixed scale 1-8 for consistent comparison)
map <- map + tmap::tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
- limits = c(1, 8)),
- col.legend = tm_legend(title = "Chlorophyll Index (CI)",
+ limits = c(1, 8)), col.legend = tm_legend(title = "Chlorophyll Index (CI)",
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
-
- # Complete the map with layout and other elements
+ # Complete the map with layout and other elements
map <- map +
- tmap::tm_scalebar(position = c("right", "bottom"), text.color = "black") +
- tmap::tm_compass(position = c("right", "bottom"), text.color = "black") +
+ tmap::tm_scalebar(position = tm_pos_out("right", "bottom"), text.color = "black") +
+ tmap::tm_compass(position = tm_pos_out("right", "bottom"), text.color = "black") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "black") +
tmap::tm_text("sub_field", size = 0.6, col = "black")
@@ -440,25 +464,26 @@ tryCatch({ # Base shape
plot(1, type="n", axes=FALSE, xlab="", ylab="")
text(1, 1, "Error creating CI overview map", cex=1.5)
})
+
```
+
+\newpage
\pagebreak
# Weekly Chlorophyll Index Difference Map
-```{r render_ci_difference_map, echo=FALSE, fig.height=6.9, fig.width=9, message=FALSE, warning=FALSE}
+```{r render_ci_difference_map, echo=FALSE, fig.height=7, fig.width=10, message=FALSE, warning=FALSE}
# Create chlorophyll index difference map
tryCatch({ # Base shape
map <- tmap::tm_shape(last_week_dif_raster_abs, unit = "m") # Add raster layer with continuous spectrum (centered at 0 for difference maps, fixed scale)
map <- map + tmap::tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
midpoint = 0,
- limits = c(-3, 3)),
- col.legend = tm_legend(title = "Chlorophyll Index (CI) Change",
+ limits = c(-3, 3)), col.legend = tm_legend(title = "Chlorophyll Index (CI) Change",
orientation = "landscape",
position = tm_pos_out("center", "bottom")))
-
- # Complete the map with layout and other elements
+ # Complete the map with layout and other elements
map <- map +
- tmap::tm_scalebar(position = c("right", "bottom"), text.color = "black") +
- tmap::tm_compass(position = c("right", "bottom"), text.color = "black") +
+ tmap::tm_scalebar(position = tm_pos_out("right", "bottom"), text.color = "black") +
+ tmap::tm_compass(position = tm_pos_out("right", "bottom"), text.color = "black") +
tmap::tm_shape(AllPivots0) +
tmap::tm_borders(col = "black") +
tmap::tm_text("sub_field", size = 0.6, col = "black")
@@ -471,10 +496,11 @@ tryCatch({ # Base shape
text(1, 1, "Error creating CI difference map", cex=1.5)
})
```
+
+\newpage
\pagebreak
-
-```{r generate_field_visualizations, eval=TRUE, fig.height=3.8, fig.width=10, message=FALSE,echo=FALSE, warning=FALSE, include=TRUE, results='asis'}
+```{r generate_field_visualizations, eval=TRUE, fig.height=4.5, fig.width=12, message=FALSE,echo=FALSE, warning=FALSE, include=TRUE, results='asis'}
# Generate detailed visualizations for each field
tryCatch({
# Merge field polygons for processing
@@ -483,7 +509,7 @@ tryCatch({
dplyr::summarise(.groups = 'drop')
# Generate plots for each field
- purrr::walk(AllPivots_merged$field[1:5], function(field_name) {
+ purrr::walk(AllPivots_merged$field, function(field_name) {
tryCatch({
cat("\n") # Add an empty line for better spacing
@@ -503,7 +529,7 @@ tryCatch({
borders = borders
)
- cat("\n")
+ # cat("\n")
# Call cum_ci_plot with explicit parameters
cum_ci_plot(
@@ -550,7 +576,7 @@ tryCatch({
})
# Add page break after each subgroup
- cat("\\pagebreak\n")
+ cat("\\newpage\n")
}
}, error = function(e) {
safe_log(paste("Error in subarea visualization section:", e$message), "ERROR")
@@ -699,7 +725,3 @@ tryCatch({
})
```
-
-
-\pagebreak
-
diff --git a/r_app/report_utils.R b/r_app/report_utils.R
index 8ed131b..6b7dc7c 100644
--- a/r_app/report_utils.R
+++ b/r_app/report_utils.R
@@ -73,15 +73,15 @@ create_CI_map <- function(pivot_raster, pivot_shape, pivot_spans, show_legend =
}
if (missing(age) || is.null(age)) {
stop("age parameter is required")
- }
- # Create the base map
+ } # Create the base map
map <- tm_shape(pivot_raster, unit = "m") # Add raster with continuous spectrum (fixed scale 1-8 for consistent comparison)
map <- map + tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
limits = c(1, 8)),
col.legend = tm_legend(title = "CI",
orientation = if(legend_is_portrait) "portrait" else "landscape",
show = show_legend,
- position = c("left", "bottom")))
+ position = if(show_legend) tm_pos_out("left", "center") else c("left", "bottom")
+ ))
# Add layout elements
map <- map + tm_layout(main.title = paste0("Max CI week ", week,"\n", age, " weeks old"),
main.title.size = 0.7)
@@ -128,16 +128,16 @@ create_CI_diff_map <- function(pivot_raster, pivot_shape, pivot_spans, show_lege
}
if (missing(age) || is.null(age)) {
stop("age parameter is required")
- }
- # Create the base map
+ } # Create the base map
map <- tm_shape(pivot_raster, unit = "m") # Add raster with continuous spectrum (centered at 0 for difference maps, fixed scale)
map <- map + tm_raster(col.scale = tm_scale_continuous(values = "brewer.rd_yl_gn",
midpoint = 0,
limits = c(-3, 3)),
- col.legend = tm_legend(title = "CI difference",
+ col.legend = tm_legend(title = "CI diff.",
orientation = if(legend_is_portrait) "portrait" else "landscape",
show = show_legend,
- position = c("left", "bottom")))
+ position = if(show_legend) tm_pos_out("right", "center") else c("left", "bottom")
+ ))
# Add layout elements
map <- map + tm_layout(main.title = paste0("CI change week ", week_1, " - week ", week_2, "\n", age, " weeks old"),
main.title.size = 0.7)
@@ -266,9 +266,9 @@ ci_plot <- function(pivotName,
CI_max_abs_three_week <- create_CI_diff_map(abs_CI_three_week, AllPivots2, joined_spans2,
show_legend = TRUE, legend_is_portrait = TRUE,
week_1 = week, week_2 = week_minus_3, age = age, borders = borders)
-
- # Arrange the maps
- tst <- tmap_arrange(CImap_m2, CImap_m1, CImap, CI_max_abs_last_week, CI_max_abs_three_week, nrow = 1)
+ # Arrange the maps with equal widths
+ tst <- tmap_arrange(CImap_m2, CImap_m1, CImap, CI_max_abs_last_week, CI_max_abs_three_week,
+ nrow = 1, widths = c(0.23, 0.18, 0.18, 0.18, 0.23))
# Output heading and map to R Markdown
cat(paste("## Field", pivotName, "-", age, "weeks after planting/harvest", "\n"))
diff --git a/renv/.gitignore b/renv/.gitignore
new file mode 100644
index 0000000..0ec0cbb
--- /dev/null
+++ b/renv/.gitignore
@@ -0,0 +1,7 @@
+library/
+local/
+cellar/
+lock/
+python/
+sandbox/
+staging/