Merge branch 'code-improvements' into SC-204

This commit is contained in:
Timon 2026-03-18 14:31:22 +01:00
commit 573733cfb4
10 changed files with 685 additions and 218 deletions

View file

@ -9,7 +9,8 @@
"Bash(/c/Users/timon/AppData/Local/r-miniconda/python.exe -c \":*)", "Bash(/c/Users/timon/AppData/Local/r-miniconda/python.exe -c \":*)",
"Bash(python3 -c \":*)", "Bash(python3 -c \":*)",
"Bash(Rscript -e \":*)", "Bash(Rscript -e \":*)",
"Bash(\"/c/Program Files/R/R-4.4.3/bin/x64/Rscript.exe\" -e \":*)" "Bash(\"/c/Program Files/R/R-4.4.3/bin/x64/Rscript.exe\" -e \":*)",
"Read(//c/Users/timon/Documents/SmartCane_code/r_app/**)"
] ]
} }
} }

186
create_field_checklist.R Normal file
View file

@ -0,0 +1,186 @@
# Creates an Excel checklist from pivot.geojson
# Fields sorted largest to smallest, split across Timon/Joey/Dimitra side-by-side
# Install packages if needed
if (!requireNamespace("jsonlite", quietly = TRUE)) install.packages("jsonlite", repos = "https://cloud.r-project.org")
if (!requireNamespace("openxlsx", quietly = TRUE)) install.packages("openxlsx", repos = "https://cloud.r-project.org")
library(jsonlite)
library(openxlsx)
# ---- Load GeoJSON ----
geojson_path <- "laravel_app/storage/app/angata/pivot.geojson"
gj <- fromJSON(geojson_path, simplifyVector = FALSE)
features <- gj$features
cat(sprintf("Total features: %d\n", length(features)))
# ---- Shoelace area (degrees²) ----
shoelace <- function(ring) {
n <- length(ring)
lons <- sapply(ring, `[[`, 1)
lats <- sapply(ring, `[[`, 2)
area <- 0
for (i in seq_len(n)) {
j <- (i %% n) + 1
area <- area + lons[i] * lats[j] - lons[j] * lats[i]
}
abs(area) / 2
}
# ---- Approx area in m² ----
area_m2 <- function(ring) {
R <- 6371000
lats <- sapply(ring, `[[`, 2)
mean_lat <- mean(lats)
lat_rad <- mean_lat * pi / 180
m_per_deg_lat <- R * pi / 180
m_per_deg_lon <- R * cos(lat_rad) * pi / 180
shoelace(ring) * m_per_deg_lat * m_per_deg_lon
}
# ---- Compute feature areas ----
compute_area <- function(feat) {
geom <- feat$geometry
total <- 0
if (geom$type == "MultiPolygon") {
for (polygon in geom$coordinates) {
total <- total + area_m2(polygon[[1]]) # outer ring
}
} else if (geom$type == "Polygon") {
total <- total + area_m2(geom$coordinates[[1]])
}
total
}
field_names <- sapply(features, function(f) f$properties$field)
areas_m2 <- sapply(features, compute_area)
areas_ha <- areas_m2 / 10000
df <- data.frame(
field = field_names,
area_ha = round(areas_ha, 2),
stringsAsFactors = FALSE
)
# Sort largest to smallest
df <- df[order(df$area_ha, decreasing = TRUE), ]
df$rank <- seq_len(nrow(df))
cat("\nTop 10 fields by area:\n")
print(head(df[, c("rank", "field", "area_ha")], 10))
# ---- Split: Timon=1st, Joey=2nd, Dimitra=3rd ----
idx <- seq_len(nrow(df))
timon <- df[idx %% 3 == 1, ]
joey <- df[idx %% 3 == 2, ]
dimitra <- df[idx %% 3 == 0, ]
cat(sprintf("\nSplit: Timon=%d, Joey=%d, Dimitra=%d\n",
nrow(timon), nrow(joey), nrow(dimitra)))
# ---- Build Excel ----
wb <- createWorkbook()
addWorksheet(wb, "Field Checklist")
# Header colors
col_timon <- "1F6AA5"
col_joey <- "2E7D32"
col_dimitra <- "7B1FA2"
alt_timon <- "D6E4F0"
alt_joey <- "D7F0D8"
alt_dimitra <- "EDD7F0"
header_font <- createStyle(fontName = "Calibri", fontSize = 11, fontColour = "FFFFFF",
halign = "CENTER", valign = "center", textDecoration = "bold",
border = "TopBottomLeftRight")
sub_font <- createStyle(fontName = "Calibri", fontSize = 10, fontColour = "FFFFFF",
halign = "CENTER", valign = "center", textDecoration = "bold",
border = "TopBottomLeftRight")
# Title row
writeData(wb, "Field Checklist",
"Angata Pivot Field Checklist — sorted largest to smallest",
startRow = 1, startCol = 1)
mergeCells(wb, "Field Checklist", cols = 1:14, rows = 1)
addStyle(wb, "Field Checklist",
createStyle(fontName = "Calibri", fontSize = 13, textDecoration = "bold",
halign = "CENTER", valign = "center",
fgFill = "F0F0F0"),
rows = 1, cols = 1)
setRowHeights(wb, "Field Checklist", rows = 1, heights = 28)
# Person block writer
write_person_block <- function(wb, ws_name, data, start_col, hdr_color, alt_color, person_name) {
end_col <- start_col + 3
# Person name header (row 2)
mergeCells(wb, ws_name, cols = start_col:end_col, rows = 2)
writeData(wb, ws_name, person_name, startRow = 2, startCol = start_col)
addStyle(wb, ws_name,
createStyle(fontName = "Calibri", fontSize = 12, fontColour = "FFFFFF",
textDecoration = "bold", halign = "CENTER", valign = "center",
fgFill = hdr_color, border = "TopBottomLeftRight",
borderColour = "999999"),
rows = 2, cols = start_col:end_col)
# Sub-headers (row 3)
sub_headers <- c("#", "Field", "Area (ha)", "Checked \u2713")
writeData(wb, ws_name, as.data.frame(t(sub_headers)),
startRow = 3, startCol = start_col, colNames = FALSE)
addStyle(wb, ws_name,
createStyle(fontName = "Calibri", fontSize = 10, fontColour = "FFFFFF",
textDecoration = "bold", halign = "CENTER", valign = "center",
fgFill = hdr_color, border = "TopBottomLeftRight",
borderColour = "999999"),
rows = 3, cols = start_col:end_col)
# Data rows (starting row 4)
n <- nrow(data)
for (i in seq_len(n)) {
row_num <- i + 3
bg <- if (i %% 2 == 0) alt_color else "FFFFFF"
# Rank
writeData(wb, ws_name, i, startRow = row_num, startCol = start_col)
# Field name
writeData(wb, ws_name, data$field[i], startRow = row_num, startCol = start_col + 1)
# Area
writeData(wb, ws_name, data$area_ha[i], startRow = row_num, startCol = start_col + 2)
# Checked (empty)
writeData(wb, ws_name, "", startRow = row_num, startCol = start_col + 3)
row_style <- createStyle(fontName = "Calibri", fontSize = 10, halign = "center",
fgFill = bg, border = "TopBottomLeftRight",
borderColour = "CCCCCC")
field_style <- createStyle(fontName = "Calibri", fontSize = 10, halign = "left",
fgFill = bg, border = "TopBottomLeftRight",
borderColour = "CCCCCC")
addStyle(wb, ws_name, row_style, rows = row_num, cols = start_col)
addStyle(wb, ws_name, field_style, rows = row_num, cols = start_col + 1)
addStyle(wb, ws_name, row_style, rows = row_num, cols = start_col + 2)
addStyle(wb, ws_name, row_style, rows = row_num, cols = start_col + 3)
}
}
write_person_block(wb, "Field Checklist", timon, 1, col_timon, alt_timon, "Timon")
write_person_block(wb, "Field Checklist", joey, 6, col_joey, alt_joey, "Joey")
write_person_block(wb, "Field Checklist", dimitra, 11, col_dimitra, alt_dimitra, "Dimitra")
# Column widths (col 5 and 10 = spacers)
setColWidths(wb, "Field Checklist",
cols = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14),
widths = c(5, 14, 10, 12, 2, 5, 14, 10, 12, 2, 5, 14, 10, 12))
# Row heights
setRowHeights(wb, "Field Checklist", rows = 2:3, heights = c(22, 18))
max_rows <- max(nrow(timon), nrow(joey), nrow(dimitra))
setRowHeights(wb, "Field Checklist", rows = 4:(max_rows + 3), heights = 16)
# Freeze panes below header
freezePane(wb, "Field Checklist", firstActiveRow = 4)
# Save
out_path <- "angata_field_checklist.xlsx"
saveWorkbook(wb, out_path, overwrite = TRUE)
cat(sprintf("\nExcel saved to: %s\n", out_path))
cat(sprintf("Total: %d fields — Timon: %d, Joey: %d, Dimitra: %d\n",
nrow(df), nrow(timon), nrow(joey), nrow(dimitra)))

194
create_field_checklist.py Normal file
View file

