Code
%load_ext jupyter_black
%load_ext autoreload
%autoreload 2The CHIRPS GEFS (Climate Hazards Group InfraRed Precipitation with Station data - Global Ensemble Forecast System) module demonstrates efficient COG-based raster processing workflows for precipitation forecast data. This chapter focuses on the technical implementation of cloud-optimized spatial data processing, showing how to work with large raster datasets efficiently using two-stage spatial clipping.
This system demonstrates best practices for working with Cloud-Optimized GeoTIFFs:
The system uses five main classes that demonstrate different aspects of COG processing:
%load_ext jupyter_black
%load_ext autoreload
%autoreload 2import pandas as pd
import geopandas as gpd
import datetime
import xarray as xr
import rioxarray as rxr
from pathlib import Path
import ocha_stratus as stratus
# Import our CHIRPS GEFS module components
from src.datasources.chirps_gefs import (
ChirpsGefsManager,
ChirpsGefsConfig,
ChirpsGefsDownloader,
ChirpsGefsLoader,
ChirpsGefsProcessor,
)
from src.datasources import codab
from src.constants import *All imports successful!
We’ll use Cuba as our example geometry to demonstrate the spatial processing efficiency:
# Load Cuba administrative boundaries for demonstration
cuba_gdf = codab.load_codab_from_blob()
cuba_gdf = cuba_gdf[cuba_gdf["ADM0_PCODE"] == "CU"]
cuba_gdf = cuba_gdf[["ADM0_ES", "ADM0_PCODE", "geometry"]].copy()
print(f"Loaded Cuba geometry with {len(cuba_gdf)} features")
print(f"Total bounds: {cuba_gdf.total_bounds}")
cuba_gdf.head()Loaded Cuba geometry with 1 features
Total bounds: [-84.95236206 19.8259716 -74.13118744 23.27763939]
| ADM0_ES | ADM0_PCODE | geometry | |
|---|---|---|---|
| 0 | Cuba | CU | MULTIPOLYGON (((-83.06403 21.55069, -83.06403 ... |
Let’s start with the most practical example - downloading a small amount of data to test the system:
# Start small: 2 days, 5 leadtimes = 10 files total
config = ChirpsGefsConfig(
geometry=cuba_gdf,
region_name="cuba_test",
start_date="2024-12-10", # Single day to start
end_date="2024-12-11", # Just 2 days
leadtime_days=5, # First 5 leadtimes only
verbose=True
)
print(f"🌍 Region: {config.region_name}")
print(f"📅 Date range: {config.start_date} to {config.end_date}")
print(f"⏱️ Leadtime: {config.leadtime_days} days")
# Calculate total number of files: days × leadtimes
date_range = pd.date_range(config.start_date, config.end_date, freq='D')
total_files = len(date_range) * config.leadtime_days
print(f"📊 Total forecasts: {total_files} files (small test!)")🌍 Region: cuba_test
📅 Date range: 2024-12-10 to 2024-12-11
⏱️ Leadtime: 5 days
📊 Total forecasts: 10 files (small test!)
# Initialize downloader
downloader = ChirpsGefsDownloader(config)
# Download just our small test dataset
download_stats = downloader.download_date_range(
config.start_date,
config.end_date
)
print("Download Results:")
print(f"✅ Successful: {download_stats['success']}")
print(f"❌ Failed: {download_stats['failed']}")
print(f"📊 Total attempted: {download_stats['total']}")# Load and examine one of the downloaded rasters
loader = ChirpsGefsLoader(config)
# Option 1: Load a single specific forecast
da_single = loader.load_raster(
issue_date=pd.Timestamp("2024-12-10"),
valid_date=pd.Timestamp("2024-12-12"),
)
print("Single COG Properties:")
print(f"✅ Shape: {da_single.shape}")
print(f"📐 CRS: {da_single.rio.crs}")
print(f"📏 Resolution: {da_single.rio.resolution()}")
print(f"🗺️ Bounds: {da_single.rio.bounds()}")
print(f"💧 Data range: {da_single.min().values:.2f} to {da_single.max().values:.2f} mm/day")
print(f"📊 Data size: {da_single.nbytes / 1024:.1f} KB (compare to ~103 MB global!)")# Option 2: Load entire time series from your configuration
ds_timeseries = loader.load_raster_time_series()
print("\nTime Series Dataset Properties:")
print(f"✅ Dataset dimensions: {dict(ds_timeseries.dims)}")
print(f"📅 Issue dates: {len(ds_timeseries.issue_date)} dates")
print(f"📅 Valid dates: {len(ds_timeseries.valid_date)} dates")
print(f"⏱️ Leadtimes: {len(ds_timeseries.leadtime)} leadtimes")
if "precipitation" in ds_timeseries.data_vars:
precip = ds_timeseries.precipitation
print(
f"💧 Precipitation range: {precip.min().values:.2f} to {precip.max().values:.2f} mm/day"
)
print(f"📊 Total data size: {precip.nbytes / 1024:.1f} KB")
# Show some sample data structure
print(f"\nSample coordinate values:")
print(
f" Issue dates: {[str(d.date()) for d in ds_timeseries.issue_date.values[:3]]}"
)
print(
f" Valid dates: {[str(d.date()) for d in ds_timeseries.valid_date.values[:3]]}"
)
print(f" Leadtimes: {ds_timeseries.leadtime.values[:3]}")Note: If you get an AttributeError about load_raster_time_series not being found, restart your Jupyter kernel or reload the module:
import importlib
from src.datasources import chirps_gefs
importlib.reload(chirps_gefs)
from src.datasources.chirps_gefs import ChirpsGefsLoader
loader = ChirpsGefsLoader(config)# Working with the loaded time series dataset
ds = loader.load_raster_time_series()
# Example 1: Extract data for a specific issue date
issue_dec10 = ds.sel(issue_date="2024-12-10")
print(f"Dec 10 issue forecast shape: {issue_dec10.precipitation.shape}")
print(f"Leadtimes available: {issue_dec10.leadtime.values}")
# Example 2: Extract data for a specific valid date (across all issue dates)
valid_dec12 = ds.sel(valid_date="2024-12-12", method="nearest")
print(f"Dec 12 valid forecasts shape: {valid_dec12.precipitation.shape}")
print(
f"Issue dates that forecast Dec 12: {[str(d.date()) for d in valid_dec12.issue_date.values]}"
)
# Example 3: Calculate spatial mean for each forecast
spatial_means = ds.precipitation.mean(dim=["x", "y"])
print(f"Spatial means shape: {spatial_means.shape}")
print(f"Sample spatial means: {spatial_means.values.flatten()[:5]} mm/day")
# Example 4: Create a summary DataFrame
summary_data = []
for issue_dt in ds.issue_date.values:
for valid_dt in ds.valid_date.values:
for leadtime in ds.leadtime.values:
try:
precip_data = ds.precipitation.sel(
issue_date=issue_dt, valid_date=valid_dt, leadtime=leadtime
)
mean_precip = float(precip_data.mean().values)
summary_data.append(
{
"issue_date": pd.to_datetime(issue_dt).date(),
"valid_date": pd.to_datetime(valid_dt).date(),
"leadtime_days": int(leadtime),
"mean_precipitation_mm_day": mean_precip,
}
)
except (KeyError, ValueError):
# Skip if combination doesn't exist
continue
df_summary = pd.DataFrame(summary_data)
print(f"\nSummary DataFrame:")
print(f"Shape: {df_summary.shape}")
print(df_summary.head())Now that you’ve successfully downloaded your first COGs, let’s understand the technical details of what made this so efficient:
The key innovation in this workflow is the two-stage spatial processing that dramatically reduces data transfer and processing requirements:
The key innovation in this workflow is the two-stage spatial processing that dramatically reduces data transfer and processing requirements:
CHIRPS GEFS Spatial Processing Efficiency:
# Calculate Cuba's spatial footprint
cuba_bounds = cuba_gdf.total_bounds
print(f"Cuba bounding box: {cuba_bounds}")
print(f"Longitude: {cuba_bounds[0]:.2f}° to {cuba_bounds[2]:.2f}°")
print(f"Latitude: {cuba_bounds[1]:.2f}° to {cuba_bounds[3]:.2f}°")Cuba bounding box: [-84.95236206 19.8259716 -74.13118744 23.27763939]
Longitude: -84.95° to -74.13°
Latitude: 19.83° to 23.28°
Data Size Comparison: - 📊 Global CHIRPS GEFS grid: ~7,200 × 3,600 pixels (≈26M pixels) - 📋 Cuba bounding box: ~220 × 100 pixels (≈22K pixels)
- ⚡ Efficiency gain: 99.9% reduction (1,000× smaller!)
Stage 1: Bounding Box Clipping (during download) - Applied immediately during COG streaming - Reduces network transfer by 99.9% - Only downloads pixels within bounding box
Stage 2: Precise Geometry Clipping (during processing) - Applied to buffered bounding box data - Uses exact country boundary geometry - Handles complex shapes and islands
The modular architecture allows you to work with individual components or use the complete workflow:
# Create a CHIRPS GEFS configuration for demonstration
config = ChirpsGefsConfig(
geometry=cuba_gdf,
region_name="cuba_cog_demo",
start_date="2024-12-01",
end_date="2024-12-05",
leadtime_days=5, # Short demo period
verbose=True
)
print(f"🌍 Region: {config.region_name}")
print(f"📅 Date range: {config.start_date} to {config.end_date}")
print(f"⏱️ Leadtime: {config.leadtime_days} days")
# Calculate total number of files: days × leadtimes
date_range = pd.date_range(config.start_date, config.end_date, freq='D')
total_files = len(date_range) * config.leadtime_days
print(f"📊 Total forecasts: {total_files} files")🌍 Region: cuba_cog_demo
📅 Date range: 2024-12-01 to 2024-12-05
⏱️ Leadtime: 5 days
📊 Total forecasts: 25 files
Let’s examine each component’s role in the COG processing pipeline:
# Initialize each component to show their specific functions
downloader = ChirpsGefsDownloader(config)
loader = ChirpsGefsLoader(config)
processor = ChirpsGefsProcessor(config)
print("Initialized all components successfully")Initialized all components successfully
Component Architecture:
Understanding the CHIRPS GEFS data structure and URL patterns:
# Example: Forecast issued on Dec 10th for Dec 15th (5-day leadtime)
issue_date = pd.Timestamp("2024-12-10")
valid_date = pd.Timestamp("2024-12-15")
url = downloader.generate_url(issue_date, valid_date)
filename = downloader.generate_filename(issue_date, valid_date)
blob_path = downloader.generate_blob_path(filename)
print(f"📅 Issue date: {issue_date.date()}")
print(f"📅 Valid date: {valid_date.date()} (5-day leadtime)")
print(f"🌐 Source URL: {url}")
print()
print("Local Storage Structure:")
print(f"📄 Filename: {filename}")
print(f"☁️ Blob path: {blob_path}")
print()
print("Date Pair Examples:")
# Generate a few examples to show the pattern
date_range = pd.date_range(config.start_date, config.end_date, freq='D')
examples_shown = 0
for issue_date in date_range[:2]: # Show first 2 issue dates
for leadtime in range(min(3, config.leadtime_days)): # Show first 3 leadtimes
valid_date = issue_date + pd.Timedelta(days=leadtime)
examples_shown += 1
print(f" {examples_shown}. Issue: {issue_date.date()} → Valid: {valid_date.date()}")
if total_files > examples_shown:
print(f" ... ({total_files - examples_shown} more combinations)")📅 Issue date: 2024-12-10
📅 Valid date: 2024-12-15 (5-day leadtime)
🌐 Source URL: https://data.chc.ucsb.edu/products/EWX/data/forecasts/CHIRPS-GEFS_precip_v12/daily_16day/2024/12/10/data.2024.1215.tif
Local Storage Structure:
📄 Filename: chirps-gefs-cuba_cog_demo_issued-2024-12-10_valid-2024-12-15.tif
☁️ Blob path: ds-aa-cub-hurricanes/raw/chirps_gefs/cuba_cog_demo/chirps-gefs-cuba_cog_demo_issued-2024-12-10_valid-2024-12-15.tif
Date Pair Examples:
1. Issue: 2024-12-01 → Valid: 2024-12-01
2. Issue: 2024-12-01 → Valid: 2024-12-02
3. Issue: 2024-12-01 → Valid: 2024-12-03
4. Issue: 2024-12-02 → Valid: 2024-12-02
5. Issue: 2024-12-02 → Valid: 2024-12-03
6. Issue: 2024-12-02 → Valid: 2024-12-04
... (19 more combinations)
This is where the major efficiency gains happen:
Step 1: COG Streaming Workflow
🔽 DOWNLOAD PHASE (ChirpsGefsDownloader.download_single_file):
# Open global COG directly from CHIRPS GEFS server
with rxr.open_rasterio(source_url) as da_global:
# da_global.shape = (1, 3600, 7200) # Global 0.05° grid
# da_global.nbytes ≈ 103 MB # Full global array
# IMMEDIATE bounding box clipping during streaming:
bounds = self.config.geometry.total_bounds
da_aoi = da_global.rio.clip_box(*bounds)
# da_aoi.shape = (1, 100, 220) # Cuba region only
# da_aoi.nbytes ≈ 88 KB # 99.9% size reduction!
# Upload spatially-optimized COG to blob storage:
stratus.upload_cog_to_blob(da_aoi, blob_path)⚡ Result: Only the Cuba region data is transferred and stored
📊 Network efficiency: 99.9% reduction in data transfer
After download, the processing phase applies exact geometry clipping:
Step 2: Spatial Statistics Processing
🔄 PROCESSING PHASE (ChirpsGefsProcessor.process_spatial_mean):
# Load spatially-optimized COG from blob storage
da_loaded = loader.load_raster(issue_date, valid_date)
# da_loaded.shape = (100, 220) # Bounding box clipped
# da_loaded.nbytes ≈ 88 KB # Small, fast loading
# Apply precise geometry clipping:
da_clipped = da_loaded.rio.clip(
self.config.geometry.geometry,
all_touched=True
)
# da_clipped.shape = varies # Exact Cuba boundary
# Handles islands, complex coastlines, etc.
# Calculate spatial statistics (customizable):
spatial_mean = da_clipped.mean(dim=['x', 'y']).values
# Result: Single precipitation value for Cuba🎯 Result: Precise spatial statistics for complex geometries
🔧 Customizable: Easy to implement other zonal statistics
Once COGs are processed and stored, loading them is very fast:
# Example of loading a specific raster (requires downloaded data)
try:
# Load a specific issue/valid date combination
da = loader.load_raster(
issue_date=pd.Timestamp("2024-12-10"),
valid_date=pd.Timestamp("2024-12-15")
)
print(f"✅ Loaded raster shape: {da.shape}")
print(f"🗺️ Spatial extent: {da.rio.bounds()}")
print(f"📐 CRS: {da.rio.crs}")
print(f"📏 Resolution: {da.rio.resolution()}")
print(f"💧 Precipitation range: {da.min().values:.2f} to {da.max().values:.2f} mm/day")
except Exception as e:
print(f"⚠️ Could not load raster (expected without downloaded data): {e}")The current system uses spatial mean calculations, but the architecture is designed for easy customization. Here’s how you would implement custom zonal statistics:
# Template for implementing custom zonal statistics
class CustomChirpsGefsProcessor(ChirpsGefsProcessor):
def calculate_custom_statistics(self, da_clipped):
"""
Implement your custom spatial statistics here.
Args:
da_clipped: xarray.DataArray clipped to geometry
Returns:
dict: Custom statistics
"""
return {
'mean': da_clipped.mean().values,
'max': da_clipped.max().values,
'std': da_clipped.std().values,
'percentile_95': da_clipped.quantile(0.95).values,
'pixel_count': da_clipped.count().values,
# Add your custom metrics here
}
def process_spatial_statistics(self, issue_date, valid_date):
# Load and clip data
da = self.loader.load_raster(issue_date, valid_date)
da_clipped = da.rio.clip(self.config.geometry.geometry)
# Use custom statistics instead of just mean
stats = self.calculate_custom_statistics(da_clipped)
return {
'issue_date': issue_date.date(),
'valid_date': valid_date.date(),
**stats
}
print("✅ Custom zonal statistics template ready!")✅ Custom zonal statistics template ready!
try:
da = loader.load_raster(issue_date, valid_date)
da["valid_date"] = valid_date
da["leadtime"] = leadtime
das_all_leadtimes.append(da)
print(f"Loaded leadtime {leadtime}: {valid_date.date()}")
except Exception as e:
print(f"Could not load leadtime {leadtime}: {e}")
if das_all_leadtimes: # Combine into single dataset da_combined = xr.concat(das_all_leadtimes, dim=“valid_date”) print(f”Combined dataset shape: {da_combined.shape}“)
# Apply precise geometry clipping
da_cuba = da_combined.rio.clip(cuba_gdf.geometry, all_touched=True)
# Calculate spatial mean for each forecast day
spatial_means = da_cuba.mean(dim=["x", "y"])
print(f"Spatial means by leadtime: {spatial_means.values}")
### Flexible Zonal Statistics
The current system calculates spatial means, but you can easily modify it for different statistics:
::: {#3ead2aeb .cell execution_count=15}
```` {.python .cell-code}
### Development Workflow: Start Small, Scale Up
The recommended approach for developing custom zonal statistics:
**🚀 Phase 1: Small-Scale Development**
1. Use short date ranges (2-3 days)
2. Download/process limited data for testing
3. Develop and test custom zonal statistics
4. Iterate on statistical methods
**📈 Phase 2: Scale Testing**
5. Test with longer periods (1-2 weeks)
6. Validate statistical methods
7. Optimize performance
**🌍 Phase 3: Operational Deployment**
8. Deploy with full date ranges
9. Implement error handling
10. Set up monitoring/logging
**🔧 Key Benefits of This Architecture:**
- Start with spatial means (already working)
- Add custom statistics incrementally
- COG processing handles the complex spatial work
- Focus on the statistics, not the data management
## Complete Pipeline Example
### High-Level Manager Usage
For production workflows, use the manager class:
```{python}
# | eval: false
# Create manager for complete workflow
manager = ChirpsGefsManager(config)
# Run complete pipeline: download → process → return results
df_results = manager.run_pipeline()
print("Pipeline Results:")
print(f"✅ Processed {len(df_results)} forecast records")
print(f"📅 Date range: {df_results['issue_date'].min()} to {df_results['issue_date'].max()}")
print(f"🎯 Lead times: {df_results['valid_date'].min()} to {df_results['valid_date'].max()}")
print(f"💧 Precipitation range: {df_results['mean_precipitation_mm_day'].min():.2f} to {df_results['mean_precipitation_mm_day'].max():.2f} mm/day")
:::
CHIRPS GEFS provides 16-day forecasts. Here’s how to work with multiple leadtimes:
# Analyze forecast skill by leadtime
import matplotlib.pyplot as plt
# Group results by leadtime for analysis
df_results['leadtime_days'] = (
pd.to_datetime(df_results['valid_date']) -
pd.to_datetime(df_results['issue_date'])
).dt.days
# Calculate statistics by leadtime
leadtime_stats = df_results.groupby('leadtime_days')['mean_precipitation_mm_day'].agg([
'count', 'mean', 'std', 'min', 'max'
]).round(2)
print("Precipitation by Leadtime:")
print(leadtime_stats)
# Simple visualization concept
print("\nVisualization Example:")
print("plt.figure(figsize=(10, 6))")
print("df_results.boxplot(column='mean_precipitation_mm_day', by='leadtime_days')")
print("plt.title('Precipitation Forecasts by Leadtime')")
print("plt.xlabel('Leadtime (days)')")
print("plt.ylabel('Precipitation (mm/day)')")This demonstrates how you can implement your own zonal stats method.
"""
loader = ChirpsGefsLoader(config)
results = []
issue_date_range = pd.date_range(start=start_date, end=end_date, freq="D")
for issue_date in issue_date_range:
print(f"Processing issue date: {issue_date.date()}")
# Load all leadtimes for this issue date
das_leadtimes = []
for leadtime in range(config.leadtime_days):
valid_date = issue_date + pd.Timedelta(days=leadtime)
try:
# Load pre-processed COG (bounding box already applied)
da = loader.load_raster(issue_date, valid_date)
da = da.expand_dims(valid_date=[valid_date])
das_leadtimes.append(da)
except Exception as e:
print(f"Could not load leadtime {leadtime}: {e}")
continue
if das_leadtimes:
# Combine into single dataset
da_combined = xr.concat(das_leadtimes, dim="valid_date")
# Apply precise geometry clipping
da_cuba = da_combined.rio.clip(cuba_gdf.geometry, all_touched=True)
# Calculate spatial mean for each forecast day
spatial_means = da_cuba.mean(dim=["x", "y"])
print(f"Spatial means by leadtime: {spatial_means.values}")
# Add to results
for i, valid_date in enumerate(da_cuba.valid_date):
da_single = da_cuba.isel(valid_date=i)
# Basic statistics
stats = {
'issue_date': issue_date.date(),
'valid_date': pd.to_datetime(valid_date.values).date(),
'leadtime_days': i,
'mean_precipitation_mm_day': float(da_single.mean().values),
'max_precipitation_mm_day': float(da_single.max().values),
'min_precipitation_mm_day': float(da_single.min().values),
'std_precipitation_mm_day': float(da_single.std().values),
'pixel_count': int(da_single.count().values),
'pixel_coverage': float(da_single.count().values / da_single.size),
}
# Add to results
results.append(stats)
return pd.DataFrame(results)
print(“✅ Custom zonal statistics template ready!”) print(“🔧 Modify the stats dictionary to add your specific metrics”)
## High-Level Interface Options
### Option 1: Simple One-Line Processing
For quick analysis and prototyping:
::: {#48b269fe .cell execution_count=17}
``` {.python .cell-code}
from src.datasources.chirps_gefs import process_chirps_gefs_for_region
# Process CHIRPS GEFS data with minimal configuration
results = process_chirps_gefs_for_region(
geometry=cuba_gdf,
region_name="cuba_test",
recent_only=True, # Only download recent data
start_date="2024-12-09",
end_date="2024-12-10",
)
# Access results
download_stats = results["download_stats"]
processed_data = results["processed_data"]
print("One-Line Processing Results:")
print(f"✅ Downloaded {download_stats['success']} files successfully")
print(f"❌ Failed downloads: {download_stats['failed']}")
print(f"📊 Processed data shape: {processed_data.shape}")
if "warning" in results:
print(f"⚠️ Warning: {results['warning']}")
:::
For production workflows with detailed control:
# Create manager with custom configuration
manager = ChirpsGefsManager(
geometry=cuba_gdf,
region_name="cuba_production",
start_date="2024-12-09",
end_date="2024-12-10",
leadtime_days=16, # Full 16-day forecasts
verbose=True,
)
# Option A: Run individual steps
print("🔽 Downloading data...")
download_stats = manager.download_only(recent_only=True, days_back=30)
print(f" Downloaded {download_stats['success']}/{download_stats['total']} files")
print("🔄 Processing data...")
processed_data = manager.process_only(recent_only=True)
print(f" Processed {len(processed_data)} forecast records")
# Option B: Run complete pipeline
print("🚀 Running complete pipeline...")
pipeline_results = manager.run_pipeline()
print(f" Pipeline completed: {len(pipeline_results)} records")For maximum flexibility and customization:
# Initialize individual components
config = ChirpsGefsConfig(geometry=cuba_gdf, region_name="cuba_custom")
downloader = ChirpsGefsDownloader(config)
loader = ChirpsGefsLoader(config)
processor = ChirpsGefsProcessor(config)
# Download specific files
success = downloader.download_single_file(
issue_date=pd.Timestamp("2024-12-10"),
valid_date=pd.Timestamp("2024-12-15")
)
print(f"🔽 Single file download: {success}")
# Load and examine data
da = loader.load_raster(
issue_date=pd.Timestamp("2024-12-10"),
valid_date=pd.Timestamp("2024-12-15")
)
print(f"📁 Loaded raster: {da.shape}")
# Process with custom statistics (when you implement them)
# custom_stats = your_custom_processor.calculate_statistics(da)The COG-based architecture provides significant efficiency gains:
📊 Spatial Efficiency: - Global dataset: ~26M pixels (103 MB per file) - Cuba region: ~22K pixels (88 KB per file) - Reduction: 99.9% (1,000× smaller)
⚡ Network Efficiency: - Only region-specific data transferred - No temporary global files - Direct streaming with spatial cropping
🔧 Processing Efficiency: - Precise geometry clipping on small datasets - Fast statistical calculations - Easy parallelization potential
🚀 Development Efficiency: - Start with working spatial means - Add custom statistics incrementally - Focus on analysis, not data management - Test with small datasets, scale up easily
Based on this COG processing framework, here’s your development path:
🎯 Immediate (Ready Now): 1. Use existing spatial mean processing 2. Test with short date ranges 3. Validate Cuba geometry and results
🔧 Short Term (When You’re Ready): 4. Implement custom zonal statistics using template 5. Add specific metrics you need 6. Test custom processing with small datasets
🌍 Medium Term (Scaling Up): 7. Extend to longer time periods 8. Add other regions if needed 9. Optimize performance for large-scale processing
📈 Long Term (Operational): 10. Integrate with larger analytical workflows 11. Add automated monitoring and error handling 12. Consider real-time processing capabilities
✅ Key Advantage: You have a working foundation. The COG processing handles all the complex spatial work. You can focus on implementing the specific statistics you need.
This documentation demonstrates the complete COG processing workflow, from efficient spatial data handling to customizable zonal statistics implementation. The system is designed to be both immediately useful (with spatial means) and easily extensible for your custom analytical needs. pipeline_results = manager.run_full_pipeline( download_recent_only=True, days_back=30 )
::: {#fc32308d .cell execution_count=20}
```` {.python .cell-code}
# Create manager with custom configuration
manager = ChirpsGefsManager(
geometry=cuba_gdf,
region_name="cuba_detailed",
start_date="2024-12-09",
end_date="2024-12-10",
leadtime_days=16,
blob_base_dir="custom/chirps_gefs",
clobber=False,
verbose=True,
)
# Run individual steps
download_stats = manager.download_only(recent_only=True, days_back=30)
processed_data = manager.process_only(recent_only=True)
# Or run the full pipeline
## Practical Development Workflow
Here's the recommended workflow for working with CHIRPS GEFS data:
### Step 1: Start Small and Test COG Processing
```{python}
# | eval: false
# 1. Create a small test configuration
test_config = ChirpsGefsConfig(
geometry=cuba_gdf,
region_name="cuba_test",
start_date="2024-12-10", # Single day
end_date="2024-12-10", # Same day
leadtime_days=3, # Just 3 leadtimes
verbose=True
)
# 2. Test download for a single day (3 files)
downloader = ChirpsGefsDownloader(test_config)
stats = downloader.download_date_range("2024-12-10", "2024-12-10")
print(f"Downloaded {stats['success']} files")
# 3. Examine the downloaded rasters
loader = ChirpsGefsLoader(test_config)
da = loader.load_raster(
pd.Timestamp("2024-12-10"),
pd.Timestamp("2024-12-11") # 1-day leadtime
)
print(f"Raster shape: {da.shape}")
print(f"Data range: {da.min().values:.2f} to {da.max().values:.2f} mm/day")
:::
# Test your custom processing on the small dataset
df_custom = process_custom_zonal_stats(
test_config, "2024-12-10", "2024-12-10"
)
print("Custom processing results:")
print(df_custom)# Once your custom method works, scale up
production_config = ChirpsGefsConfig(
geometry=cuba_gdf,
region_name="cuba_production",
start_date="2024-12-01",
end_date="2024-12-31", # Full month
leadtime_days=16, # Full 16-day forecast
verbose=False # Reduce logging for production
)
# Run your custom processing at scale
df_production = process_custom_zonal_stats(
production_config, "2024-12-01", "2024-12-31"
)The system achieves 99.9% data reduction through spatial clipping:
print("CHIRPS GEFS Spatial Efficiency:")
print(" Global file: ~26 million pixels")
print(" Cuba region: ~22 thousand pixels")
print(" Reduction: 1,000× smaller files")
print(" Benefit: Faster downloads, less storage, quicker processing")CHIRPS GEFS Spatial Efficiency:
Global file: ~26 million pixels
Cuba region: ~22 thousand pixels
Reduction: 1,000× smaller files
Benefit: Faster downloads, less storage, quicker processing
Data is organized in a clear hierarchy:
projects/ds-aa-cub-hurricanes/
├── raw/chirps_gefs/cuba_test/
│ ├── chirps-gefs-cuba_test_issued-2024-12-10_valid-2024-12-10.tif
│ ├── chirps-gefs-cuba_test_issued-2024-12-10_valid-2024-12-11.tif
│ └── chirps-gefs-cuba_test_issued-2024-12-10_valid-2024-12-12.tif
└── processed/chirps_gefs/cuba_test/
└── cuba_test_chirps_gefs_mean_daily_2024_2024.parquet
The modular design allows you to: - Use existing spatial means: Call ChirpsGefsProcessor.process_spatial_mean() - Implement custom statistics: Use ChirpsGefsLoader to load rasters and process yourself - Mix approaches: Use downloader/loader separately, then apply your own analysis
This CHIRPS GEFS module provides the foundation for precipitation forecast processing:
The system handles all the complex COG processing, storage, and data management - you can focus on implementing the specific zonal statistics that meet your analytical needs.