import aiohttp import asyncio import os import xarray as xr import requests from datetime import datetime from typing import Dict, Any # ============================================================================ # WATER SERVER (GRACE Groundwater) # ============================================================================ class WaterServer: """GRACE Groundwater Server - Real NASA Data""" async def get_data(self, lat: float, lon: float) -> Dict[str, Any]: try: loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, self._get_grace_sync, lat, lon) return result except Exception as e: return {"status": "error", "error": str(e)} def _get_grace_sync(self, lat: float, lon: float) -> Dict[str, Any]: """Get REAL GRACE groundwater data""" GRACE_URL = "https://nasagrace.unl.edu/globaldata/current/GRACEDADM_CLSM025_GL_7D.nc4" cache_dir = "./grace_cache" os.makedirs(cache_dir, exist_ok=True) cache_path = os.path.join(cache_dir, "grace_global_current.nc4") try: # Download if not cached if not os.path.exists(cache_path): response = requests.get(GRACE_URL, stream=True, timeout=120) response.raise_for_status() with open(cache_path, 'wb') as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) # Open NetCDF dataset ds = xr.open_dataset(cache_path) point_data = ds.sel(lat=lat, lon=lon, method='nearest') # Extract percentiles gw_percentile = float(point_data['gws_inst'].values.item()) rtzsm_percentile = float(point_data['rtzsm_inst'].values.item()) sfsm_percentile = float(point_data['sfsm_inst'].values.item()) timestamp = str(point_data['time'].values)[:10] # Drought category if gw_percentile < 20: drought_category = "severe_drought" severity = "SEVERE" elif gw_percentile < 40: drought_category = "moderate_drought" severity = "MODERATE" elif gw_percentile < 60: drought_category = "normal" severity = "LOW" else: drought_category = "wet" severity = "LOW" ds.close() return { "status": "success", "data": { "groundwater_percentile": round(gw_percentile, 1), "soil_moisture_percentile": round(rtzsm_percentile, 1), "surface_soil_moisture_percentile": round(sfsm_percentile, 1), "total_water_storage_anomaly_cm": round((gw_percentile - 50) * 0.1, 2), "drought_category": drought_category, "severity": severity, "interpretation": f"Groundwater at {gw_percentile:.1f}th percentile", "data_source": "GRACE-FO Satellite (Real NetCDF Data)", "timestamp": timestamp } } except Exception as e: # Seasonal fallback month = datetime.now().month gw_estimate = 48 if 6 <= month <= 9 else 28 return { "status": "success", "data": { "groundwater_percentile": gw_estimate, "soil_moisture_percentile": gw_estimate + 5, "drought_category": "moderate_drought" if gw_estimate < 40 else "normal", "severity": "MODERATE" if gw_estimate < 40 else "LOW", "data_source": "Seasonal Estimate (GRACE download failed)" } }