@ -0,0 +1,194 @@
"""
Creates an Excel checklist from pivot.geojson, with fields sorted largest to smallest,
split across Timon (1st), Joey (2nd), Dimitra (3rd) in a single sheet side-by-side.
"""
import json
import math
import os
try:
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
except ImportError:
print("Installing openpyxl...")
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl"])
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
def shoelace_area(coords):
"""Compute area in square degrees using the shoelace formula."""
n = len(coords)
area = 0.0
for i in range(n):
j = (i + 1) % n
area += coords[i][0] * coords[j][1]
area -= coords[j][0] * coords[i][1]
return abs(area) / 2.0
def polygon_area_m2(ring):
"""
Approximate polygon area in using spherical coordinates.
ring: list of [lon, lat] pairs
"""
R = 6371000 # Earth radius in meters
area_deg2 = shoelace_area(ring)
# Convert to m² using mean latitude
mean_lat = sum(p[1] for p in ring) / len(ring)
lat_rad = math.radians(mean_lat)
# 1 degree lat ≈ R * pi/180 meters, 1 degree lon ≈ R * cos(lat) * pi/180 meters
m_per_deg_lat = R * math.pi / 180
m_per_deg_lon = R * math.cos(lat_rad) * math.pi / 180
return area_deg2 * m_per_deg_lat * m_per_deg_lon
def feature_area(feature):
"""Compute total area of a feature (MultiPolygon or Polygon) in m²."""
geom = feature["geometry"]
total = 0.0
if geom["type"] == "MultiPolygon":
for polygon in geom["coordinates"]:
# First ring is outer boundary
total += polygon_area_m2(polygon[0])
elif geom["type"] == "Polygon":
total += polygon_area_m2(geom["coordinates"][0])
return total
# Load GeoJSON
geojson_path = r"C:\Users\timon\Documents\SmartCane_code\laravel_app\storage\app\angata\pivot.geojson"
with open(geojson_path, "r", encoding="utf-8") as f:
gj = json.load(f)
features = gj["features"]
print(f"Total features: {len(features)}")
# Compute areas and sort
fields = []
for feat in features:
field_name = feat["properties"].get("field", "?")
area = feature_area(feat)
fields.append({"field": field_name, "area_m2": area, "area_ha": area / 10000})
fields.sort(key=lambda x: x["area_m2"], reverse=True)
# Print top 10 for verification
print("\nTop 10 fields by area:")
for i, f in enumerate(fields[:10]):
print(f" {i+1:3d}. Field {f['field']:15s} {f['area_ha']:.2f} ha")
# Split: index 0,3,6,... → Timon; 1,4,7,... → Joey; 2,5,8,... → Dimitra
timon = [f for i, f in enumerate(fields) if i % 3 == 0]
joey = [f for i, f in enumerate(fields) if i % 3 == 1]
dimitra = [f for i, f in enumerate(fields) if i % 3 == 2]
print(f"\nSplit: Timon={len(timon)}, Joey={len(joey)}, Dimitra={len(dimitra)}")
# Create Excel
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "Field Checklist"
# Color palette
colors = {
"timon": {"header_bg": "1F6AA5", "alt_bg": "D6E4F0"},
"joey": {"header_bg": "2E7D32", "alt_bg": "D7F0D8"},
"dimitra": {"header_bg": "7B1FA2", "alt_bg": "EDD7F0"},
}
white_font = Font(name="Calibri", bold=True, color="FFFFFF", size=11)
black_font = Font(name="Calibri", size=10)
bold_black = Font(name="Calibri", bold=True, size=10)
center_align = Alignment(horizontal="center", vertical="center", wrap_text=True)
thin = Side(style="thin", color="CCCCCC")
border = Border(left=thin, right=thin, top=thin, bottom=thin)
# Column layout: each person gets 3 columns (Rank, Field, Area ha, Checked)
# Spacing: col 1-4 = Timon, col 5 = spacer, col 6-9 = Joey, col 10 = spacer, col 11-14 = Dimitra
persons = [
("Timon", timon, 1),
("Joey", joey, 6),
("Dimitra", dimitra, 11),
]
# Row 1: Person name header (merged across 4 cols)
ws.row_dimensions[1].height = 25
ws.row_dimensions[2].height = 20
for name, data, start_col in persons:
c = colors[name.lower()]
# Merge cells for person name
end_col = start_col + 3
ws.merge_cells(
start_row=1, start_column=start_col,
end_row=1, end_column=end_col
)
cell = ws.cell(row=1, column=start_col, value=name)
cell.font = white_font
cell.fill = PatternFill("solid", fgColor=c["header_bg"])
cell.alignment = center_align
cell.border = border
# Sub-headers
sub_headers = ["#", "Field", "Area (ha)", "Checked ✓"]
for i, hdr in enumerate(sub_headers):
cell = ws.cell(row=2, column=start_col + i, value=hdr)
cell.font = white_font
cell.fill = PatternFill("solid", fgColor=c["header_bg"])
cell.alignment = center_align
cell.border = border
# Data rows
for row_i, field in enumerate(data):
row_num = row_i + 3
ws.row_dimensions[row_num].height = 16
alt = row_i % 2 == 1
bg = c["alt_bg"] if alt else "FFFFFF"
fill = PatternFill("solid", fgColor=bg)
values = [row_i + 1, field["field"], round(field["area_ha"], 2), ""]
for col_i, val in enumerate(values):
cell = ws.cell(row=row_num, column=start_col + col_i, value=val)
cell.font = black_font
cell.fill = fill
cell.border = border
cell.alignment = Alignment(horizontal="center" if col_i != 1 else "left",
vertical="center")
# Column widths
col_widths = {1: 5, 2: 14, 3: 10, 4: 12, # Timon
5: 2, # spacer
6: 5, 7: 14, 8: 10, 9: 12, # Joey
10: 2, # spacer
11: 5, 12: 14, 13: 10, 14: 12} # Dimitra
for col, width in col_widths.items():
ws.column_dimensions[get_column_letter(col)].width = width
# Freeze header rows
ws.freeze_panes = "A3"
# Title row above everything — insert a title row
ws.insert_rows(1)
ws.merge_cells("A1:N1")
title_cell = ws.cell(row=1, column=1, value="Angata Pivot Field Checklist — sorted largest to smallest")
title_cell.font = Font(name="Calibri", bold=True, size=13, color="1A1A1A")
title_cell.alignment = Alignment(horizontal="center", vertical="center")
title_cell.fill = PatternFill("solid", fgColor="F0F0F0")
ws.row_dimensions[1].height = 28
# Re-freeze after insert
ws.freeze_panes = "A4"
out_path = r"C:\Users\timon\Documents\SmartCane_code\angata_field_checklist.xlsx"
wb.save(out_path)
print(f"\nExcel saved to: {out_path}")
print(f"Total fields: {len(fields)}")
print(f" Timon: {len(timon)} fields")
print(f" Joey: {len(joey)} fields")
print(f" Dimitra: {len(dimitra)} fields")

View file

