Initial localisation report text

This commit is contained in:
Nik Verweel 2026-02-18 15:30:49 +01:00
parent 58eab001a6
commit fa5db056a4

View file

@ -8,6 +8,7 @@ params:
borders: FALSE
ci_plot_type: "both"
colorblind_friendly: TRUE
lang: "es"
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
lang <- params$lang
```
```{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
})
# Load custom utility functions
@ -268,7 +274,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) {
@ -290,21 +295,68 @@ if (!is.null(CI_quadrant) && nrow(CI_quadrant) > 0) {
}
```
## Report Summary
```{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)
**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")`
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
})
## Report Structure
# Try to select the translations for this report, otherwise fall back to English
tryCatch({
if (lang %in% names(translations)) {
lang_col <- lang
safe_log(paste0("Loaded localisation for '", lang,"'"))
}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
})
**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
# Helper function to handle missing translation keys
t <- function(key) {
if (key %in% names(tr)) {
txt <- glue(tr[key], .envir = parent.frame())
# 1. Handle tabs
txt <- gsub(">>", " ", txt)
# 2. Handle newlines
txt <- gsub("\n", " \n", txt)
return(txt)
} else {
return(paste0("[", key, "]"))
}
}
```
## Key Insights
`r t("report_summary")`
`r t("report_structure")`
`r t("key_insights")`
```{r key_insights, echo=FALSE, results='asis'}
# Calculate key insights from aggregated KPI summary data
@ -321,62 +373,62 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
# Uniformity insights
if (!is.null(uniformity_summary) && nrow(uniformity_summary) > 0) {
cat("**Field Uniformity:**\n")
cat(t("field_unif"))
for (i in 1:nrow(uniformity_summary)) {
status <- uniformity_summary$Status[i]
count <- uniformity_summary$`Field Count`[i]
if (count > 0) {
cat("- ", count, " field(s) with ", status, "\n", sep="")
cat(t("unif_status"))
}
}
}
# Area change insights
if (!is.null(area_change_summary) && nrow(area_change_summary) > 0) {
cat("\n**Area Change Status:**\n")
cat("\n", t("field_area"))
for (i in 1:nrow(area_change_summary)) {
status <- area_change_summary$Status[i]
count <- area_change_summary$`Field Count`[i]
if (count > 0 && !is.na(status)) {
cat("- ", count, " field(s) ", status, "\n", sep="")
cat(t("area_status"))
}
}
}
# Growth trend insights
if (!is.null(growth_summary) && nrow(growth_summary) > 0) {
cat("\n**Growth Trends (4-Week):**\n")
cat("\n", t("growth_trend"))
for (i in 1:nrow(growth_summary)) {
trend <- growth_summary$Trend[i]
count <- growth_summary$`Field Count`[i]
if (count > 0 && !is.na(trend)) {
cat("- ", count, " field(s) with ", trend, "\n", sep="")
cat(t("trend_status"))
}
}
}
# Weed pressure insights
if (!is.null(weed_summary) && nrow(weed_summary) > 0) {
cat("\n**Weed/Pest Pressure Risk:**\n")
cat("\n", t("weed_press"))
for (i in 1:nrow(weed_summary)) {
risk <- weed_summary$`Risk Level`[i]
count <- weed_summary$`Field Count`[i]
if (count > 0 && !is.na(risk)) {
cat("- ", count, " field(s) at ", risk, " risk\n", sep="")
cat(t("weed_status"))
}
}
}
} 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")`
```{r combined_kpi_table, echo=FALSE, results='asis'}
# Display KPI tables - standardized format with Level, Count, Percent columns
@ -481,7 +533,7 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
}
```
## Field Alerts
`r t("field_alerts")`
```{r field_alerts_table, echo=FALSE, results='asis'}
# Generate alerts for all fields
@ -592,7 +644,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")
@ -679,7 +731,7 @@ if (!exists("field_details_table") || is.null(field_details_table)) {
}
```
## Farm-Level Overview Maps
`r t("overview_maps")`
```{r aggregate_farm_level_rasters, message=FALSE, warning=FALSE, include=FALSE}
# Aggregate per-field weekly mosaics into single farm-level rasters for visualization
@ -826,7 +878,7 @@ tryCatch({
})
```
### Chlorophyll Index (CI) Overview Map - Current Week
`r t("ci_overview_map")`
```{r render_farm_ci_map, echo=FALSE, fig.height=5.5, fig.width=6.5, dpi=150, dev='png', message=FALSE, warning=FALSE}
# Create farm-level chlorophyll index map with OpenStreetMap basemap
@ -934,7 +986,7 @@ tryCatch({
})
```
### Weekly Chlorophyll Index Difference Map
`r t("ci_diff_map")`
```{r render_farm_ci_diff_map, echo=FALSE, fig.height=5.5, fig.width=6.5, dpi=150, dev='png', message=FALSE, warning=FALSE}
# Create farm-level CI difference map (week-over-week change)
@ -1045,18 +1097,7 @@ tryCatch({
\newpage
# Section 2: Field-by-Field Analysis
## 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
@ -1257,11 +1298,7 @@ tryCatch({
})
```
## KPI Summary by Field
## Detailed Field Performance Summary
The following table provides a comprehensive overview of all monitored fields with their key performance metrics from the KPI analysis.
`r t("kpi_per_field")`
```{r detailed_field_table, echo=FALSE, results='asis'}
# Detailed field performance table
@ -1329,6 +1366,8 @@ if (!exists("field_details_table") || is.null(field_details_table)) {
)
}
# Set names according to localisation
names(field_details_clean) <- c(t("field"), t("field_size"), t("grow_unif"), t("yield_forecast"), t("gap_score"), t("decline_risk"), t("weed_risk"), t("mean_ci"), t("cv_value"))
# Display the cleaned field table with flextable
# Set column widths to fit page (approximately 6.5 inches for 1-inch margins)
@ -1336,7 +1375,7 @@ if (!exists("field_details_table") || is.null(field_details_table)) {
col_widths <- c(0.97, 0.73, 0.80, 0.80, 0.65, 0.73, 0.65, 0.56, 0.48) # inches for each column
ft <- flextable(field_details_clean) %>%
set_caption("Detailed Field Performance Summary") %>%
set_caption(t("detailed_kpi_caption")) %>%
width(width = col_widths)
ft
@ -1344,112 +1383,39 @@ ft
\newpage
# Section 3: Report Methodology and Definitions
`r t("section_iii")`
## About This Report
![`r t("ci_caption")`](CI_graph_example.png)
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.
![Chlorophyll Index Example](CI_graph_example.png)
### What You'll Find in This Report:
1. **Key Performance Indicators (KPIs):**
The report provides a farm-wide analysis based on the Chlorophyll Index (CI) changes. KPIs are calculated field by field and summarized in tables. The current KPIs included are:
- **Field Uniformity:** Assesses the consistency of crop growth within each field using the coefficient of variation (CV) of CI values. Uniformity levels are classified as:
- **Excellent:** CV < 0.08 (very uniform growth)
- **Good:** CV < 0.15 (acceptable uniformity)
- **Moderate:** CV < 0.25 (some variation present)
- **Poor:** CV ≥ 0.25 (high variation - investigate irrigation, fertility, or pests)
- **Area Change:** Summarizes the proportion of field area that is improving, stable, or declining week-over-week, based on CI changes. Helps identify fields requiring immediate attention.
- **TCH Forecasted:** Provides yield predictions (tons cane per hectare) for mature fields (typically over 240 days old), using a machine learning model trained on historical CI and yield data. Helps plan harvest timing and logistics.
- **Weed Presence Score:** Detects rapid CI increases between weeks as a proxy for weed outbreaks in young fields (< 8 months old). After 8 months, canopy closure prevents weed growth. Risk levels based on percentage of pixels showing rapid growth (> 2.0 CI units increase):
- **Low:** < 10% of field area (minimal weed presence)
- **Moderate:** 1025% (monitor and scout)
- **High:** > 25% (requires immediate intervention)
- **Note:** Mature fields (≥ 8 months) show "Canopy closed - Low weed risk" as the closed canopy suppresses weed growth.
- **Gap Filling Score:** Indicates the proportion of a field with low CI values (lowest 25% of the distribution), highlighting areas with poor crop establishment or gaps that may need replanting.
2. **Overview Map: Growth on Farm:**
Provides a traffic light overview of field-by-field growth status for quick prioritization and reporting.
3. **Chlorophyll Index Overview Map:**
Shows current CI values for all fields, helping to identify high- and low-performing areas.
4. **Field-by-Field Analysis:**
Includes detailed maps, trend graphs, and performance metrics for each field.
5. **Yield Prediction:**
For mature crops (over 240 days), yield is predicted using current and historical CI data.
6. **Farm Overview Table:**
Presents numerical field-level results for all KPIs.
`r t("find_report")`
---
### Historical Benchmark Lines
The CI time series graphs include historical benchmark lines for the 10th, 50th, and 90th percentiles of CI values across all fields and seasons.
**Note:** These lines are now all rendered as solid lines (not dashed or dotted), with different colors for each percentile.
- **10th Percentile:** Lower end of historical performance
- **50th Percentile:** Median historical performance
- **90th Percentile:** Upper end of historical performance
Comparing the current season to these lines helps assess whether crop growth is below, at, or above historical norms.
`r t("historical_benchmark")`
\newpage
## Report Metadata
`r t("metadata")`
```{r report_metadata, echo=FALSE, results='asis'}
metadata_info <- data.frame(
Metric = c("Report Generated", "Data Source", "Analysis Period", "Total Fields", "Next Update"),
Metric = c(t("report_gen"), t("data_source"), t("analysis_period"), t("tot_fields"), t("next_update")),
Value = c(
format(Sys.time(), "%Y-%m-%d %H:%M:%S"),
paste("Project", toupper(project_dir)),
paste("Week", current_week, "of", year),
ifelse(exists("AllPivots0"), nrow(AllPivots0 %>% filter(!is.na(field)) %>% group_by(field) %>% summarise()), "Unknown"),
"Next Wednesday"
paste(t("project"), toupper(project_dir)),
paste(t("week"), current_week, "of", year),
ifelse(exists("AllPivots0"), nrow(AllPivots0 %>% filter(!is.na(field)) %>% group_by(field) %>% summarise()), t("unknown")),
t("next_wed")
)
)
# Set names of columns according to localisation
names(metadata_info) <- c(t("metric"), t("value"))
ft <- flextable(metadata_info) %>%
set_caption("Report Metadata") %>%
set_caption(t("metadata_caption")) %>%
autofit()
ft
```
*This report was automatically generated by the SmartCane monitoring system. For questions or additional analysis, please contact the technical team.*
`r t("disclaimer")`