Super-Space-WX / app.py
nakas's picture
Update app.py
c245f9d verified
"""
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()