272 lines
9.1 KiB
R
272 lines
9.1 KiB
R
#' Combined: Create master grid and split TIFFs into tiles
|
||
#' ====================================================================
|
||
#'
|
||
#' Purpose:
|
||
#' 1. Check all daily TIFFs for matching extents
|
||
#' 2. Create master 5×5 grid covering all TIFFs
|
||
#' 3. Split each daily TIFF into 25 tiles using the master grid
|
||
#' 4. Save tiles in date-specific folders: daily_tiles/[DATE]/[DATE]_[TILE_ID].tif
|
||
|
||
library(terra)
|
||
library(sf)
|
||
|
||
# ============================================================================
|
||
# CONFIGURATION
|
||
# ============================================================================
|
||
|
||
PROJECT <- "angata"
|
||
TIFF_FOLDER <- file.path("laravel_app", "storage", "app", PROJECT, "merged_tif_8b")
|
||
OUTPUT_FOLDER <- file.path("laravel_app", "storage", "app", PROJECT, "daily_tiles_split")
|
||
|
||
# Grid dimensions will be auto-determined based on ROI size
|
||
# Default: 5×5 = 25 tiles
|
||
# If ROI < 10×10 km: 1×1 = 1 tile (no splitting needed)
|
||
GRID_NROWS <- 5
|
||
GRID_NCOLS <- 5
|
||
|
||
cat("Combined: Create Master Grid (5x5) and Split TIFFs into Tiles\n")
|
||
|
||
# ============================================================================
|
||
# PART 1: CHECK TIFF EXTENTS AND CREATE MASTER GRID
|
||
# ============================================================================
|
||
|
||
cat("\n[PART 1] Creating Master Grid\n")
|
||
|
||
cat("\n[1] Checking TIFF extents...\n")
|
||
|
||
tiff_files <- list.files(TIFF_FOLDER, pattern = "\\.tif$", full.names = FALSE)
|
||
tiff_files <- sort(tiff_files)
|
||
|
||
if (length(tiff_files) == 0) {
|
||
stop("No TIFF files found in ", TIFF_FOLDER)
|
||
}
|
||
|
||
cat(" Found", length(tiff_files), "TIFF file(s)\n")
|
||
|
||
# Load all extents
|
||
extents <- list()
|
||
for (i in seq_along(tiff_files)) {
|
||
tiff_path <- file.path(TIFF_FOLDER, tiff_files[i])
|
||
raster <- terra::rast(tiff_path)
|
||
ext <- terra::ext(raster)
|
||
extents[[i]] <- ext
|
||
}
|
||
|
||
# Check if all extents match
|
||
cat("\n[2] Comparing extents...\n")
|
||
|
||
tolerance <- 1e-8
|
||
all_match <- TRUE
|
||
first_ext <- extents[[1]]
|
||
|
||
for (i in 2:length(extents)) {
|
||
curr_ext <- extents[[i]]
|
||
match <- (
|
||
abs(curr_ext$xmin - first_ext$xmin) < tolerance &&
|
||
abs(curr_ext$xmax - first_ext$xmax) < tolerance &&
|
||
abs(curr_ext$ymin - first_ext$ymin) < tolerance &&
|
||
abs(curr_ext$ymax - first_ext$ymax) < tolerance
|
||
)
|
||
if (!match) {
|
||
all_match <- FALSE
|
||
cat(" ✗ Extent mismatch: ", tiff_files[1], " vs ", tiff_files[i], "\n", sep = "")
|
||
cat(" File 1: X [", round(first_ext$xmin, 6), ", ", round(first_ext$xmax, 6), "] ",
|
||
"Y [", round(first_ext$ymin, 6), ", ", round(first_ext$ymax, 6), "]\n", sep = "")
|
||
cat(" File ", i, ": X [", round(curr_ext$xmin, 6), ", ", round(curr_ext$xmax, 6), "] ",
|
||
"Y [", round(curr_ext$ymin, 6), ", ", round(curr_ext$ymax, 6), "]\n", sep = "")
|
||
}
|
||
}
|
||
|
||
if (all_match) {
|
||
cat(" ✓ All TIFF extents MATCH perfectly!\n")
|
||
} else {
|
||
cat(" ⚠️ Extents differ - creating master extent covering all\n")
|
||
}
|
||
|
||
# Create master extent
|
||
cat("\n[3] Creating master extent...\n")
|
||
|
||
master_xmin <- min(sapply(extents, function(e) e$xmin))
|
||
master_xmax <- max(sapply(extents, function(e) e$xmax))
|
||
master_ymin <- min(sapply(extents, function(e) e$ymin))
|
||
master_ymax <- max(sapply(extents, function(e) e$ymax))
|
||
|
||
x_range_m <- (master_xmax - master_xmin) * 111320
|
||
y_range_m <- (master_ymax - master_ymin) * 111320
|
||
|
||
cat(" Master extent: X [", round(master_xmin, 6), ", ", round(master_xmax, 6), "] ",
|
||
"Y [", round(master_ymin, 6), ", ", round(master_ymax, 6), "]\n", sep = "")
|
||
cat(" Coverage: ", round(x_range_m / 1000, 1), "km × ", round(y_range_m / 1000, 1), "km\n", sep = "")
|
||
|
||
# Auto-determine grid size based on ROI dimensions
|
||
if (x_range_m < 10000 && y_range_m < 10000) {
|
||
cat("\n ⚠️ ROI is small (< 10×10 km). Using single tile (1×1 grid) - no splitting needed!\n")
|
||
GRID_NROWS <- 1
|
||
GRID_NCOLS <- 1
|
||
} else {
|
||
cat("\n ROI size allows tiling. Using 5×5 grid (25 tiles per date).\n")
|
||
GRID_NROWS <- 5
|
||
GRID_NCOLS <- 5
|
||
}
|
||
|
||
N_TILES <- GRID_NROWS * GRID_NCOLS
|
||
|
||
# Check if master grid already exists
|
||
cat("\n[4] Checking if master grid exists...\n")
|
||
|
||
master_grid_file <- file.path(OUTPUT_FOLDER, "master_grid_5x5.geojson")
|
||
|
||
if (file.exists(master_grid_file)) {
|
||
cat(" ✓ Master grid exists! Loading existing grid...\n")
|
||
master_grid_sf <- st_read(master_grid_file, quiet = TRUE)
|
||
master_grid_vect <- terra::vect(master_grid_file)
|
||
cat(" ✓ Loaded grid with ", nrow(master_grid_sf), " tiles\n", sep = "")
|
||
} else {
|
||
cat(" Grid does not exist. Creating new master grid...\n")
|
||
|
||
# Create 5×5 grid
|
||
cat("\n[5] Creating ", GRID_NCOLS, "×", GRID_NROWS, " master grid...\n", sep = "")
|
||
|
||
master_bbox <- st_bbox(c(
|
||
xmin = master_xmin,
|
||
xmax = master_xmax,
|
||
ymin = master_ymin,
|
||
ymax = master_ymax
|
||
), crs = 4326)
|
||
|
||
bbox_sf <- st_as_sfc(master_bbox)
|
||
|
||
master_grid <- st_make_grid(
|
||
bbox_sf,
|
||
n = c(GRID_NCOLS, GRID_NROWS),
|
||
what = "polygons"
|
||
)
|
||
|
||
master_grid_sf <- st_sf(
|
||
tile_id = sprintf("%02d", 1:length(master_grid)),
|
||
geometry = master_grid
|
||
)
|
||
|
||
cat(" ✓ Created grid with ", length(master_grid), " cells\n", sep = "")
|
||
|
||
# Convert to SpatVector for use in makeTiles
|
||
master_grid_vect <- terra::vect(master_grid_sf)
|
||
|
||
# Save master grid
|
||
if (!dir.exists(OUTPUT_FOLDER)) {
|
||
dir.create(OUTPUT_FOLDER, recursive = TRUE, showWarnings = FALSE)
|
||
}
|
||
st_write(master_grid_sf, master_grid_file, delete_dsn = TRUE, quiet = TRUE)
|
||
cat(" ✓ Master grid saved to: master_grid_5x5.geojson\n")
|
||
}
|
||
|
||
# ============================================================================
|
||
# PART 2: SPLIT EACH TIFF INTO TILES
|
||
# ============================================================================
|
||
|
||
cat("\n[PART 2] Splitting TIFFs into Tiles\n")
|
||
|
||
cat("\n[6] Splitting TIFFs using master grid...\n")
|
||
|
||
total_tiles_created <- 0
|
||
|
||
for (file_idx in seq_along(tiff_files)) {
|
||
tiff_file <- tiff_files[file_idx]
|
||
date_str <- gsub("\\.tif$", "", tiff_file)
|
||
|
||
cat("\n Processing: ", tiff_file, "\n", sep = "")
|
||
|
||
# Load TIFF
|
||
tiff_path <- file.path(TIFF_FOLDER, tiff_file)
|
||
raster <- terra::rast(tiff_path)
|
||
|
||
dims <- dim(raster)
|
||
cat(" Dimensions: ", dims[2], "×", dims[1], " pixels\n", sep = "")
|
||
|
||
# Create date-specific output folder
|
||
date_output_folder <- file.path(OUTPUT_FOLDER, date_str)
|
||
if (!dir.exists(date_output_folder)) {
|
||
dir.create(date_output_folder, recursive = TRUE, showWarnings = FALSE)
|
||
}
|
||
|
||
cat(" Splitting into ", N_TILES, " tiles using master grid...\n", sep = "")
|
||
|
||
# Split using master grid zones
|
||
tiles_list <- terra::makeTiles(
|
||
x = raster,
|
||
y = master_grid_vect,
|
||
filename = file.path(date_output_folder, "tile.tif"),
|
||
overwrite = TRUE
|
||
)
|
||
|
||
cat(" ✓ Created ", length(tiles_list), " tiles\n", sep = "")
|
||
|
||
# Rename tiles to [DATE]_[TILE_ID].tif
|
||
cat(" Renaming tiles...\n")
|
||
|
||
for (tile_idx in seq_along(tiles_list)) {
|
||
source_file <- file.path(date_output_folder, paste0("tile", tile_idx, ".tif"))
|
||
tile_id <- sprintf("%02d", tile_idx)
|
||
final_file <- file.path(date_output_folder, paste0(date_str, "_", tile_id, ".tif"))
|
||
|
||
if (file.exists(source_file)) {
|
||
file.rename(source_file, final_file)
|
||
}
|
||
}
|
||
|
||
cat(" ✓ Saved ", N_TILES, " tiles in folder: ", date_str, "/\n", sep = "")
|
||
total_tiles_created <- total_tiles_created + length(tiles_list)
|
||
}
|
||
|
||
# ============================================================================
|
||
# VERIFICATION
|
||
# ============================================================================
|
||
|
||
cat("\n[7] Verifying output...\n")
|
||
|
||
# Count tiles per date folder
|
||
date_folders <- list.dirs(OUTPUT_FOLDER, full.names = FALSE, recursive = FALSE)
|
||
date_folders <- sort(date_folders[date_folders != "."])
|
||
|
||
total_tile_files <- 0
|
||
for (date_folder in date_folders) {
|
||
tiles_in_folder <- list.files(file.path(OUTPUT_FOLDER, date_folder),
|
||
pattern = "\\.tif$")
|
||
tiles_in_folder <- tiles_in_folder[!grepl("master_grid", tiles_in_folder)]
|
||
total_tile_files <- total_tile_files + length(tiles_in_folder)
|
||
cat(" ", date_folder, ": ", length(tiles_in_folder), " tiles\n", sep = "")
|
||
}
|
||
|
||
# ============================================================================
|
||
# SUMMARY
|
||
# ============================================================================
|
||
|
||
cat("SUMMARY\n")
|
||
|
||
cat("\nMaster Grid:\n")
|
||
cat(" - Dimensions: ", GRID_NCOLS, "×", GRID_NROWS, "=", N_TILES, " tiles\n", sep = "")
|
||
cat(" - File: master_grid_5x5.geojson\n")
|
||
|
||
cat("\nTIFF Splitting:\n")
|
||
cat(" - TIFFs processed: ", length(tiff_files), "\n", sep = "")
|
||
cat(" - Total tile files created: ", total_tile_files, "\n", sep = "")
|
||
cat(" - Expected total: ", length(tiff_files) * N_TILES, "\n", sep = "")
|
||
|
||
cat("\nDirectory Structure:\n")
|
||
cat(" daily_tiles/\n")
|
||
cat(" ├── master_grid_5x5.geojson\n")
|
||
for (date_folder in date_folders) {
|
||
cat(" ├── ", date_folder, "/\n", sep = "")
|
||
cat(" │ ├── ", date_folder, "_01.tif\n", sep = "")
|
||
cat(" │ ├── ", date_folder, "_02.tif\n", sep = "")
|
||
cat(" │ └── ...\n")
|
||
}
|
||
|
||
cat("\nKey Properties:\n")
|
||
cat(" - All tiles align perfectly across dates\n")
|
||
cat(" - Tile 01 covers same area in all dates\n")
|
||
cat(" - Date folders provide organizational hierarchy\n")
|
||
cat(" - Can do time-series analysis per tile\n")
|
||
|
||
cat("✓ Script complete!\n")
|