@ -170,7 +170,7 @@ calculate_status_alert <- function(imminent_prob, age_week, mean_ci,
# Priority 1: HARVEST READY - highest business priority # Priority 1: HARVEST READY - highest business priority
# Field is mature (≥12 months) AND harvest model predicts imminent harvest # Field is mature (≥12 months) AND harvest model predicts imminent harvest
if (!is.na(imminent_prob) && imminent_prob > 0.5 && !is.na(age_week) && age_week >= 52) { if (!is.na(imminent_prob) && imminent_prob > 0.3 && !is.na(age_week) && age_week >= 52) {
return("harvest_ready") return("harvest_ready")
} }

View file

@ -650,48 +650,6 @@ get_phase_by_age <- function(age_weeks) {
return("Unknown") return("Unknown")
} }
#' Get status trigger based on CI values and field age
get_status_trigger <- function(ci_values, ci_change, age_weeks) {
if (is.na(age_weeks) || length(ci_values) == 0) return(NA_character_)
ci_values <- ci_values[!is.na(ci_values)]
if (length(ci_values) == 0) return(NA_character_)
pct_above_2 <- sum(ci_values > 2) / length(ci_values) * 100
pct_at_or_above_2 <- sum(ci_values >= 2) / length(ci_values) * 100
ci_cv <- if (mean(ci_values, na.rm = TRUE) > 0) sd(ci_values) / mean(ci_values, na.rm = TRUE) else 0
mean_ci <- mean(ci_values, na.rm = TRUE)
if (age_weeks >= 0 && age_weeks <= 6) {
if (pct_at_or_above_2 >= 70) {
return("germination_complete")
} else if (pct_above_2 > 10) {
return("germination_started")
}
}
if (age_weeks >= 45) {
return("harvest_ready")
}
if (age_weeks > 6 && !is.na(ci_change) && ci_change < -1.5 && ci_cv < 0.25) {
return("stress_detected_whole_field")
}
if (age_weeks > 6 && !is.na(ci_change) && ci_change > 1.5) {
return("strong_recovery")
}
if (age_weeks >= 4 && age_weeks < 39 && !is.na(ci_change) && ci_change > 0.2) {
return("growth_on_track")
}
if (age_weeks >= 39 && age_weeks < 45 && mean_ci > 3.5) {
return("maturation_progressing")
}
return(NA_character_)
}
#' Extract planting dates from harvesting data #' Extract planting dates from harvesting data
extract_planting_dates <- function(harvesting_data, field_boundaries_sf = NULL) { extract_planting_dates <- function(harvesting_data, field_boundaries_sf = NULL) {

View file

@ -599,7 +599,17 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
if (!is.na(total_fields)) { if (!is.na(total_fields)) {
cat("\n\n", tr_key("tot_fields_analyzed")) cat("\n\n", tr_key("tot_fields_analyzed"))
} }
# Total area analyzed
if (exists("field_details_table") && !is.null(field_details_table)) {
area_col_name <- paste0("Area_", get_area_unit_label(AREA_UNIT_PREFERENCE))
unit_label <- get_area_unit_label(AREA_UNIT_PREFERENCE)
if (area_col_name %in% names(field_details_table)) {
total_area <- sum(field_details_table[[area_col_name]], na.rm = TRUE)
cat("\n\n", tr_key("tot_area_analyzed"))
}
}
} else { } else {
cat(tr_key("kpi_na")) cat(tr_key("kpi_na"))
} }
@ -618,15 +628,89 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
tryCatch({ tryCatch({
# KPI metadata for display # KPI metadata for display
kpi_display_order <- list( kpi_display_order <- list(
uniformity = list(display = "Field Uniformity", level_col = "interpretation", count_col = "field_count"), uniformity = list(display = "Field Uniformity", level_col = "interpretation", count_col = "field_count", area_col = "area_sum"),
area_change = list(display = "Area Change", level_col = "interpretation", count_col = "field_count"), area_change = list(display = "Area Change", level_col = "interpretation", count_col = "field_count", area_col = "area_sum"),
growth_decline = list(display = "4-Week Trend", level_col = "trend_interpretation", count_col = "field_count"), growth_decline = list(display = "4-Week Trend", level_col = "trend_interpretation", count_col = "field_count", area_col = "area_sum"),
patchiness = list(display = "Field Patchiness", level_col = "gini_category", count_col = "field_count", detail_col = "patchiness_risk"), patchiness = list(display = "Field Patchiness", level_col = "gini_category", count_col = "field_count", detail_col = "patchiness_risk", area_col = "area_sum"),
tch_forecast = list(display = "TCH Forecasted", level_col = "tch_category", detail_col = "range", count_col = "field_count"), tch_forecast = list(display = "TCH Forecasted", level_col = "tch_category", count_col = "field_count", detail_col = "range", area_col = "area_sum"),
gap_filling = list(display = "Gaps", level_col = "gap_level", count_col = "field_count") gap_filling = list(display = "Gaps", level_col = "gap_level", count_col = "field_count", area_col = "area_sum")
) )
standardize_kpi <- function(df, level_col, count_col, detail_col = NULL) { # Enrich summary_tables with area_sum from field_details_table (mirrors script 91 pattern)
area_col_name <- paste0("Area_", get_area_unit_label(AREA_UNIT_PREFERENCE))
unit_label <- get_area_unit_label(AREA_UNIT_PREFERENCE)
has_area <- exists("field_details_table") && !is.null(field_details_table) &&
area_col_name %in% names(field_details_table)
if (has_area) {
fdt <- field_details_table
if (!is.null(summary_tables$uniformity) && "Uniformity_Category" %in% names(fdt)) {
summary_tables$uniformity <- summary_tables$uniformity %>%
left_join(fdt %>% group_by(interpretation = Uniformity_Category) %>%
summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop"),
by = "interpretation")
}
if (!is.null(summary_tables$area_change) && "Area_Change_Interpretation" %in% names(fdt)) {
summary_tables$area_change <- summary_tables$area_change %>%
left_join(fdt %>% group_by(interpretation = Area_Change_Interpretation) %>%
summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop"),
by = "interpretation")
}
if (!is.null(summary_tables$growth_decline) && "Trend_Interpretation" %in% names(fdt)) {
summary_tables$growth_decline <- summary_tables$growth_decline %>%
left_join(fdt %>% group_by(trend_interpretation = Trend_Interpretation) %>%
summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop"),
by = "trend_interpretation")
}
if (!is.null(summary_tables$patchiness) && all(c("Patchiness_Risk", "Gini_Coefficient") %in% names(fdt))) {
summary_tables$patchiness <- summary_tables$patchiness %>%
left_join(
fdt %>%
mutate(gini_category = case_when(
Gini_Coefficient < 0.2 ~ "Uniform (Gini<0.2)",
Gini_Coefficient < 0.4 ~ "Moderate (Gini 0.2-0.4)",
TRUE ~ "High (Gini≥0.4)"
)) %>%
group_by(gini_category, patchiness_risk = Patchiness_Risk) %>%
summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop"),
by = c("gini_category", "patchiness_risk")
)
}
if (!is.null(summary_tables$gap_filling) && "Gap_Level" %in% names(fdt)) {
summary_tables$gap_filling <- summary_tables$gap_filling %>%
left_join(fdt %>% group_by(gap_level = Gap_Level) %>%
summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop"),
by = "gap_level")
}
# TCH forecast: reproduce the same quartile logic used when building summary_tables so we
# can assign each field to a tch_category and sum its area
if (!is.null(summary_tables$tch_forecast) && "TCH_Forecasted" %in% names(fdt)) {
tch_vals <- fdt %>% dplyr::filter(!is.na(TCH_Forecasted)) %>% dplyr::pull(TCH_Forecasted)
if (length(tch_vals) > 0) {
if (length(unique(tch_vals)) == 1) {
area_tch <- fdt %>%
dplyr::filter(!is.na(TCH_Forecasted)) %>%
dplyr::summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE)) %>%
dplyr::mutate(tch_category = "All equal")
} else {
q25 <- quantile(tch_vals, 0.25, na.rm = TRUE)
q75 <- quantile(tch_vals, 0.75, na.rm = TRUE)
area_tch <- fdt %>%
dplyr::filter(!is.na(TCH_Forecasted)) %>%
dplyr::mutate(tch_category = dplyr::case_when(
TCH_Forecasted >= q75 ~ "Top 25%",
TCH_Forecasted >= q25 ~ "Middle 50%",
TRUE ~ "Bottom 25%"
)) %>%
dplyr::group_by(tch_category) %>%
dplyr::summarise(area_sum = sum(.data[[area_col_name]], na.rm = TRUE), .groups = "drop")
}
summary_tables$tch_forecast <- summary_tables$tch_forecast %>%
left_join(area_tch, by = "tch_category")
}
}
}
standardize_kpi <- function(df, level_col, count_col, detail_col = NULL, area_col = NULL) {
if (is.null(level_col) || !(level_col %in% names(df)) || is.null(count_col) || !(count_col %in% names(df))) { if (is.null(level_col) || !(level_col %in% names(df)) || is.null(count_col) || !(count_col %in% names(df))) {
return(NULL) return(NULL)
} }
@ -640,10 +724,16 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
display_level <- df[[level_col]] display_level <- df[[level_col]]
} }
area_vals <- if (!is.null(area_col) && area_col %in% names(df))
round(df[[area_col]], 1)
else
rep(NA_real_, nrow(df))
df %>% df %>%
dplyr::transmute( dplyr::transmute(
Level = if (level_col == "trend_interpretation") map_trend_to_arrow(display_level, include_text = TRUE) else as.character(display_level), Level = if (level_col == "trend_interpretation") map_trend_to_arrow(display_level, include_text = TRUE) else as.character(display_level),
Count = as.integer(round(as.numeric(.data[[count_col]]))), Count = as.integer(round(as.numeric(.data[[count_col]]))),
Area = area_vals,
Percent = if (is.na(total)) { Percent = if (is.na(total)) {
NA_real_ NA_real_
} else { } else {
@ -661,9 +751,10 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
kpi_df <- summary_tables[[kpi_key]] kpi_df <- summary_tables[[kpi_key]]
if (is.null(kpi_df) || !is.data.frame(kpi_df) || nrow(kpi_df) == 0) next if (is.null(kpi_df) || !is.data.frame(kpi_df) || nrow(kpi_df) == 0) next
# Pass detail_col if it exists in config # Pass detail_col and area_col if present in config
detail_col <- if (!is.null(config$detail_col)) config$detail_col else NULL detail_col <- if (!is.null(config$detail_col)) config$detail_col else NULL
kpi_rows <- standardize_kpi(kpi_df, config$level_col, config$count_col, detail_col) kpi_rows <- standardize_kpi(kpi_df, config$level_col, config$count_col, detail_col,
config[["area_col"]])
if (!is.null(kpi_rows)) { if (!is.null(kpi_rows)) {
kpi_rows$KPI <- config$display kpi_rows$KPI <- config$display
@ -685,10 +776,14 @@ if (exists("summary_tables") && !is.null(summary_tables) && length(summary_table
kpi_group_sizes <- rle(combined_df$KPI_group)$lengths kpi_group_sizes <- rle(combined_df$KPI_group)$lengths
display_df <- combined_df %>% display_df <- combined_df %>%
dplyr::select(KPI = KPI_display, Level, Count, Percent) dplyr::select(KPI = KPI_display, Level, Count, Area, Percent)
# Translate the table for visualization # Translate the table for visualization
names(display_df) <- c(tr_key("KPI"), tr_key("Level"), tr_key("Count"), tr_key("Percent")) names(display_df) <- c(
tr_key("KPI"), tr_key("Level"), tr_key("Count"),
paste0(tr_key("Area"), " (", unit_label, ")"),
tr_key("Percent")
)
display_df[, 1:2] <- lapply(display_df[, 1:2], function(col) sapply(col, tr_key)) display_df[, 1:2] <- lapply(display_df[, 1:2], function(col) sapply(col, tr_key))
ft <- flextable(display_df) %>% ft <- flextable(display_df) %>%
merge_v(j = tr_key("KPI")) %>% merge_v(j = tr_key("KPI")) %>%
@ -1188,9 +1283,11 @@ tryCatch({
minus_2_ww <- get_week_year(as.Date(today) - lubridate::weeks(2)) minus_2_ww <- get_week_year(as.Date(today) - lubridate::weeks(2))
minus_3_ww <- get_week_year(as.Date(today) - lubridate::weeks(3)) minus_3_ww <- get_week_year(as.Date(today) - lubridate::weeks(3))
message(paste("Processing", nrow(AllPivots_merged), "fields for weeks:",
message(paste("Processing", nrow(AllPivots_merged), "fields for weeks:", message(paste("Processing", nrow(AllPivots_merged), "fields for weeks:",
current_ww$week, minus_1_ww$week, minus_2_ww$week, minus_3_ww$week)) current_ww$week, minus_1_ww$week, minus_2_ww$week, minus_3_ww$week))
# load_per_field_mosaic() is defined in 90_report_utils.R (sourced above) # load_per_field_mosaic() is defined in 90_report_utils.R (sourced above)
# --- Fetch rain for all fields (one API call per 0.25° tile) --- # --- Fetch rain for all fields (one API call per 0.25° tile) ---
@ -1333,6 +1430,14 @@ tryCatch({
sprintf("**%s:** %.2f", tr_key("cv_value"), field_kpi$CV), sprintf("**%s:** %.2f", tr_key("cv_value"), field_kpi$CV),
sprintf("**%s:** %.2f", tr_key("mean_ci"), field_kpi$Mean_CI) sprintf("**%s:** %.2f", tr_key("mean_ci"), field_kpi$Mean_CI)
) )
# Prepend area as first item (static field attribute)
if (area_col_name %in% names(field_kpi) && !is.na(field_kpi[[area_col_name]])) {
kpi_parts <- c(
sprintf("**%s:** %.1f %s", tr_key("Area"), field_kpi[[area_col_name]], unit_label),
kpi_parts
)
}
# Add Weekly_CI_Change if available (note: capital C and I) # 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)) { if (!is.null(field_kpi$Weekly_CI_Change) && !is.na(field_kpi$Weekly_CI_Change)) {

View file

@ -544,9 +544,11 @@ if (exists("summary_data") && !is.null(summary_data) && "field_analysis" %in% na
tryCatch({ tryCatch({
# Use per-field field_analysis data from RDS (already loaded in load_kpi_data chunk) # Use per-field field_analysis data from RDS (already loaded in load_kpi_data chunk)
if (exists("summary_data") && !is.null(summary_data) && "field_analysis" %in% names(summary_data)) { if (exists("summary_data") && !is.null(summary_data) && "field_analysis" %in% names(summary_data)) {
analysis_data <- summary_data$field_analysis %>% fa <- summary_data$field_analysis
select(Field_id, Status_Alert) %>% status_col <- intersect(c("Status_Alert", "Status_trigger"), names(fa))[1]
rename(Status_trigger = Status_Alert) # Rename to Status_trigger for compatibility with hexbin logic analysis_data <- fa %>%
select(Field_id, all_of(status_col)) %>%
rename(Status_trigger = all_of(status_col))
} else { } else {
analysis_data <- tibble(Field_id = character(), Status_trigger = character()) analysis_data <- tibble(Field_id = character(), Status_trigger = character())
} }
@ -619,10 +621,9 @@ tryCatch({
filter(Status_trigger != "harvest_ready" | is.na(Status_trigger)) filter(Status_trigger != "harvest_ready" | is.na(Status_trigger))
# Generate breaks for color gradients # Generate breaks for color gradients
breaks_vec <- c(0, 5, 10, 15, 20, 30, 35) breaks_vec <- c(5, 10, 15, 20, 30, 35)
labels_vec <- as.character(breaks_vec) labels_vec <- as.character(breaks_vec)
labels_vec[length(labels_vec)] <- ">30" labels_vec[length(labels_vec)] <- ">30"
labels_vec[1] <- "0.1"
# Calculate data bounds for coordinate limits (prevents basemap scale conflicts) # Calculate data bounds for coordinate limits (prevents basemap scale conflicts)
# Use actual data bounds without dummy points to avoid column mismatch # Use actual data bounds without dummy points to avoid column mismatch
@ -635,36 +636,45 @@ tryCatch({
ceiling(max(points_processed$Y, na.rm = TRUE) * 20) / 20 ceiling(max(points_processed$Y, na.rm = TRUE) * 20) / 20
) )
# ggplot2's StatBinhex computes the hex grid origin as c(min(x), min(y)) of each layer's
# own data. Feeding different subsets (points_ready / points_not_ready) to the two layers
# gives different origins → misaligned hexes. Fix: pass points_processed to BOTH layers.
# For the coloured layer, use ready_area = area_value for harvest-ready fields, 0 elsewhere.
# The scale censors 0 → NA → transparent, so non-ready hexes are invisible in that layer.
points_processed <- points_processed %>%
mutate(ready_area = ifelse(Status_trigger == "harvest_ready", area_value, 0))
# Create hexbin map with enhanced aesthetics, basemap, and proper legend # Create hexbin map with enhanced aesthetics, basemap, and proper legend
ggplot() + ggplot() +
# OpenStreetMap basemap (zoom=11 appropriate for agricultural fields) # OpenStreetMap basemap (zoom=11 appropriate for agricultural fields)
ggspatial::annotation_map_tile(type = "osm", zoom = 11, progress = "none", alpha = 0.5) + ggspatial::annotation_map_tile(type = "osm", zoom = 11, progress = "none", alpha = 0.5) +
# Hexbin for NOT ready fields (light background) # Hexbin background: ALL fields as white hexes (anchors the hex grid to the full extent)
geom_hex( geom_hex(
data = points_not_ready, data = points_processed,
aes(x = X, y = Y, weight = area_value, alpha = tr_key("hexbin_not_ready")), aes(x = X, y = Y, weight = area_value, alpha = tr_key("hexbin_not_ready")),
binwidth = c(0.012, 0.012), binwidth = c(0.012, 0.012),
fill = "#ffffff", fill = "#ffffff",
colour = "#0000009a", colour = "#0000009a",
linewidth = 0.1 linewidth = 0.1
) + ) +
# Hexbin for READY fields (colored gradient) # Hexbin overlay: same grid (same data), coloured only where ready_area > 0
geom_hex( geom_hex(
data = points_ready, data = points_processed,
aes(x = X, y = Y, weight = area_value), aes(x = X, y = Y, weight = ready_area),
binwidth = c(0.012, 0.012), binwidth = c(0.012, 0.012),
alpha = 0.9, alpha = 0.9,
colour = "#0000009a", colour = "#0000009a",
linewidth = 0.1 linewidth = 0.1
) + ) +
# Color gradient scale for area # Color gradient scale — values of 0 (non-ready hexes) are censored → NA → transparent
scale_fill_viridis_b( scale_fill_viridis_b(
option = "viridis", option = "viridis",
direction = -1, direction = -1,
breaks = breaks_vec, breaks = breaks_vec,
labels = labels_vec, labels = labels_vec,
limits = c(0, 35), limits = c(0.001, 35),
oob = scales::squish, oob = scales::oob_censor,
na.value = "transparent",
name = tr_key("hexbin_legend_acres") name = tr_key("hexbin_legend_acres")
) + ) +
# Alpha scale for "not ready" status indication # Alpha scale for "not ready" status indication
@ -722,7 +732,10 @@ if (exists("summary_data") && !is.null(summary_data) && "field_analysis" %in% na
if (is.null(summary_data$field_analysis_summary) || !("field_analysis_summary" %in% names(summary_data)) || if (is.null(summary_data$field_analysis_summary) || !("field_analysis_summary" %in% names(summary_data)) ||
!is.data.frame(summary_data$field_analysis_summary)) { !is.data.frame(summary_data$field_analysis_summary)) {
# Create summary by aggregating by Status_Alert and Phase categories # Detect which status column is present (Status_Alert from cane_supply, Status_trigger from others)
status_col <- intersect(c("Status_Alert", "Status_trigger"), names(field_analysis_df))[1]
# Create summary by aggregating by status and Phase categories
phase_summary <- field_analysis_df %>% phase_summary <- field_analysis_df %>%
filter(!is.na(Phase)) %>% filter(!is.na(Phase)) %>%
group_by(Phase) %>% group_by(Phase) %>%
@ -736,21 +749,21 @@ if (exists("summary_data") && !is.null(summary_data) && "field_analysis" %in% na
# Create Status trigger summary - includes both active alerts and "No active triggers" # Create Status trigger summary - includes both active alerts and "No active triggers"
trigger_summary <- tryCatch({ trigger_summary <- tryCatch({
# Active alerts (fields with non-NA Status_Alert) # Active alerts (fields with non-NA status)
active_alerts <- field_analysis_df %>% active_alerts <- field_analysis_df %>%
filter(!is.na(Status_Alert), Status_Alert != "") %>% filter(!is.na(.data[[status_col]]), .data[[status_col]] != "") %>%
group_by(Status_Alert) %>% group_by(across(all_of(status_col))) %>%
summarise( summarise(
Acreage = sum(Acreage, na.rm = TRUE), Acreage = sum(Acreage, na.rm = TRUE),
Field_count = n_distinct(Field_id), Field_count = n_distinct(Field_id),
.groups = "drop" .groups = "drop"
) %>% ) %>%
mutate(Category = Status_Alert) %>% mutate(Category = .data[[status_col]]) %>%
select(Category, Acreage, Field_count) select(Category, Acreage, Field_count)
# No active triggers (fields with NA Status_Alert) # No active triggers (fields with NA status)
no_alerts <- field_analysis_df %>% no_alerts <- field_analysis_df %>%
filter(is.na(Status_Alert) | Status_Alert == "") %>% filter(is.na(.data[[status_col]]) | .data[[status_col]] == "") %>%
summarise( summarise(
Acreage = sum(Acreage, na.rm = TRUE), Acreage = sum(Acreage, na.rm = TRUE),
Field_count = n_distinct(Field_id), Field_count = n_distinct(Field_id),

View file

@ -456,8 +456,8 @@ rmarkdown::render(
# rmarkdown::render( # rmarkdown::render(
rmarkdown::render( rmarkdown::render(
"r_app/91_CI_report_with_kpis_cane_supply.Rmd", "r_app/91_CI_report_with_kpis_cane_supply.Rmd",
params = list(data_dir = "angata", report_date = as.Date("2026-02-23")), params = list(data_dir = "angata", report_date = as.Date("2026-03-17")),
output_file = "SmartCane_Report_cane_supply_angata_2026-02-23_en.docx", output_file = "SmartCane_Report_cane_supply_angata_2026-03-17_en.docx",
output_dir = "laravel_app/storage/app/angata/reports" output_dir = "laravel_app/storage/app/angata/reports"
) )
# #

View file

@ -512,6 +512,16 @@
"es-mx": "**Total de parcelas analizadas:** {total_fields}", "es-mx": "**Total de parcelas analizadas:** {total_fields}",
"pt-br": "**Total de campos analisados:** {total_fields}" "pt-br": "**Total de campos analisados:** {total_fields}"
}, },
"tot_area_analyzed": {
"en": "**Total Area Analyzed:** {round(total_area, 1)} {unit_label}",
"es-mx": "**Área total analizada:** {round(total_area, 1)} {unit_label}",
"pt-br": "**Área total analisada:** {round(total_area, 1)} {unit_label}"
},
"Area": {
"en": "Area",
"es-mx": "Área",
"pt-br": "Área"
},
"Medium": { "Medium": {
"en": "Medium", "en": "Medium",
"es-mx": "Medio", "es-mx": "Medio",

View file

@ -2,541 +2,541 @@
"main_translations": { "main_translations": {
"cover_title": { "cover_title": {
"en": "Satellite Based Field Reporting", "en": "Satellite Based Field Reporting",
"es-mx": "", "es-mx": "Reportes de Campo Basados en Satélite",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"cover_subtitle": { "cover_subtitle": {
"en": "Cane Supply Office\n\nChlorophyll Index (CI) Monitoring Report — {toupper(params$data_dir)} Estate (Week {if (!is.null(params$week)) params$week else format(as.Date(params$report_date), '%V')}, {format(as.Date(params$report_date), '%Y')})", "en": "Cane Supply Office\n\nChlorophyll Index (CI) Monitoring Report — {toupper(params$data_dir)} Estate (Week {if (!is.null(params$week)) params$week else format(as.Date(params$report_date), '%V')}, {format(as.Date(params$report_date), '%Y')})",
"es-mx": "", "es-mx": "Oficina de Abastecimiento de Caña\n\nReporte de Monitoreo del Índice de Clorofila (IC) — Finca {toupper(params$data_dir)} (Semana {if (!is.null(params$week)) params$week else format(as.Date(params$report_date), '%V')}, {format(as.Date(params$report_date), '%Y')})",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_summary_heading": { "report_summary_heading": {
"en": "## Report Generated", "en": "## Report Generated",
"es-mx": "", "es-mx": "## Reporte Generado",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_farm_location": { "report_farm_location": {
"en": "**Farm Location:**", "en": "**Farm Location:**",
"es-mx": "", "es-mx": "**Ubicación de la Finca:**",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_period_label": { "report_period_label": {
"en": "**Report Period:** Week {current_week} of {year}", "en": "**Report Period:** Week {current_week} of {year}",
"es-mx": "", "es-mx": "**Período del Reporte:** Semana {current_week} de {year}",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_generated_on": { "report_generated_on": {
"en": "**Report Generated on:**", "en": "**Report Generated on:**",
"es-mx": "", "es-mx": "**Reporte Generado el:**",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_farm_size": { "report_farm_size": {
"en": "**Farm Size Included in Analysis:** {formatC(total_acreage, format='f', digits=1)} acres", "en": "**Farm Size Included in Analysis:** {formatC(total_acreage, format='f', digits=1)} acres",
"es-mx": "", "es-mx": "**Tamaño de Finca Incluido en el Análisis:** {formatC(total_acreage, format='f', digits=1)} acres",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_data_source": { "report_data_source": {
"en": "**Data Source:** Planet Labs Satellite Imagery", "en": "**Data Source:** Planet Labs Satellite Imagery",
"es-mx": "", "es-mx": "**Fuente de Datos:** Imágenes Satelitales de Planet Labs",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_analysis_type": { "report_analysis_type": {
"en": "**Analysis Type:** Chlorophyll Index (CI) Monitoring", "en": "**Analysis Type:** Chlorophyll Index (CI) Monitoring",
"es-mx": "", "es-mx": "**Tipo de Análisis:** Monitoreo del Índice de Clorofila (IC)",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"key_insights": { "key_insights": {
"en": "## Key Insights", "en": "## Key Insights",
"es-mx": "", "es-mx": "## Hallazgos Clave",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"insight_excellent_unif": { "insight_excellent_unif": {
"en": "- {excellent_pct}% of fields have excellent uniformity (CV < 0.08)", "en": "- {excellent_pct}% of fields have excellent uniformity (CV < 0.08)",
"es-mx": "", "es-mx": "- {excellent_pct}% de los campos tienen uniformidad excelente (CV < 0.08)",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"insight_good_unif": { "insight_good_unif": {
"en": "- {good_pct}% of fields have good uniformity (CV < 0.15)", "en": "- {good_pct}% of fields have good uniformity (CV < 0.15)",
"es-mx": "", "es-mx": "- {good_pct}% de los campos tienen buena uniformidad (CV < 0.15)",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"insight_improving": { "insight_improving": {
"en": "- {round(improving_acreage, 1)} acres ({improving_pct}%) of farm area is improving week-over-week", "en": "- {round(improving_acreage, 1)} acres ({improving_pct}%) of farm area is improving week-over-week",
"es-mx": "", "es-mx": "- {round(improving_acreage, 1)} acres ({improving_pct}%) del área de la finca está mejorando semana a semana",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"insight_declining": { "insight_declining": {
"en": "- {round(declining_acreage, 1)} acres ({declining_pct}%) of farm area is declining week-over-week", "en": "- {round(declining_acreage, 1)} acres ({declining_pct}%) of farm area is declining week-over-week",
"es-mx": "", "es-mx": "- {round(declining_acreage, 1)} acres ({declining_pct}%) del área de la finca está en declive semana a semana",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_na_insights": { "kpi_na_insights": {
"en": "KPI data not available for key insights.", "en": "KPI data not available for key insights.",
"es-mx": "", "es-mx": "Datos de KPI no disponibles para hallazgos clave.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_structure_heading": { "report_structure_heading": {
"en": "## Report Structure", "en": "## Report Structure",
"es-mx": "", "es-mx": "## Estructura del Reporte",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_structure_body": { "report_structure_body": {
"en": "**Section 1:** Cane supply zone analyses, summaries and Key Performance Indicators (KPIs)\n**Section 2:** Explanation of the report, definitions, methodology, and CSV export structure", "en": "**Section 1:** Cane supply zone analyses, summaries and Key Performance Indicators (KPIs)\n**Section 2:** Explanation of the report, definitions, methodology, and CSV export structure",
"es-mx": "", "es-mx": "**Sección 1:** Análisis de zonas de abastecimiento de caña, resúmenes e Indicadores Clave de Desempeño (KPIs)\n**Sección 2:** Explicación del reporte, definiciones, metodología y estructura de exportación CSV",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"section_i": { "section_i": {
"en": "# Section 1: Farm-wide Analyses and KPIs", "en": "# Section 1: Farm-wide Analyses and KPIs",
"es-mx": "", "es-mx": "# Sección 1: Análisis General de la Finca y KPIs",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"section_1_1": { "section_1_1": {
"en": "## 1.1 Overview of cane supply area, showing zones with number of acres being harvest ready", "en": "## 1.1 Overview of cane supply area, showing zones with number of acres being harvest ready",
"es-mx": "", "es-mx": "## 1.1 Panorama del área de abastecimiento de caña, mostrando zonas con número de acres listos para cosecha",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"section_1_2": { "section_1_2": {
"en": "## 1.2 Key Performance Indicators", "en": "## 1.2 Key Performance Indicators",
"es-mx": "", "es-mx": "## 1.2 Indicadores Clave de Desempeño",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_empty": { "kpi_empty": {
"en": "KPI summary data available but is empty/invalid.", "en": "KPI summary data available but is empty/invalid.",
"es-mx": "", "es-mx": "Datos de resumen de KPI disponibles pero vacíos/inválidos.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_unavailable": { "kpi_unavailable": {
"en": "KPI summary data not available.", "en": "KPI summary data not available.",
"es-mx": "", "es-mx": "Datos de resumen de KPI no disponibles.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"metadata": { "metadata": {
"en": "## Report Metadata", "en": "## Report Metadata",
"es-mx": "", "es-mx": "## Metadatos del Reporte",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"disclaimer": { "disclaimer": {
"en": "*This report was automatically generated by the SmartCane monitoring system. For questions or additional analysis, please contact the technical team at info@smartcane.ag.*", "en": "*This report was automatically generated by the SmartCane monitoring system. For questions or additional analysis, please contact the technical team at info@smartcane.ag.*",
"es-mx": "", "es-mx": "*Este reporte fue generado automáticamente por el sistema de monitoreo SmartCane. Para preguntas o análisis adicionales, por favor contacte al equipo técnico en info@smartcane.ag.*",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"section_2_heading": { "section_2_heading": {
"en": "# Section 2: Support Document for weekly SmartCane data package.", "en": "# Section 2: Support Document for weekly SmartCane data package.",
"es-mx": "", "es-mx": "# Sección 2: Documento de Soporte para el paquete de datos semanal de SmartCane.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_doc_heading": { "about_doc_heading": {
"en": "## 1. About This Document", "en": "## 1. About This Document",
"es-mx": "", "es-mx": "## 1. Acerca de Este Documento",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_doc_body": { "about_doc_body": {
"en": "This document is the support document to the SmartCane data file. It includes the definitions, explanatory calculations and suggestions for interpretations of the data as provided. For additional questions please feel free to contact SmartCane support, through your contact person, or via info@smartcane.ag.", "en": "This document is the support document to the SmartCane data file. It includes the definitions, explanatory calculations and suggestions for interpretations of the data as provided. For additional questions please feel free to contact SmartCane support, through your contact person, or via info@smartcane.ag.",
"es-mx": "", "es-mx": "Este documento es el documento de soporte del archivo de datos SmartCane. Incluye las definiciones, cálculos explicativos y sugerencias para la interpretación de los datos proporcionados. Para preguntas adicionales, no dude en contactar al soporte de SmartCane, a través de su persona de contacto o vía info@smartcane.ag.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_heading": { "about_data_heading": {
"en": "## 2. About the Data File", "en": "## 2. About the Data File",
"es-mx": "", "es-mx": "## 2. Acerca del Archivo de Datos",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_body": { "about_data_body": {
"en": "The data file is automatically populated based on normalized and indexed remote sensing images of provided polygons. Specific SmartCane algorithms provide tailored calculation results developed to support the sugarcane operations by:", "en": "The data file is automatically populated based on normalized and indexed remote sensing images of provided polygons. Specific SmartCane algorithms provide tailored calculation results developed to support the sugarcane operations by:",
"es-mx": "", "es-mx": "El archivo de datos se genera automáticamente a partir de imágenes de teledetección normalizadas e indexadas de los polígonos proporcionados. Los algoritmos específicos de SmartCane proporcionan resultados de cálculo personalizados desarrollados para apoyar las operaciones de caña de azúcar mediante:",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_bullet_harvest": { "about_data_bullet_harvest": {
"en": "Supporting harvest planning mill-field logistics to ensure optimal tonnage and sucrose levels", "en": "Supporting harvest planning mill-field logistics to ensure optimal tonnage and sucrose levels",
"es-mx": "", "es-mx": "Apoyo a la planificación de cosecha y logística ingenio-campo para asegurar niveles óptimos de tonelaje y sacarosa",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_bullet_monitoring": { "about_data_bullet_monitoring": {
"en": "Monitoring of the crop growth rates on the farm, providing evidence of performance", "en": "Monitoring of the crop growth rates on the farm, providing evidence of performance",
"es-mx": "", "es-mx": "Monitoreo de las tasas de crecimiento del cultivo en la finca, proporcionando evidencia de rendimiento",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_bullet_identifying": { "about_data_bullet_identifying": {
"en": "Identifying growth-related issues that are in need of attention", "en": "Identifying growth-related issues that are in need of attention",
"es-mx": "", "es-mx": "Identificación de problemas relacionados con el crecimiento que requieren atención",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_bullet_enabling": { "about_data_bullet_enabling": {
"en": "Enabling timely actions to minimize negative impact", "en": "Enabling timely actions to minimize negative impact",
"es-mx": "", "es-mx": "Habilitación de acciones oportunas para minimizar el impacto negativo",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"about_data_key_features": { "about_data_key_features": {
"en": "Key Features of the data file: - High-resolution satellite imagery analysis - Week-over-week change detection - Individual field performance metrics - Actionable insights for crop management.", "en": "Key Features of the data file: - High-resolution satellite imagery analysis - Week-over-week change detection - Individual field performance metrics - Actionable insights for crop management.",
"es-mx": "", "es-mx": "Características principales del archivo de datos: - Análisis de imágenes satelitales de alta resolución - Detección de cambios semana a semana - Métricas de rendimiento por campo individual - Información accionable para el manejo del cultivo.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_section_heading": { "ci_section_heading": {
"en": "#### *What is the Chlorophyll Index (CI)?*", "en": "#### *What is the Chlorophyll Index (CI)?*",
"es-mx": "", "es-mx": "#### *¿Qué es el Índice de Clorofila (IC)?*",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_intro_body": { "ci_intro_body": {
"en": "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:", "en": "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:",
"es-mx": "", "es-mx": "El Índice de Clorofila (IC) es un índice de vegetación que mide la cantidad relativa de clorofila en las hojas de las plantas. La clorofila es el pigmento verde responsable de la fotosíntesis en las plantas. Valores más altos de IC indican:",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_bullet_photosynthetic": { "ci_bullet_photosynthetic": {
"en": "Greater photosynthetic activity", "en": "Greater photosynthetic activity",
"es-mx": "", "es-mx": "Mayor actividad fotosintética",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_bullet_healthy": { "ci_bullet_healthy": {
"en": "Healthier plant tissue", "en": "Healthier plant tissue",
"es-mx": "", "es-mx": "Tejido vegetal más saludable",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_bullet_nitrogen": { "ci_bullet_nitrogen": {
"en": "Better nitrogen uptake", "en": "Better nitrogen uptake",
"es-mx": "", "es-mx": "Mejor absorción de nitrógeno",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_bullet_vigorous": { "ci_bullet_vigorous": {
"en": "More vigorous crop growth", "en": "More vigorous crop growth",
"es-mx": "", "es-mx": "Crecimiento más vigoroso del cultivo",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"ci_range_body": { "ci_range_body": {
"en": "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.", "en": "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.",
"es-mx": "", "es-mx": "Los valores de IC generalmente varían de 0 (suelo descubierto o vegetación severamente estresada) a 7+ (vegetación muy saludable y densa). Para la caña de azúcar, valores entre 3-7 generalmente indican buena salud del cultivo, dependiendo de la etapa de crecimiento.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"data_structure_heading": { "data_structure_heading": {
"en": "### Data File Structure and Columns", "en": "### Data File Structure and Columns",
"es-mx": "", "es-mx": "### Estructura del Archivo de Datos y Columnas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"data_structure_intro": { "data_structure_intro": {
"en": "The data file is organized in rows, one row per agricultural field (polygon), and columns, providing field data, actual measurements, calculation results and descriptions. The data file can be directly integrated with existing farm management systems for further analysis. Each column is described hereunder:", "en": "The data file is organized in rows, one row per agricultural field (polygon), and columns, providing field data, actual measurements, calculation results and descriptions. The data file can be directly integrated with existing farm management systems for further analysis. Each column is described hereunder:",
"es-mx": "", "es-mx": "El archivo de datos está organizado en filas, una fila por campo agrícola (polígono), y columnas, proporcionando datos del campo, mediciones reales, resultados de cálculos y descripciones. El archivo de datos puede integrarse directamente con los sistemas de gestión agrícola existentes para análisis adicional. Cada columna se describe a continuación:",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_field_id_desc": { "col_field_id_desc": {
"en": "Unique identifier for a cane field combining field name and sub-field number. This can be the same as Field_Name but is also helpful in keeping track of cane fields should they change, split or merge.", "en": "Unique identifier for a cane field combining field name and sub-field number. This can be the same as Field_Name but is also helpful in keeping track of cane fields should they change, split or merge.",
"es-mx": "", "es-mx": "Identificador único para un campo de caña que combina el nombre del campo y el número de subcampo. Puede ser igual que Field_Name pero también es útil para dar seguimiento a los campos de caña en caso de cambios, divisiones o fusiones.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_farm_section_desc": { "col_farm_section_desc": {
"en": "Sub-area or section name", "en": "Sub-area or section name",
"es-mx": "", "es-mx": "Nombre de subárea o sección",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_field_name_desc": { "col_field_name_desc": {
"en": "Client name or label assigned to a cane field.", "en": "Client name or label assigned to a cane field.",
"es-mx": "", "es-mx": "Nombre o etiqueta del cliente asignado a un campo de caña.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_acreage_desc": { "col_acreage_desc": {
"en": "Field size in acres", "en": "Field size in acres",
"es-mx": "", "es-mx": "Tamaño del campo en acres",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_status_trigger_desc": { "col_status_trigger_desc": {
"en": "Shows changes in crop status worth alerting. More detailed explanation of the possible alerts is written down under key concepts.", "en": "Shows changes in crop status worth alerting. More detailed explanation of the possible alerts is written down under key concepts.",
"es-mx": "", "es-mx": "Muestra cambios en el estado del cultivo que ameritan alerta. Una explicación más detallada de las alertas posibles se encuentra en conceptos clave.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_last_harvest_desc": { "col_last_harvest_desc": {
"en": "Date of most recent harvest as per satellite detection algorithm / or manual entry", "en": "Date of most recent harvest as per satellite detection algorithm / or manual entry",
"es-mx": "", "es-mx": "Fecha de la cosecha más reciente según el algoritmo de detección satelital / o ingreso manual",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_age_week_desc": { "col_age_week_desc": {
"en": "Time elapsed since planting/harvest in weeks; used to predict expected growth phases. Reflects planting/harvest date.", "en": "Time elapsed since planting/harvest in weeks; used to predict expected growth phases. Reflects planting/harvest date.",
"es-mx": "", "es-mx": "Tiempo transcurrido desde la siembra/cosecha en semanas; utilizado para predecir fases de crecimiento esperadas. Refleja la fecha de siembra/cosecha.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_phase_desc": { "col_phase_desc": {
"en": "Current growth phase (e.g., germination, tillering, stem elongation, grain fill, mature) inferred from crop age", "en": "Current growth phase (e.g., germination, tillering, stem elongation, grain fill, mature) inferred from crop age",
"es-mx": "", "es-mx": "Fase de crecimiento actual (ej., germinación, macollamiento, elongación del tallo, llenado de grano, madurez) inferida de la edad del cultivo",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_germination_progress_desc": { "col_germination_progress_desc": {
"en": "Estimated percentage or stage of germination/emergence based on CI patterns and age. This goes for young fields (age < 4 months). Remains at 100% when finished.", "en": "Estimated percentage or stage of germination/emergence based on CI patterns and age. This goes for young fields (age < 4 months). Remains at 100% when finished.",
"es-mx": "", "es-mx": "Porcentaje estimado o etapa de germinación/emergencia basado en patrones de IC y edad. Aplica para campos jóvenes (edad < 4 meses). Permanece en 100% al completarse.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_mean_ci_desc": { "col_mean_ci_desc": {
"en": "Average Chlorophyll Index value across the field; higher values indicate healthier, greener vegetation. Calculated on a 7-day merged weekly image.", "en": "Average Chlorophyll Index value across the field; higher values indicate healthier, greener vegetation. Calculated on a 7-day merged weekly image.",
"es-mx": "", "es-mx": "Valor promedio del Índice de Clorofila en todo el campo; valores más altos indican vegetación más saludable y verde. Calculado sobre una imagen semanal fusionada de 7 días.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_weekly_ci_change_desc": { "col_weekly_ci_change_desc": {
"en": "Week-over-week change in Mean_CI; positive values indicate greening/growth, negative values indicate yellowing/decline", "en": "Week-over-week change in Mean_CI; positive values indicate greening/growth, negative values indicate yellowing/decline",
"es-mx": "", "es-mx": "Cambio semanal en Mean_CI; valores positivos indican reverdecimiento/crecimiento, valores negativos indican amarillamiento/declive",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_four_week_trend_desc": { "col_four_week_trend_desc": {
"en": "Long term change in mean CI; smoothed trend (strong growth, growth, no growth, decline, strong decline)", "en": "Long term change in mean CI; smoothed trend (strong growth, growth, no growth, decline, strong decline)",
"es-mx": "", "es-mx": "Cambio a largo plazo en el IC promedio; tendencia suavizada (crecimiento fuerte, crecimiento, sin crecimiento, declive, declive fuerte)",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_ci_range_desc": { "col_ci_range_desc": {
"en": "Min-max Chlorophyll Index values within the field; wide ranges indicate spatial heterogeneity/patches. Derived from week mosaic.", "en": "Min-max Chlorophyll Index values within the field; wide ranges indicate spatial heterogeneity/patches. Derived from week mosaic.",
"es-mx": "", "es-mx": "Valores mínimo-máximo del Índice de Clorofila dentro del campo; rangos amplios indican heterogeneidad espacial/parches. Derivado del mosaico semanal.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_ci_percentiles_desc": { "col_ci_percentiles_desc": {
"en": "The CI-range without border effects", "en": "The CI-range without border effects",
"es-mx": "", "es-mx": "El rango de IC sin efectos de borde",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_cv_desc": { "col_cv_desc": {
"en": "Coefficient of variation of CI; measures field uniformity (lower = more uniform, >0.25 = poor uniformity). Derived from week mosaic. In percentages.", "en": "Coefficient of variation of CI; measures field uniformity (lower = more uniform, >0.25 = poor uniformity). Derived from week mosaic. In percentages.",
"es-mx": "", "es-mx": "Coeficiente de variación del IC; mide la uniformidad del campo (menor = más uniforme, >0.25 = uniformidad deficiente). Derivado del mosaico semanal. En porcentajes.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_cv_trend_short_desc": { "col_cv_trend_short_desc": {
"en": "Trend of CV over two weeks. Indicating short-term heterogeneity.", "en": "Trend of CV over two weeks. Indicating short-term heterogeneity.",
"es-mx": "", "es-mx": "Tendencia del CV en dos semanas. Indica heterogeneidad a corto plazo.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_cv_trend_long_desc": { "col_cv_trend_long_desc": {
"en": "Slope of 8-week trend line.", "en": "Slope of 8-week trend line.",
"es-mx": "", "es-mx": "Pendiente de la línea de tendencia de 8 semanas.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_imminent_prob_desc": { "col_imminent_prob_desc": {
"en": "Probability (0-1) that the field is ready for harvest based on LSTM harvest model predictions", "en": "Probability (0-1) that the field is ready for harvest based on LSTM harvest model predictions",
"es-mx": "", "es-mx": "Probabilidad (0-1) de que el campo esté listo para cosecha basado en predicciones del modelo de cosecha LSTM",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_cloud_pct_desc": { "col_cloud_pct_desc": {
"en": "Percentage of field visible in the satellite image (unobstructed by clouds); lower values indicate poor data quality", "en": "Percentage of field visible in the satellite image (unobstructed by clouds); lower values indicate poor data quality",
"es-mx": "", "es-mx": "Porcentaje del campo visible en la imagen satelital (sin obstrucción de nubes); valores más bajos indican calidad de datos deficiente",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"col_cloud_category_desc": { "col_cloud_category_desc": {
"en": "Classification of cloud cover level (e.g., clear, partial, heavy); indicates confidence in CI measurements", "en": "Classification of cloud cover level (e.g., clear, partial, heavy); indicates confidence in CI measurements",
"es-mx": "", "es-mx": "Clasificación del nivel de cobertura de nubes (ej., despejado, parcial, denso); indica confianza en las mediciones de IC",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"key_concepts_heading": { "key_concepts_heading": {
"en": "# 3. Key Concepts", "en": "# 3. Key Concepts",
"es-mx": "", "es-mx": "# 3. Conceptos Clave",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"growth_phases_heading": { "growth_phases_heading": {
"en": "#### *Growth Phases (Age-Based)*", "en": "#### *Growth Phases (Age-Based)*",
"es-mx": "", "es-mx": "#### *Fases de Crecimiento (Basadas en Edad)*",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"growth_phases_intro": { "growth_phases_intro": {
"en": "Each field is assigned to one of four growth phases based on age in weeks since planting:", "en": "Each field is assigned to one of four growth phases based on age in weeks since planting:",
"es-mx": "", "es-mx": "Cada campo se asigna a una de cuatro fases de crecimiento según la edad en semanas desde la siembra:",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"germination_age_range": { "germination_age_range": {
"en": "0-6 weeks", "en": "0-6 weeks",
"es-mx": "", "es-mx": "0-6 semanas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"tillering_age_range": { "tillering_age_range": {
"en": "4-16 weeks", "en": "4-16 weeks",
"es-mx": "", "es-mx": "4-16 semanas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"grand_growth_age_range": { "grand_growth_age_range": {
"en": "17-39 weeks", "en": "17-39 weeks",
"es-mx": "", "es-mx": "17-39 semanas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"maturation_age_range": { "maturation_age_range": {
"en": "39+ weeks", "en": "39+ weeks",
"es-mx": "", "es-mx": "39+ semanas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"germination_characteristics": { "germination_characteristics": {
"en": "Crop emergence and early establishment; high variability expected", "en": "Crop emergence and early establishment; high variability expected",
"es-mx": "", "es-mx": "Emergencia del cultivo y establecimiento temprano; alta variabilidad esperada",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"tillering_characteristics": { "tillering_characteristics": {
"en": "Shoot multiplication and plant establishment; rapid growth phase", "en": "Shoot multiplication and plant establishment; rapid growth phase",
"es-mx": "", "es-mx": "Multiplicación de brotes y establecimiento de la planta; fase de crecimiento rápido",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"grand_growth_characteristics": { "grand_growth_characteristics": {
"en": "Peak vegetative growth; maximum height and biomass accumulation", "en": "Peak vegetative growth; maximum height and biomass accumulation",
"es-mx": "", "es-mx": "Pico de crecimiento vegetativo; máxima altura y acumulación de biomasa",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"maturation_characteristics": { "maturation_characteristics": {
"en": "Ripening phase; sugar accumulation and preparation for harvest", "en": "Ripening phase; sugar accumulation and preparation for harvest",
"es-mx": "", "es-mx": "Fase de maduración; acumulación de azúcar y preparación para la cosecha",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"status_alert_heading": { "status_alert_heading": {
"en": "#### *Status Alert*", "en": "#### *Status Alert*",
"es-mx": "", "es-mx": "#### *Alerta de Estado*",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"status_alert_intro": { "status_alert_intro": {
"en": "Status alerts indicate the current field condition based on CI and age-related patterns. Each field receives **one alert** reflecting its most relevant status:", "en": "Status alerts indicate the current field condition based on CI and age-related patterns. Each field receives **one alert** reflecting its most relevant status:",
"es-mx": "", "es-mx": "Las alertas de estado indican la condición actual del campo basada en patrones de IC y edad. Cada campo recibe **una alerta** que refleja su estado más relevante:",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_ready_condition": { "harvest_ready_condition": {
"en": "Harvest model > 0.50 and crop is mature", "en": "Harvest model > 0.50 and crop is mature",
"es-mx": "", "es-mx": "Modelo de cosecha > 0.50 y el cultivo está maduro",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_ready_phase_info": { "harvest_ready_phase_info": {
"en": "Active from 52 weeks onwards", "en": "Active from 52 weeks onwards",
"es-mx": "", "es-mx": "Activo a partir de las 52 semanas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_ready_msg": { "harvest_ready_msg": {
"en": "Ready for harvest-check", "en": "Ready for harvest-check",
"es-mx": "", "es-mx": "Listo para verificación de cosecha",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvested_bare_condition": { "harvested_bare_condition": {
"en": "Field of 50 weeks or older either shows mean CI values lower than 1.5 (for a maximum of three weeks) OR drops from higher CI to lower than 1.5. Alert drops if CI rises and passes 1.5 again", "en": "Field of 50 weeks or older either shows mean CI values lower than 1.5 (for a maximum of three weeks) OR drops from higher CI to lower than 1.5. Alert drops if CI rises and passes 1.5 again",
"es-mx": "", "es-mx": "Campo de 50 semanas o más que muestra valores promedio de IC menores a 1.5 (por un máximo de tres semanas) O que cae de un IC mayor a menor de 1.5. La alerta se desactiva si el IC sube y supera 1.5 nuevamente",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvested_bare_phase_info": { "harvested_bare_phase_info": {
"en": "Maturation (39+)", "en": "Maturation (39+)",
"es-mx": "", "es-mx": "Maduración (39+)",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvested_bare_msg": { "harvested_bare_msg": {
"en": "Harvested or bare field", "en": "Harvested or bare field",
"es-mx": "", "es-mx": "Campo cosechado o descubierto",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"stress_condition": { "stress_condition": {
"en": "Mean CI on field drops by 2+ points but field mean CI remains higher than 1.5", "en": "Mean CI on field drops by 2+ points but field mean CI remains higher than 1.5",
"es-mx": "", "es-mx": "El IC promedio del campo cae 2+ puntos pero el IC promedio del campo permanece mayor a 1.5",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"stress_phase_info": { "stress_phase_info": {
"en": "Any", "en": "Any",
"es-mx": "", "es-mx": "Cualquiera",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"stress_msg": { "stress_msg": {
"en": "Strong decline in crop health", "en": "Strong decline in crop health",
"es-mx": "", "es-mx": "Declive fuerte en la salud del cultivo",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_date_heading": { "harvest_date_heading": {
"en": "#### *Harvest Date and Harvest Imminent*", "en": "#### *Harvest Date and Harvest Imminent*",
"es-mx": "", "es-mx": "#### *Fecha de Cosecha y Cosecha Inminente*",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_date_body_1": { "harvest_date_body_1": {
"en": "The SmartCane algorithm calculates the last harvest date and the probability of harvest approaching in the next 4 weeks. Two different algorithms are used.", "en": "The SmartCane algorithm calculates the last harvest date and the probability of harvest approaching in the next 4 weeks. Two different algorithms are used.",
"es-mx": "", "es-mx": "El algoritmo de SmartCane calcula la última fecha de cosecha y la probabilidad de que la cosecha se aproxime en las próximas 4 semanas. Se utilizan dos algoritmos diferentes.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_date_body_2": { "harvest_date_body_2": {
"en": "The **last harvest date** is a timeseries analysis of the CI levels of the past years, based on clean factory managed fields as data set for the machine learning, a reliability of over 90% has been reached. Smallholder managed fields of small size (0.3 acres) have specific side effects and field management characteristics, that influence the model results.", "en": "The **last harvest date** is a timeseries analysis of the CI levels of the past years, based on clean factory managed fields as data set for the machine learning, a reliability of over 90% has been reached. Smallholder managed fields of small size (0.3 acres) have specific side effects and field management characteristics, that influence the model results.",
"es-mx": "", "es-mx": "La **última fecha de cosecha** es un análisis de series de tiempo de los niveles de IC de años anteriores, basado en campos limpios administrados por la fábrica como conjunto de datos para el aprendizaje automático, se ha alcanzado una confiabilidad superior al 90%. Los campos pequeños administrados por pequeños productores (0.3 acres) tienen efectos secundarios específicos y características de manejo que influyen en los resultados del modelo.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_date_body_3": { "harvest_date_body_3": {
"en": "**Imminent_probability** of harvest is a prediction algorithm, estimating the likelihood of a crop ready to be harvested in the near future. This prediction takes the CI-levels into consideration, building on the vegetative development of sugarcane in the last stage of Maturation, where all sucrose is pulled into the stalk, depleting the leaves from energy and productive function, reducing the levels of CI in the leaf tissue.", "en": "**Imminent_probability** of harvest is a prediction algorithm, estimating the likelihood of a crop ready to be harvested in the near future. This prediction takes the CI-levels into consideration, building on the vegetative development of sugarcane in the last stage of Maturation, where all sucrose is pulled into the stalk, depleting the leaves from energy and productive function, reducing the levels of CI in the leaf tissue.",
"es-mx": "", "es-mx": "**Imminent_probability** de cosecha es un algoritmo de predicción que estima la probabilidad de que un cultivo esté listo para ser cosechado en el futuro cercano. Esta predicción toma en cuenta los niveles de IC, basándose en el desarrollo vegetativo de la caña de azúcar en la última etapa de Maduración, donde toda la sacarosa se concentra en el tallo, agotando la energía y función productiva de las hojas, reduciendo los niveles de IC en el tejido foliar.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_date_body_4": { "harvest_date_body_4": {
"en": "Both algorithms are not always in sync, and can have contradictory results. Wider field characteristics analysis is suggested if such contradictory calculation results occur.", "en": "Both algorithms are not always in sync, and can have contradictory results. Wider field characteristics analysis is suggested if such contradictory calculation results occur.",
"es-mx": "", "es-mx": "Ambos algoritmos no siempre están sincronizados y pueden tener resultados contradictorios. Se sugiere un análisis más amplio de las características del campo si se presentan resultados de cálculo contradictorios.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
} }
@ -544,127 +544,127 @@
"status_translations": { "status_translations": {
"PHASE DISTRIBUTION": { "PHASE DISTRIBUTION": {
"en": "Phase Distribution", "en": "Phase Distribution",
"es-mx": "", "es-mx": "Distribución de Fases",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"OPERATIONAL ALERTS": { "OPERATIONAL ALERTS": {
"en": "Operational Alerts", "en": "Operational Alerts",
"es-mx": "", "es-mx": "Alertas Operativas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"AREA CHANGE": { "AREA CHANGE": {
"en": "Area Change", "en": "Area Change",
"es-mx": "", "es-mx": "Cambio de Área",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"CLOUD INFLUENCE": { "CLOUD INFLUENCE": {
"en": "Cloud Influence", "en": "Cloud Influence",
"es-mx": "", "es-mx": "Influencia de Nubes",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"TOTAL FARM": { "TOTAL FARM": {
"en": "Total Farm", "en": "Total Farm",
"es-mx": "", "es-mx": "Total de la Finca",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Germination": { "Germination": {
"en": "Germination", "en": "Germination",
"es-mx": "", "es-mx": "Germinación",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Tillering": { "Tillering": {
"en": "Tillering", "en": "Tillering",
"es-mx": "", "es-mx": "Macollamiento",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Grand Growth": { "Grand Growth": {
"en": "Grand Growth", "en": "Grand Growth",
"es-mx": "", "es-mx": "Gran Crecimiento",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Maturation": { "Maturation": {
"en": "Maturation", "en": "Maturation",
"es-mx": "", "es-mx": "Maduración",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Unknown Phase": { "Unknown Phase": {
"en": "Unknown Phase", "en": "Unknown Phase",
"es-mx": "", "es-mx": "Fase Desconocida",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvest_ready": { "harvest_ready": {
"en": "Ready for Harvest-Check", "en": "Ready for Harvest-Check",
"es-mx": "", "es-mx": "Listo para Verificación de Cosecha",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"harvested_bare": { "harvested_bare": {
"en": "Harvested / Bare Field", "en": "Harvested / Bare Field",
"es-mx": "", "es-mx": "Cosechado / Campo Descubierto",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"stress_detected": { "stress_detected": {
"en": "Stress Detected", "en": "Stress Detected",
"es-mx": "", "es-mx": "Estrés Detectado",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"germination_delayed": { "germination_delayed": {
"en": "Germination Delayed", "en": "Germination Delayed",
"es-mx": "", "es-mx": "Germinación Retrasada",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"growth_on_track": { "growth_on_track": {
"en": "Growth on Track", "en": "Growth on Track",
"es-mx": "", "es-mx": "Crecimiento en Curso",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"No active triggers": { "No active triggers": {
"en": "No Active Triggers", "en": "No Active Triggers",
"es-mx": "", "es-mx": "Sin Alertas Activas",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Improving": { "Improving": {
"en": "Improving", "en": "Improving",
"es-mx": "", "es-mx": "Mejorando",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Stable": { "Stable": {
"en": "Stable", "en": "Stable",
"es-mx": "", "es-mx": "Estable",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Declining": { "Declining": {
"en": "Declining", "en": "Declining",
"es-mx": "", "es-mx": "En Declive",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"Total Acreage": { "Total Acreage": {
"en": "Total Acreage", "en": "Total Acreage",
"es-mx": "", "es-mx": "Superficie Total",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_na": { "kpi_na": {
"en": "KPI data not available for {project_dir} on this date.", "en": "KPI data not available for {project_dir} on this date.",
"es-mx": "", "es-mx": "Datos de KPI no disponibles para {project_dir} en esta fecha.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
} }
@ -672,159 +672,159 @@
"figure_translations": { "figure_translations": {
"kpi_col_category": { "kpi_col_category": {
"en": "KPI Category", "en": "KPI Category",
"es-mx": "", "es-mx": "Categoría de KPI",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_col_item": { "kpi_col_item": {
"en": "Item", "en": "Item",
"es-mx": "", "es-mx": "Elemento",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_col_acreage": { "kpi_col_acreage": {
"en": "Acreage", "en": "Acreage",
"es-mx": "", "es-mx": "Superficie",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_col_percent": { "kpi_col_percent": {
"en": "Percentage of Total Fields", "en": "Percentage of Total Fields",
"es-mx": "", "es-mx": "Porcentaje del Total de Campos",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"kpi_col_fields": { "kpi_col_fields": {
"en": "# Fields", "en": "# Fields",
"es-mx": "", "es-mx": "# Campos",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"hexbin_legend_acres": { "hexbin_legend_acres": {
"en": "Total Acres", "en": "Total Acres",
"es-mx": "", "es-mx": "Acres Totales",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"hexbin_subtitle": { "hexbin_subtitle": {
"en": "Acres of fields 'harvest ready within a month'", "en": "Acres of fields 'harvest ready within a month'",
"es-mx": "", "es-mx": "Acres de campos listos para cosecha dentro de un mes",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"hexbin_not_ready": { "hexbin_not_ready": {
"en": "Not harvest ready", "en": "Not harvest ready",
"es-mx": "", "es-mx": "No listo para cosecha",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"metadata_caption": { "metadata_caption": {
"en": "Report Metadata", "en": "Report Metadata",
"es-mx": "", "es-mx": "Metadatos del Reporte",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"metric": { "metric": {
"en": "Metric", "en": "Metric",
"es-mx": "", "es-mx": "Métrica",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"value": { "value": {
"en": "Value", "en": "Value",
"es-mx": "", "es-mx": "Valor",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"report_gen": { "report_gen": {
"en": "Report Generated", "en": "Report Generated",
"es-mx": "", "es-mx": "Reporte Generado",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"data_source": { "data_source": {
"en": "Data Source", "en": "Data Source",
"es-mx": "", "es-mx": "Fuente de Datos",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"analysis_period": { "analysis_period": {
"en": "Analysis Period", "en": "Analysis Period",
"es-mx": "", "es-mx": "Período de Análisis",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"tot_fields_number": { "tot_fields_number": {
"en": "Total Fields [number]", "en": "Total Fields [number]",
"es-mx": "", "es-mx": "Total de Campos [número]",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"tot_area_acres": { "tot_area_acres": {
"en": "Total Area [acres]", "en": "Total Area [acres]",
"es-mx": "", "es-mx": "Área Total [acres]",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"next_update": { "next_update": {
"en": "Next Update", "en": "Next Update",
"es-mx": "", "es-mx": "Próxima Actualización",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"service_provided": { "service_provided": {
"en": "Service provided", "en": "Service provided",
"es-mx": "", "es-mx": "Servicio proporcionado",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"service_start": { "service_start": {
"en": "Starting date service", "en": "Starting date service",
"es-mx": "", "es-mx": "Fecha de inicio del servicio",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"next_wed": { "next_wed": {
"en": "Next Wednesday", "en": "Next Wednesday",
"es-mx": "", "es-mx": "Próximo miércoles",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"week": { "week": {
"en": "Week", "en": "Week",
"es-mx": "", "es-mx": "Semana",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"of": { "of": {
"en": "of", "en": "of",
"es-mx": "", "es-mx": "de",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"project": { "project": {
"en": "Project", "en": "Project",
"es-mx": "", "es-mx": "Proyecto",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"cane_supply_service": { "cane_supply_service": {
"en": "Cane Supply Office - Weekly", "en": "Cane Supply Office - Weekly",
"es-mx": "", "es-mx": "Oficina de Abastecimiento de Caña - Semanal",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"unknown": { "unknown": {
"en": "Unknown", "en": "Unknown",
"es-mx": "", "es-mx": "Desconocido",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
}, },
"no_field_data": { "no_field_data": {
"en": "No field-level KPI data available for this report period.", "en": "No field-level KPI data available for this report period.",
"es-mx": "", "es-mx": "No hay datos de KPI a nivel de campo disponibles para este período de reporte.",
"pt-br": "", "pt-br": "",
"sw": "" "sw": ""
} }
} }
} }