Fix backward compatibility: Support non-tiled (single-file) mosaic projects

- Script 80: Add fallback logic to detect and handle single-file mosaics
- Weekly stats utils: Support both tile-based and single-file mosaic detection
- Pipeline runner: Auto-detect mosaic mode (tiled vs single-file)
- Flexible grid size detection for tile-based projects (5x5, 10x10, etc)

Fixes:
- Script 80 now checks weekly_tile_max/{grid_size} first, falls back to weekly_mosaic
- calculate_field_statistics handles both tile patterns and single-file patterns
- run_full_pipeline detects project mode automatically
- All verification checks are now flexible and don't assume fixed paths

Projects like 'aura' (small ROI < 10km) will use single-file approach automatically
Projects like 'angata' (large ROI >= 10km) will use tile-based approach automatically
This commit is contained in:
Timon 2026-01-28 11:19:07 +01:00
parent fde60cbbdf
commit b2de819fc4
3 changed files with 129 additions and 27 deletions

View file

@ -173,9 +173,6 @@ STATUS_TRIGGERS <- data.frame(
# MAIN
# ============================================================================
# ============================================================================
# MAIN
# ============================================================================
main <- function() {
# Parse command-line arguments
@ -261,27 +258,50 @@ main <- function() {
message(paste("Week:", current_week, "/ Year:", year))
# Find tile files - approach from Script 20
message("Finding tile files...")
# Find mosaic files - support both tile-based AND single-file approaches
message("Finding mosaic files...")
tile_pattern <- sprintf("week_%02d_%d_([0-9]{2})\\.tif", current_week, year)
single_file_pattern <- sprintf("week_%02d_%d\\.tif", current_week, year)
# Detect grid size subdirectory
# PRIORITY 1: Check for tile-based mosaics (projects with large ROI)
detected_grid_size <- NA
mosaic_dir <- NA
mosaic_mode <- NA
if (dir.exists(weekly_tile_max)) {
subfolders <- list.dirs(weekly_tile_max, full.names = FALSE, recursive = FALSE)
grid_patterns <- grep("^\\d+x\\d+$", subfolders, value = TRUE)
if (length(grid_patterns) > 0) {
detected_grid_size <- grid_patterns[1]
mosaic_dir <- file.path(weekly_tile_max, detected_grid_size)
message(paste(" Using grid-size subdirectory:", detected_grid_size))
tile_files <- list.files(mosaic_dir, pattern = tile_pattern, full.names = TRUE)
if (length(tile_files) > 0) {
message(paste(" ✓ Using tile-based approach (grid-size:", detected_grid_size, ")"))
message(paste(" Found", length(tile_files), "tiles"))
mosaic_mode <- "tiled"
}
}
}
tile_files <- list.files(mosaic_dir, pattern = tile_pattern, full.names = TRUE)
if (length(tile_files) == 0) {
stop(paste("No tile files found for week", current_week, year, "in", mosaic_dir))
# PRIORITY 2: Fall back to single-file mosaic (projects with small ROI, legacy approach)
if (is.na(mosaic_mode)) {
message(" No tiles found. Checking for single-file mosaic (legacy approach)...")
mosaic_dir <- weekly_mosaic
single_file <- list.files(mosaic_dir, pattern = single_file_pattern, full.names = TRUE)
if (length(single_file) > 0) {
message(paste(" ✓ Using single-file approach"))
message(paste(" Found 1 mosaic file:", basename(single_file[1])))
mosaic_mode <- "single-file"
} else {
stop(paste("ERROR: No mosaic files found for week", current_week, year,
"\n Checked (1) tile-based:", file.path(weekly_tile_max, "*", "week_*.tif"),
"\n Checked (2) single-file:", file.path(weekly_mosaic, "week_*.tif")))
}
}
message(paste(" Found", length(tile_files), "tiles"))
message(paste(" Using mosaic mode:", mosaic_mode))
# Load field boundaries
tryCatch({
@ -354,8 +374,38 @@ main <- function() {
# ============================================================================
# Build tile grid (needed by calculate_field_statistics)
message("\nBuilding tile grid for current week...")
tile_grid <- build_tile_grid(mosaic_dir, current_week, year)
message("\nPreparing mosaic configuration for statistics calculation...")
# For tile-based mosaics: build the grid mapping
# For single-file: create a minimal grid structure (single "tile" = entire mosaic)
if (mosaic_mode == "tiled") {
tile_grid <- build_tile_grid(mosaic_dir, current_week, year)
message(paste(" ✓ Built tile grid with", nrow(tile_grid), "tiles"))
} else {
# Single-file mode: create a minimal grid with just the single mosaic
message(" ✓ Using single-file mosaic (no tile grid needed)")
single_file_pattern <- sprintf("week_%02d_%d\\.tif", current_week, year)
single_file <- list.files(mosaic_dir, pattern = single_file_pattern, full.names = TRUE)
if (length(single_file) == 0) {
stop("ERROR: Single-file mosaic not found in", mosaic_dir)
}
# Create a minimal tile_grid structure with one "tile" representing the entire mosaic
tile_grid <- list(
mosaic_dir = mosaic_dir,
data = data.frame(
id = 0, # Single tile ID = 0 (full extent)
xmin = NA_real_,
xmax = NA_real_,
ymin = NA_real_,
ymax = NA_real_,
stringsAsFactors = FALSE
),
mode = "single-file",
file = single_file[1]
)
}
message("\nUsing modular RDS-based approach for weekly statistics...")

View file

@ -381,14 +381,25 @@ calculate_field_statistics <- function(field_boundaries_sf, week_num, year,
message(paste("Calculating statistics for all fields - Week", week_num, year))
# Support both tile-based and single-file mosaics
tile_pattern <- sprintf("week_%02d_%d_([0-9]{2})\\.tif", week_num, year)
single_file_pattern <- sprintf("week_%02d_%d\\.tif", week_num, year)
# Try tile-based first
tile_files <- list.files(mosaic_dir, pattern = tile_pattern, full.names = TRUE)
# If no tiles, try single-file
if (length(tile_files) == 0) {
stop(paste("No tile files found for week", week_num, year, "in", mosaic_dir))
single_file <- list.files(mosaic_dir, pattern = single_file_pattern, full.names = TRUE)
if (length(single_file) > 0) {
message(paste(" Using single-file mosaic for week", week_num))
tile_files <- single_file[1] # Use first match as single "tile"
} else {
stop(paste("No mosaic files found for week", week_num, year, "in", mosaic_dir))
}
}
message(paste(" Found", length(tile_files), "tiles for week", week_num))
message(paste(" Found", length(tile_files), "mosaic file(s) for week", week_num))
results_list <- list()
fields_processed <- 0

View file

@ -48,12 +48,44 @@ pipeline_success <- TRUE
# ==============================================================================
cat("\n========== CHECKING EXISTING OUTPUTS ==========\n")
# Check Script 10 outputs (tiled splits)
tiles_dir <- file.path("laravel_app", "storage", "app", project_dir, "daily_tiles_split", "5x5")
tiles_dates <- if (dir.exists(tiles_dir)) {
list.dirs(tiles_dir, full.names = FALSE, recursive = FALSE)
} else {
c()
# Detect mosaic mode (tile-based vs single-file) automatically
detect_mosaic_mode_simple <- function(project_dir) {
# Check for tile-based approach: weekly_tile_max/{grid_size}/week_*.tif
weekly_tile_max <- file.path("laravel_app", "storage", "app", project_dir, "weekly_tile_max")
if (dir.exists(weekly_tile_max)) {
subfolders <- list.dirs(weekly_tile_max, full.names = FALSE, recursive = FALSE)
grid_patterns <- grep("^\\d+x\\d+$", subfolders, value = TRUE)
if (length(grid_patterns) > 0) {
return("tiled")
}
}
# Check for single-file approach: weekly_mosaic/week_*.tif
weekly_mosaic <- file.path("laravel_app", "storage", "app", project_dir, "weekly_mosaic")
if (dir.exists(weekly_mosaic)) {
files <- list.files(weekly_mosaic, pattern = "^week_.*\\.tif$")
if (length(files) > 0) {
return("single-file")
}
}
return("unknown")
}
mosaic_mode <- detect_mosaic_mode_simple(project_dir)
cat(sprintf("Auto-detected mosaic mode: %s\n", mosaic_mode))
# Check Script 10 outputs - look for daily_tiles_split/{GRID_SIZE} (flexible grid detection)
tiles_split_base <- file.path("laravel_app", "storage", "app", project_dir, "daily_tiles_split")
tiles_dates <- c()
if (dir.exists(tiles_split_base)) {
# Look for any grid-size subdirectories (5x5, 10x10, etc.)
subfolders <- list.dirs(tiles_split_base, full.names = FALSE, recursive = FALSE)
grid_patterns <- grep("^\\d+x\\d+$", subfolders, value = TRUE)
if (length(grid_patterns) > 0) {
grid_dir <- file.path(tiles_split_base, grid_patterns[1])
tiles_dates <- list.dirs(grid_dir, full.names = FALSE, recursive = FALSE)
}
}
cat(sprintf("Script 10: %d dates already tiled\n", length(tiles_dates)))
@ -71,12 +103,21 @@ cat(sprintf("Script 20: %d CI daily RDS files exist\n", length(ci_files)))
# For now, just note that CSV is time-dependent, not a good skip indicator
cat("Script 21: CSV file exists but gets overwritten - will run if Script 20 runs\n")
# Check Script 40 outputs (mosaics in weekly_tile_max/5x5)
mosaic_dir <- file.path("laravel_app", "storage", "app", project_dir, "weekly_tile_max", "5x5")
mosaic_files <- if (dir.exists(mosaic_dir)) {
list.files(mosaic_dir, pattern = "\\.tif$")
} else {
c()
# Check Script 40 outputs (mosaics) - flexible detection for both tile-based and single-file
mosaic_files <- c()
if (mosaic_mode == "tiled") {
# For tile-based: look in weekly_tile_max/{grid_size}/
weekly_tile_max <- file.path("laravel_app", "storage", "app", project_dir, "weekly_tile_max")
subfolders <- list.dirs(weekly_tile_max, full.names = FALSE, recursive = FALSE)
grid_patterns <- grep("^\\d+x\\d+$", subfolders, value = TRUE)
if (length(grid_patterns) > 0) {
mosaic_dir <- file.path(weekly_tile_max, grid_patterns[1])
mosaic_files <- list.files(mosaic_dir, pattern = "\\.tif$")
}
} else if (mosaic_mode == "single-file") {
# For single-file: look in weekly_mosaic/
mosaic_dir <- file.path("laravel_app", "storage", "app", project_dir, "weekly_mosaic")
mosaic_files <- list.files(mosaic_dir, pattern = "^week_.*\\.tif$")
}
cat(sprintf("Script 40: %d mosaic files exist\n", length(mosaic_files)))