From b2de819fc49f248dc600bd0280c068ed70d2db70 Mon Sep 17 00:00:00 2001 From: Timon Date: Wed, 28 Jan 2026 11:19:07 +0100 Subject: [PATCH] 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 --- r_app/80_calculate_kpis.R | 76 +++++++++++++++++++++++++++++------ r_app/80_weekly_stats_utils.R | 15 ++++++- r_app/run_full_pipeline.R | 65 ++++++++++++++++++++++++------ 3 files changed, 129 insertions(+), 27 deletions(-) diff --git a/r_app/80_calculate_kpis.R b/r_app/80_calculate_kpis.R index ed2c330..40cfefb 100644 --- a/r_app/80_calculate_kpis.R +++ b/r_app/80_calculate_kpis.R @@ -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...") diff --git a/r_app/80_weekly_stats_utils.R b/r_app/80_weekly_stats_utils.R index a4b460e..b989292 100644 --- a/r_app/80_weekly_stats_utils.R +++ b/r_app/80_weekly_stats_utils.R @@ -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 diff --git a/r_app/run_full_pipeline.R b/r_app/run_full_pipeline.R index ae6ff14..0e1cc0a 100644 --- a/r_app/run_full_pipeline.R +++ b/r_app/run_full_pipeline.R @@ -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)))