diff --git a/.gitignore b/.gitignore index 5ca787b..15117f1 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,8 @@ Rplots.pdf # R Data Files *.rds +*.here +*.png !renv.lock # Data Files (Excel, CSV, Text) diff --git a/r_app/90_CI_report_with_kpis_agronomic_support.Rmd b/r_app/90_CI_report_with_kpis_agronomic_support.Rmd index 1d48256..d281ae5 100644 --- a/r_app/90_CI_report_with_kpis_agronomic_support.Rmd +++ b/r_app/90_CI_report_with_kpis_agronomic_support.Rmd @@ -2,12 +2,13 @@ params: ref: "word-styles-reference-var1.docx" output_file: "CI_report.docx" - report_date: "2026-02-04" #!r Sys.Date() - data_dir: "aura" + report_date: "2026-02-18" #!r Sys.Date() + data_dir: "tpc" mail_day: "Wednesday" borders: FALSE ci_plot_type: "both" colorblind_friendly: TRUE + language: "en" facet_by_season: FALSE x_axis_unit: "days" output: @@ -27,6 +28,7 @@ ci_plot_type <- params$ci_plot_type colorblind_friendly <- params$colorblind_friendly facet_by_season <- params$facet_by_season x_axis_unit <- params$x_axis_unit +language <- params$language ``` ```{r load_libraries, message=FALSE, warning=FALSE, include=FALSE} @@ -59,6 +61,10 @@ suppressPackageStartupMessages({ library(knitr) # For R Markdown document generation (code execution and output) library(flextable) # For formatted tables in Word output (professional table styling) library(officer) # For Word document manipulation (custom formatting, headers, footers) + + # Translation handling + library(readxl) # For reading in the translation Excel + library(glue) # For easy variable formatting in texts }) # Configure tmap for static plotting (required for legend.outside to work) @@ -432,7 +438,6 @@ tryCatch({ }) ``` - ```{r compute_benchmarks_once, include=FALSE} # Compute CI benchmarks once for the entire estate if (!is.null(CI_quadrant) && nrow(CI_quadrant) > 0) { @@ -454,34 +459,77 @@ if (!is.null(CI_quadrant) && nrow(CI_quadrant) > 0) { } ``` +```{r load_translations, include = FALSE} +# Load the translations document and build +tryCatch({ + # Define the sheets you want to load + sheet_names <- c("main_translations", "status_translations", "figure_translations") + + # Read each sheet and combine them into one dataframe + translation_list <- lapply(sheet_names, function(s) { + read_excel("translations/translations.xlsx", sheet = s) + }) + translations <- do.call(rbind, translation_list) + + if (!is.null(translations)) { + safe_log("Translations file succesfully loaded") + } else { + safe_log("Failed to load translations", "ERROR") + translations <- NULL + } +}, error = function(e) { + safe_log(paste("Error loading translation file:", e$message), "ERROR") + translations <<- NULL +}) + +# Try to select the translations for this report, otherwise fall back to English +tryCatch({ + if (language %in% names(translations)) { + lang_col <- language + safe_log(paste0("Loaded localisation for '", language,"'")) + } else { + lang_col <- "en" + safe_log(paste("Specified language not supported, defaulting to 'en'"), "WARNING") + } + localisation <- translations[,c("messages", lang_col)] + tr <- setNames(localisation[[2]], localisation$messages) +}, error = function(e) { + safe_log(paste("Error loading translations:", e$message), "ERROR") + localisation <<- NULL +}) + +# Helper function to handle missing translation keys +t <- function(key) { + if (key %in% names(tr)) { + txt <- glue(tr[key], .envir = parent.frame()) + txt <- gsub("\n", " \n", txt) + return(enc2utf8(as.character(txt))) + } else if (is.na(key)) { + return(t("NA")) + } else if (key == "") { + return("") + } else { + return(paste0(key)) + } +} +``` + ::: {custom-style="Cover_title" style="text-align:center; margin-top:120px;"} -Satellite Based Field Reporting +`r t("cover_title")` ::: - - ::: {custom-style="Cover_subtitle" style="text-align:center; margin-top:18px;"} -Chlorophyll Index (CI) Monitoring Report — `r toupper(params$data_dir)` Farm (Week `r { rd <- params$report_date; rd <- if (inherits(rd, "Date")) rd else suppressWarnings(as.Date(rd)); if (is.na(rd)) rd <- Sys.Date(); if (!is.null(params$week)) params$week else format(rd, '%V') }`, `r { rd <- params$report_date; rd <- if (inherits(rd, "Date")) rd else suppressWarnings(as.Date(rd)); if (is.na(rd)) rd <- Sys.Date(); format(rd, '%Y') }`) +`r t("cover_subtitle")` ::: \newpage -## Report Summary +`r t("report_summary")` -**Farm Location:** `r toupper(project_dir)` Estate -**Report Period:** Week `r current_week` of `r year` -**Data Source:** Planet Labs Satellite Imagery -**Analysis Type:** Chlorophyll Index (CI) Monitoring -**Report Generated on:** `r format(Sys.time(), "%B %d, %Y at %H:%M")` +`r t("report_structure")` -## Report Structure - -**Section 1:** Farm-wide analyses, summaries and Key Performance Indicators (KPIs) -**Section 2:** Field-by-field detailed analyses with maps and trend graphs -**Section 3:** Explanation of the report, definitions, and methodology - -## Key Insights +`r t("key_insights")` ```{r key_insights, echo=FALSE, results='asis'} # Calculate key insights from KPI data @@ -491,7 +539,7 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table # 1. Uniformity insights - group by interpretation if (!is.null(summary_tables$uniformity) && nrow(summary_tables$uniformity) > 0) { - cat("**Field Uniformity:**\n") + cat("\n", t("field_unif")) uniformity_counts <- summary_tables$uniformity %>% dplyr::select(interpretation, count = field_count) @@ -499,14 +547,14 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table status <- uniformity_counts$interpretation[i] count <- uniformity_counts$count[i] if (!is.na(status) && !is.na(count) && count > 0) { - cat("- ", count, " field(s) with ", status, "\n", sep="") + cat(" -", t("unif_status")) } } } # 2. Area change insights - group by interpretation if (!is.null(summary_tables$area_change) && nrow(summary_tables$area_change) > 0) { - cat("\n**Area Change Status:**\n") + cat("\n\n", t("field_area")) area_counts <- summary_tables$area_change %>% dplyr::select(interpretation, count = field_count) @@ -514,14 +562,14 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table status <- area_counts$interpretation[i] count <- area_counts$count[i] if (!is.na(status) && !is.na(count) && count > 0) { - cat("- ", count, " field(s) ", status, "\n", sep="") + cat(" -", t("area_status")) } } } # 3. Growth trend insights - group by trend_interpretation if (!is.null(summary_tables$growth_decline) && nrow(summary_tables$growth_decline) > 0) { - cat("\n**Growth Trends (4-Week):**\n") + cat("\n\n", t("growth_trend")) growth_counts <- summary_tables$growth_decline %>% dplyr::select(trend_interpretation, count = field_count) @@ -529,14 +577,14 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table trend <- growth_counts$trend_interpretation[i] count <- growth_counts$count[i] if (!is.na(trend) && !is.na(count) && count > 0) { - cat("- ", count, " field(s) with ", trend, "\n", sep="") + cat(" -", t("trend_status")) } } } # 4. Patchiness insights - group by patchiness_risk if (!is.null(summary_tables$patchiness) && nrow(summary_tables$patchiness) > 0) { - cat("\n**Field Patchiness Risk:**\n") + cat("\n\n", t("patch_risk")) patchiness_counts <- summary_tables$patchiness %>% dplyr::select(patchiness_risk, count = field_count) @@ -544,25 +592,25 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table risk <- patchiness_counts$patchiness_risk[i] count <- patchiness_counts$count[i] if (!is.na(risk) && !is.na(count) && count > 0) { - cat("- ", count, " field(s) at ", risk, " risk\n", sep="") + cat(" -", t("patch_status")) } } } # 5. Total fields analyzed total_fields <- sum(summary_tables$uniformity$field_count, na.rm = TRUE) - cat("\n**Total Fields Analyzed:** ", total_fields, "\n", sep="") + cat("\n\n", t("tot_fields_analyzed")) } else { - cat("KPI data not available for ", project_dir, " on this date.\n", sep="") + cat(t("kpi_na")) } ``` \newpage -# Section 1: Farm-wide Analyses and KPIs +`r t("section_i")` -## Executive Summary - Key Performance Indicators +`r t("exec_summary")`\n\n ```{r combined_kpi_table, echo=FALSE, results='asis'} # Display KPI tables - standardized format with Level, Count, Percent columns @@ -640,8 +688,12 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table display_df <- combined_df %>% dplyr::select(KPI = KPI_display, Level, Count, Percent) + # Translate the table for visualization + names(display_df) <- c(t("KPI"), t("Level"), t("Count"), t("Percent")) + display_df[, 1:2] <- lapply(display_df[, 1:2], function(col) sapply(col, t)) + ft <- flextable(display_df) %>% - merge_v(j = "KPI") %>% + merge_v(j = t("KPI")) %>% autofit() cum_rows <- cumsum(kpi_group_sizes) @@ -651,15 +703,15 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table hline(i = cum_rows[i], border = officer::fp_border(width = 2)) } } - + ft } else { - cat("No valid KPI summary tables found for display.\n") + cat(t("no_kpi_table")) } }, error = function(e) { safe_log(paste("Error displaying KPI tables:", e$message), "WARNING") - cat("KPI summary tables could not be displayed. Individual KPI sections will be shown below.\n") + cat(t("kpi_table_error")) }) } else { @@ -668,7 +720,7 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table ``` \newpage -## Field Alerts +`r t("field_alerts")` ```{r field_alerts_table, echo=FALSE, results='asis'} # Generate alerts for all fields @@ -678,7 +730,7 @@ generate_field_alerts <- function(field_details_table) { } # Check for required columns - required_cols <- c("Field", "Field Size (acres)", "Growth Uniformity", "Yield Forecast (t/ha)", + required_cols <- c("Field", "Growth Uniformity", "Yield Forecast (t/ha)", "Gap Score", "Decline Risk", "Patchiness Risk", "Mean CI", "CV Value", "Moran's I") missing_cols <- setdiff(required_cols, colnames(field_details_table)) @@ -698,7 +750,6 @@ generate_field_alerts <- function(field_details_table) { # Aggregate data for the field field_summary <- field_data %>% summarise( - field_size = sum(`Field Size (acres)`, na.rm = TRUE), uniformity_levels = paste(unique(`Growth Uniformity`), collapse = "/"), avg_yield_forecast = mean(`Yield Forecast (t/ha)`, na.rm = TRUE), max_gap_score = max(`Gap Score`, na.rm = TRUE), @@ -733,21 +784,21 @@ generate_field_alerts <- function(field_details_table) { # Generate alerts based on priority level if (priority_level == 1) { - field_alerts <- c(field_alerts, "Priority field - recommend inspection") + field_alerts <- c(field_alerts, t("priority")) } else if (priority_level == 2) { - field_alerts <- c(field_alerts, "Monitor - check when convenient") + field_alerts <- c(field_alerts, t("monitor")) } # Priority 3: No alert (no stress) # Keep other alerts for decline risk, patchiness risk, gap score if (field_summary$highest_decline_risk %in% c("High", "Very-high")) { - field_alerts <- c(field_alerts, "Growth decline observed") + field_alerts <- c(field_alerts, t("growth_decline")) } if (field_summary$highest_patchiness_risk == "High") { - field_alerts <- c(field_alerts, "High patchiness detected - recommend scouting") + field_alerts <- c(field_alerts, t("high_patchiness")) } if (field_summary$max_gap_score > 20) { - field_alerts <- c(field_alerts, "Gaps present - recommend review") + field_alerts <- c(field_alerts, t("gaps_present")) } # Only add alerts if there are any (skip fields with no alerts) @@ -793,8 +844,8 @@ if (exists("field_details_table") && !is.null(field_details_table) && nrow(field if ("Gap_Score" %in% names(field_details_for_alerts)) { field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Gap Score` = Gap_Score) } - if ("Growth_Uniformity" %in% names(field_details_for_alerts)) { - field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Growth Uniformity` = Growth_Uniformity) + if ("Uniformity_Category" %in% names(field_details_for_alerts)) { + field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Growth Uniformity` = Uniformity_Category) } if ("Decline_Risk" %in% names(field_details_for_alerts)) { field_details_for_alerts <- field_details_for_alerts %>% dplyr::rename(`Decline Risk` = Decline_Risk) @@ -816,7 +867,7 @@ if (exists("field_details_table") && !is.null(field_details_table) && nrow(field autofit() ft } else { - cat("No alerts data available.\n") + cat(t("alerts_na")) } } else { cat("Note: Field details data not available for alerts generation. Run 80_calculate_kpis.R to generate KPI data.\n") @@ -835,7 +886,6 @@ if (!exists("CI_quadrant")) { } ``` - ```{r load_field_boundaries, message=TRUE, warning=TRUE, include=FALSE} # Load field boundaries from GeoJSON tryCatch({ @@ -1064,9 +1114,9 @@ tryCatch({ # Choose color palette based on colorblind_friendly parameter if (colorblind_friendly) { fill_scale <- ggplot2::scale_fill_viridis_c( - name = "Chlorophyll Index (CI)", + name = t("legend_ci"), limits = c(1, 8), - direction = -1, # Reversed: green=high, yellow/red=low + direction = 1, na.value = "transparent", oob = scales::squish ) @@ -1074,7 +1124,7 @@ tryCatch({ # Use Red-Yellow-Green diverging palette (reversed for intuitive interpretation) fill_scale <- ggplot2::scale_fill_distiller( palette = "RdYlGn", - name = "Chlorophyll Index (CI)", + name = t("legend_ci"), limits = c(1, 8), direction = 1, # Standard direction for RdYlGn na.value = "transparent" @@ -1138,7 +1188,7 @@ tryCatch({ panel.background = ggplot2::element_rect(fill = "white", color = NA) ) + ggplot2::labs( - title = paste("Current Week CI Overview - Week", current_week, "of", current_iso_year) + title = t("ci_overview_title") ) # Print the map @@ -1157,7 +1207,7 @@ tryCatch({ }) ``` -```{r render_farm_ci_diff_map, echo=FALSE, message=FALSE, warning=FALSE, fig.height=6.8, fig.width=8.5, dpi=150, dev='png'} +```{r render_farm_ci_diff_map, echo=FALSE, fig.height=6.8, fig.width=8.5, message=FALSE, warning=FALSE, dev='png', dpi=150} # Create farm-level CI difference map (week-over-week change) tryCatch({ if (!is.null(farm_ci_diff_week_ll)) { @@ -1171,7 +1221,7 @@ tryCatch({ if (colorblind_friendly) { # Use plasma for colorblind-friendly diverging visualization fill_scale <- ggplot2::scale_fill_viridis_c( - name = "CI Change (Week-over-Week)", + name = t("legend_ci_change"), option = "plasma", limits = c(-3, 3), na.value = "transparent", @@ -1181,7 +1231,7 @@ tryCatch({ # Use Red-Blue diverging palette (red=decline, blue=increase) fill_scale <- ggplot2::scale_fill_distiller( palette = "RdBu", - name = "CI Change (Week-over-Week)", + name = t("legend_ci_change"), limits = c(-3, 3), direction = 1, na.value = "transparent" @@ -1245,7 +1295,7 @@ tryCatch({ panel.background = ggplot2::element_rect(fill = "white", color = NA) ) + ggplot2::labs( - title = paste("Weekly CI Change - Week", current_week, "vs Week", week_minus_1) + title = t("ci_change_title") ) # Print the map @@ -1264,18 +1314,9 @@ tryCatch({ }) ``` -# Section 2: Field-by-Field Analysis +\newpage -## Overview of Field-Level Insights -This section provides detailed, field-specific analyses including chlorophyll index maps, trend graphs, and performance metrics. Each field is analyzed individually to support targeted interventions. - -**Key Elements per Field:** -- Current and historical CI maps -- Week-over-week change visualizations -- Cumulative growth trends -- Field-specific KPI summaries - -*Navigate to the following pages for individual field reports.* +`r t("section_ii")` \newpage @@ -1414,14 +1455,14 @@ tryCatch({ if (nrow(field_kpi) > 0) { # Format KPIs as compact single line (no interpretations, just values) kpi_parts <- c( - sprintf("**CV:** %.2f", field_kpi$CV), - sprintf("**Mean CI:** %.2f", field_kpi$Mean_CI) + sprintf("**%s:** %.2f", t("cv_value"), field_kpi$CV), + sprintf("**%s:** %.2f", t("mean_ci"), field_kpi$Mean_CI) ) # Add Weekly_CI_Change if available (note: capital C and I) if (!is.null(field_kpi$Weekly_CI_Change) && !is.na(field_kpi$Weekly_CI_Change)) { change_sign <- ifelse(field_kpi$Weekly_CI_Change >= 0, "+", "") - kpi_parts <- c(kpi_parts, sprintf("**Δ CI:** %s%.2f", change_sign, field_kpi$Weekly_CI_Change)) + kpi_parts <- c(kpi_parts, sprintf("**Δ%s:** %s%.2f", t("CI"), change_sign, field_kpi$Weekly_CI_Change)) } # Compact trend display with symbols @@ -1433,17 +1474,17 @@ tryCatch({ grepl("Strong decline|Severe", field_kpi$Trend_Interpretation, ignore.case = TRUE) ~ "↓↓", TRUE ~ field_kpi$Trend_Interpretation ) - kpi_parts <- c(kpi_parts, sprintf("**Trend:** %s", trend_compact)) + kpi_parts <- c(kpi_parts, sprintf("**%s:** %s", t("Trend"), trend_compact)) if (!is.na(field_kpi$TCH_Forecasted) && field_kpi$TCH_Forecasted > 0) { - kpi_parts <- c(kpi_parts, sprintf("**Yield:** %.1f t/ha", field_kpi$TCH_Forecasted)) + kpi_parts <- c(kpi_parts, sprintf("**%s:** %.1f t/ha", t("Yield"), field_kpi$TCH_Forecasted)) } kpi_parts <- c( kpi_parts, - sprintf("**Gap:** %.0f", field_kpi$Gap_Score), - sprintf("**Patchiness:** %s", field_kpi$Patchiness_Risk), - sprintf("**Decline:** %s", field_kpi$Decline_Severity) + sprintf("**%s:** %.0f%%", t("Gaps"), field_kpi$Gap_Score), + sprintf("**%s:** %s", t("Patchiness"), t(field_kpi$Patchiness_Risk)), + sprintf("**%s:** %s", t("Decline"), t(field_kpi$Decline_Severity)) ) cat(paste(kpi_parts, collapse = " | "), "\n\n") # Double newline for markdown paragraph break @@ -1464,7 +1505,7 @@ tryCatch({ }) ``` -```{r generate_subarea_visualizations, echo=FALSE, fig.height=3.8, fig.width=6.5, dpi=150, message=FALSE, warning=FALSE, results='asis', eval=FALSE} +```{r generate_subarea_visualizations, eval=FALSE, echo=FALSE, fig.height=3.8, fig.width=6.5, message=FALSE, warning=FALSE, dpi=150, results='asis'} # Alternative visualization grouped by sub-area (disabled by default) tryCatch({ # Group pivots by sub-area @@ -1497,17 +1538,14 @@ tryCatch({ }) ``` -\newpage -## Detailed Field Performance Summary by Field - -The following table provides a comprehensive overview of all monitored fields with their key performance metrics from the KPI analysis. +`r t("detailed_field")` ```{r detailed_field_table, echo=FALSE, results='asis'} # Detailed field performance table if (!exists("field_details_table") || is.null(field_details_table) || nrow(field_details_table) == 0) { safe_log("No field details available for table", "WARNING") - cat("No field-level KPI data available for this report period.\n") + cat(t("no_field_data")) } else { # Calculate field sizes from boundaries (convert to acres) @@ -1561,33 +1599,56 @@ if (!exists("field_details_table") || is.null(field_details_table) || nrow(field field_details_clean <- field_details_clean %>% mutate(Weekly_CI_Change = round(Weekly_CI_Change, 2)) %>% select( - Field = Field_id, - `Field Size (acres)` = field_size_acres, - `Mean CI` = Mean_CI, - `Weekly CI Change` = Weekly_CI_Change, - `Yield Forecast (t/ha)` = TCH_Forecasted, - `Gap Score %` = Gap_Score, - `Decline Risk` = Decline_Severity, - `Patchiness Risk` = Patchiness_Risk, - `CV Value` = CV + field = Field_id, + field_size = field_size_acres, + mean_ci = Mean_CI, + weekly_ci_change = Weekly_CI_Change, + yield_forecast = TCH_Forecasted, + gap_score = Gap_Score, + decline_risk = Decline_Severity, + patchiness_risk = Patchiness_Risk, + cv_value = CV ) } else { field_details_clean <- field_details_clean %>% select( - Field = Field_id, - `Field Size (acres)` = field_size_acres, - `Mean CI` = Mean_CI, - `Yield Forecast (t/ha)` = TCH_Forecasted, - `Gap Score %` = Gap_Score, - `Decline Risk` = Decline_Severity, - `Patchiness Risk` = Patchiness_Risk, - `CV Value` = CV + field = Field_id, + field_size = field_size_acres, + mean_ci = Mean_CI, + yield_forecast = TCH_Forecasted, + gap_score = Gap_Score, + decline_risk = Decline_Severity, + patchiness_risk = Patchiness_Risk, + cv_value = CV ) } + # Translate risk levels + field_details_clean <- field_details_clean %>% + mutate( + across( + c(decline_risk, patchiness_risk), + ~ sapply(.x, t) + ) + ) + + # Translation labels for flextable + header_labels <- list( + field = t("field"), + field_size = t("field_size"), + mean_ci = t("mean_ci"), + weekly_ci_change = t("weekly_ci_change"), + yield_forecast = t("yield_forecast"), + gap_score = t("gap_score"), + decline_risk = t("decline_risk"), + patchiness_risk = t("patchiness_risk"), + cv_value = t("cv_value") + ) + # Display the cleaned field table with flextable (fit to page width) ft <- flextable(field_details_clean) %>% - set_caption("Detailed Field Performance Summary") %>% + set_header_labels(values = header_labels) %>% + set_caption(t("detailed_field_caption")) %>% theme_booktabs() %>% set_table_properties(width = 1, layout = "autofit") # Fit to 100% page width with auto-adjust @@ -1597,169 +1658,125 @@ if (!exists("field_details_table") || is.null(field_details_table) || nrow(field \newpage -# Section 3: Report Methodology and Definitions +`r t("section_iii")` -## About This Report - -This automated report provides weekly analysis of sugarcane crop health using satellite-derived Chlorophyll Index (CI) measurements. The analysis supports: - -• Scouting of growth related issues that are in need of attention -• Timely actions can be taken such that negative impact is reduced -• Monitoring of the crop growth rates of the farm, providing evidence of performance -• Planning of harvest moment and mill logistics is supported such that optimal tonnage and sucrose levels can be harvested. - -The base of the report is the Chlorophyll Index. The chlorophyll index identifies: -• Field-level crop health variations => target problem area's -• Weekly changes in crop vigor => scout for diseases and stress -• Areas requiring attention by the agricultural and irrigation teams -• Growth patterns across different field sections - -Key Features: - High-resolution satellite imagery analysis - Week-over-week change detection - Individual field performance metrics - Actionable insights for crop management - -### Explanation of the Report - -This report provides a detailed analysis (3x3m of resolution) of your sugarcane fields based on satellite imagery. It supports 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. - -### What is the Chlorophyll Index (CI)? - -The Chlorophyll Index (CI) is a vegetation index that measures the relative amount of chlorophyll in plant leaves. Chlorophyll is the green pigment responsible for photosynthesis in plants. Higher CI values indicate: -• Greater photosynthetic activity -• Healthier plant tissue -• Better nitrogen uptake -• More vigorous crop growth - -CI values typically range from 0 (bare soil or severely stressed vegetation) to 7+ (very healthy, dense vegetation). For sugarcane, values between 3-7 generally indicate good crop health, depending on the growth stage. +```{r include=FALSE} +# Tries to get the CI graph in different language, otherwise falls back on English +target_img <- paste0("CI_graph_example_", language, ".png") +img_path <- ifelse(file.exists(target_img), target_img, "CI_graph_example.png") +```