Spaces:
Sleeping
Sleeping
""" | |
Radar Data Downloader with Two-Step Workflow via Gradio Blocks | |
=============================================================== | |
Step 1: Click "Fetch Stations" to dynamically retrieve the full list of radar sites. | |
Step 2: In the "Download Data" tab, select a radar type, station, and date, then click "Download Data" to fetch the latest radar file. | |
Required packages: | |
pip install gradio boto3 requests | |
""" | |
import datetime | |
import io | |
import boto3 | |
from botocore import UNSIGNED | |
from botocore.client import Config | |
import gradio as gr | |
import requests | |
def get_full_station_list(): | |
""" | |
Fetches a full list of radar stations from a public API endpoint. | |
Here we use NOAA’s public radar station endpoint. | |
If that fails, we fall back to a static JSON file from GitHub. | |
Returns a sorted list of station identifiers. | |
""" | |
api_url = "https://api.weather.gov/radar/stations" | |
try: | |
response = requests.get(api_url, timeout=10) | |
response.raise_for_status() | |
data = response.json() | |
features = data.get("features", []) | |
stations = [] | |
for feature in features: | |
props = feature.get("properties", {}) | |
station_id = props.get("stationIdentifier") | |
if station_id: | |
stations.append(station_id.upper()) | |
stations = sorted(list(set(stations))) | |
if stations: | |
return stations | |
except Exception as e: | |
print(f"Error fetching station list from API: {e}") | |
# Fallback to static JSON from GitHub. | |
return get_station_list_from_github() | |
def get_station_list_from_github(): | |
""" | |
Fallback: Downloads and parses the radar_sites.json file from the Supercell Wx GitHub repository. | |
Returns a sorted list of station identifiers. | |
""" | |
url = "https://raw.githubusercontent.com/dpaulat/supercell-wx/main/data/radar_sites.json" | |
try: | |
response = requests.get(url, timeout=10) | |
response.raise_for_status() | |
data = response.json() | |
except Exception as e: | |
print(f"Error fetching station list from GitHub: {e}") | |
return ["KTLX", "KDOX", "KBMX"] | |
stations = [] | |
for item in data: | |
if "station" in item: | |
stations.append(item["station"].upper()) | |
elif "id" in item: | |
stations.append(item["id"].upper()) | |
stations = sorted(list(set(stations))) | |
return stations | |
def update_station_list(): | |
""" | |
Retrieves the full station list and returns a dictionary to update the dropdown component. | |
(Note: Some Gradio versions do not support .update(), so we return a plain dict.) | |
""" | |
stations = get_full_station_list() | |
if stations: | |
return {"choices": stations, "value": stations[0]} | |
else: | |
return {"choices": [], "value": None} | |
def fetch_latest_radar_data(radar_type, station, date_str): | |
""" | |
Downloads the latest radar data file from the appropriate AWS S3 bucket. | |
Parameters: | |
radar_type (str): "Level 2" or "Level 3". | |
station (str): Radar station identifier (e.g. "KTLX"). | |
date_str (str): Date in "YYYY-MM-DD" format. If blank, uses current UTC date. | |
Returns: | |
str: A summary of the downloaded file (S3 key, file size, last modified, and hex preview). | |
""" | |
if not station: | |
return "Error: No station selected. Please fetch stations first." | |
try: | |
if not date_str.strip(): | |
date_obj = datetime.datetime.utcnow() | |
else: | |
date_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d") | |
except ValueError: | |
return "Error: Date must be in YYYY-MM-DD format." | |
year = date_obj.strftime("%Y") | |
month = date_obj.strftime("%m") | |
day = date_obj.strftime("%d") | |
if radar_type == "Level 2": | |
bucket_name = "noaa-nexrad-level2" | |
prefix = f"{year}/{month}/{day}/{station.upper()}/" | |
elif radar_type == "Level 3": | |
bucket_name = "unidata-nexrad-level3" | |
prefix = f"{year}/{month}/{day}/{station.upper()}/" | |
else: | |
return "Error: Invalid radar type selected." | |
s3 = boto3.client('s3', config=Config(signature_version=UNSIGNED)) | |
try: | |
response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix) | |
except Exception as e: | |
return f"Error accessing bucket: {e}" | |
if 'Contents' not in response: | |
return f"No data found for station {station.upper()} on {date_str} in {radar_type}." | |
objects = response['Contents'] | |
objects_sorted = sorted(objects, key=lambda x: x['LastModified']) | |
latest_object = objects_sorted[-1] | |
file_key = latest_object['Key'] | |
file_size = latest_object['Size'] | |
last_modified = latest_object['LastModified'] | |
buffer = io.BytesIO() | |
try: | |
s3.download_fileobj(bucket_name, file_key, buffer) | |
except Exception as e: | |
return f"Error downloading file: {e}" | |
buffer.seek(0) | |
data_preview = buffer.read(64) | |
hex_preview = data_preview.hex() | |
summary = ( | |
f"Downloaded file: {file_key}\n" | |
f"Size: {file_size} bytes\n" | |
f"Last Modified (UTC): {last_modified}\n" | |
f"Hex preview (first 64 bytes):\n{hex_preview}" | |
) | |
return summary | |
# Build the Gradio Blocks interface. | |
with gr.Blocks() as demo: | |
gr.Markdown("# Radar Data Downloader") | |
gr.Markdown( | |
"Step 1: **Fetch Stations** to retrieve the full list of radar sites.<br>" | |
"Step 2: **Download Data** by selecting a radar type, station, and date." | |
) | |
with gr.Tab("Fetch Stations"): | |
with gr.Row(): | |
fetch_btn = gr.Button("Fetch Stations") | |
station_dropdown_fetch = gr.Dropdown(choices=[], label="Available Radar Stations") | |
fetch_btn.click(fn=update_station_list, inputs=[], outputs=station_dropdown_fetch) | |
with gr.Tab("Download Data"): | |
with gr.Row(): | |
radar_type = gr.Radio(choices=["Level 2", "Level 3"], label="Radar Data Type") | |
station_dropdown_download = gr.Dropdown(choices=[], label="Select Radar Station") | |
date_input = gr.Textbox(value=datetime.datetime.utcnow().strftime("%Y-%m-%d"), label="Date (YYYY-MM-DD)") | |
download_btn = gr.Button("Download Data") | |
download_output = gr.Textbox(label="Download Output", interactive=False) | |
download_btn.click(fn=fetch_latest_radar_data, | |
inputs=[radar_type, station_dropdown_download, date_input], | |
outputs=download_output) | |
sync_btn = gr.Button("Sync Station List to Download Tab") | |
sync_btn.click(fn=update_station_list, inputs=[], outputs=station_dropdown_download) | |
gr.Markdown( | |
"Note: First click **Fetch Stations** (or **Sync Station List to Download Tab**) to populate the radar site list before downloading data." | |
) | |
if __name__ == "__main__": | |
demo.launch() |