nakas commited on
Commit
217e5a5
·
verified ·
1 Parent(s): 7a4ea8a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +115 -92
app.py CHANGED
@@ -1,94 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
- import folium
3
-
4
- # Dictionary of radar sites with their center coordinates (latitude, longitude)
5
- RADAR_SITES = {
6
- "KTLX": {"name": "KTLX (Oklahoma City)", "lat": 35.333, "lon": -97.277},
7
- "KFWS": {"name": "KFWS (Fort Worth)", "lat": 32.847, "lon": -97.276},
8
- "KHGX": {"name": "KHGX (Houston)", "lat": 29.757, "lon": -95.389}
9
- }
10
-
11
- # Dictionary of radar product tile URLs (from Iowa State Mesonet)
12
- RADAR_PRODUCT_TILES = {
13
- "Reflectivity": "https://mesonet.agron.iastate.edu/tiles/nexrad-base/{z}/{x}/{y}.png",
14
- "Velocity": "https://mesonet.agron.iastate.edu/tiles/nexrad-velo/{z}/{x}/{y}.png"
15
- }
16
-
17
- # Function to build the base map tile URL for non-OSM providers
18
- def get_base_map_tiles(provider, api_key):
19
- if provider == "OpenStreetMap":
20
- # OpenStreetMap is the default; no API key required.
21
- return None
22
- elif provider == "MapTiler":
23
- # MapTiler Streets style URL – uses the provided API key.
24
- return f"https://api.maptiler.com/maps/streets/{{z}}/{{x}}/{{y}}.png?key={api_key}"
25
- elif provider == "Mapbox":
26
- # Mapbox Streets style URL – uses the provided API key.
27
- return f"https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{{z}}/{{x}}/{{y}}?access_token={api_key}"
28
  else:
29
- return None
30
-
31
- # Function to generate the HTML map with a radar overlay.
32
- def generate_map(radar_site, product, map_provider, api_key):
33
- # Retrieve the selected radar site coordinates; default to center of USA if not found.
34
- site = RADAR_SITES.get(radar_site, {"lat": 39.8283, "lon": -98.5795})
35
- center_lat = site["lat"]
36
- center_lon = site["lon"]
37
-
38
- # Create a folium map centered on the radar site.
39
- m = folium.Map(location=[center_lat, center_lon], zoom_start=6, control_scale=True)
40
-
41
- # If a provider other than OpenStreetMap is selected, add its tile layer.
42
- if map_provider != "OpenStreetMap":
43
- base_tile_url = get_base_map_tiles(map_provider, api_key)
44
- if base_tile_url:
45
- folium.TileLayer(
46
- tiles=base_tile_url,
47
- attr=f"{map_provider} Map",
48
- name=f"{map_provider} Map",
49
- overlay=False,
50
- control=True
51
- ).add_to(m)
52
-
53
- # Add the radar overlay tile layer based on the selected product.
54
- radar_tile_url = RADAR_PRODUCT_TILES.get(product)
55
- if radar_tile_url:
56
- folium.raster_layers.TileLayer(
57
- tiles=radar_tile_url,
58
- attr="NOAA NEXRAD",
59
- name=f"Radar {product}",
60
- overlay=True,
61
- control=True,
62
- opacity=0.7
63
- ).add_to(m)
64
-
65
- # Add a layer control so users can toggle layers.
66
- folium.LayerControl().add_to(m)
67
-
68
- # Render the map as an HTML string.
69
- return m.get_root().render()
70
-
71
- # Define the choices for each input.
72
- site_choices = list(RADAR_SITES.keys())
73
- product_choices = list(RADAR_PRODUCT_TILES.keys())
74
- map_provider_choices = ["OpenStreetMap", "MapTiler", "Mapbox"]
75
-
76
- # Build the interface using gr.Blocks
77
- with gr.Blocks() as demo:
78
- gr.Markdown("# Radar Viewer")
79
- gr.Markdown("Select a radar site and product, choose a base map provider (enter an API key if needed), then click **Submit** to view the radar overlay.")
80
-
81
- with gr.Row():
82
- radar_site = gr.Dropdown(choices=site_choices, label="Radar Site")
83
- product = gr.Dropdown(choices=product_choices, label="Radar Product")
84
-
85
- with gr.Row():
86
- map_provider = gr.Dropdown(choices=map_provider_choices, label="Map Provider")
87
- api_key = gr.Textbox(lines=1, placeholder="Enter API key if required", label="API Key")
88
-
89
- submit = gr.Button("Submit")
90
- output = gr.HTML(label="Radar Map")
91
-
92
- submit.click(generate_map, inputs=[radar_site, product, map_provider, api_key], outputs=output)
93
-
94
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Radar Data Downloader via Gradio
3
+ ==================================
4
+
5
+ This script mimics part of Supercell Wx’s functionality by downloading the latest
6
+ radar data file (Level 2 or Level 3) from public AWS S3 buckets. It uses boto3 in unsigned mode,
7
+ so no AWS credentials or API keys are required. The Gradio interface lets you select the
8
+ data type, radar station, and date (in UTC) to download a file.
9
+ """
10
+
11
+ import datetime
12
+ import io
13
+
14
+ import boto3
15
+ from botocore import UNSIGNED
16
+ from botocore.client import Config
17
  import gradio as gr
18
+
19
+
20
+ def fetch_latest_radar_data(radar_type, station, date_str):
21
+ """
22
+ Downloads the latest radar data file from the appropriate S3 bucket.
23
+
24
+ Parameters:
25
+ radar_type (str): Either "Level 2" or "Level 3".
26
+ station (str): Radar station identifier (e.g. "KTLX").
27
+ date_str (str): Date string in the format "YYYY-MM-DD". If blank, uses current UTC date.
28
+
29
+ Returns:
30
+ str: A text summary including the S3 key, file size, last modified date,
31
+ and a hex preview of the first 64 bytes of the file.
32
+ """
33
+ # Parse the date (default to current UTC if empty)
34
+ if not date_str.strip():
35
+ date_obj = datetime.datetime.utcnow()
 
 
 
 
 
 
 
 
36
  else:
37
+ try:
38
+ date_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d")
39
+ except ValueError:
40
+ return "Error: Date must be in YYYY-MM-DD format."
41
+ year = date_obj.strftime("%Y")
42
+ month = date_obj.strftime("%m")
43
+ day = date_obj.strftime("%d")
44
+
45
+ # Choose the appropriate bucket based on radar type.
46
+ if radar_type == "Level 2":
47
+ bucket_name = "noaa-nexrad-level2"
48
+ # For Level 2, the folder structure is: YYYY/MM/DD/STATION/
49
+ prefix = f"{year}/{month}/{day}/{station.upper()}/"
50
+ elif radar_type == "Level 3":
51
+ bucket_name = "unidata-nexrad-level3"
52
+ # For Level 3, the structure is similar.
53
+ prefix = f"{year}/{month}/{day}/{station.upper()}/"
54
+ else:
55
+ return "Error: Invalid radar type selected."
56
+
57
+ # Create an S3 client with unsigned requests.
58
+ s3 = boto3.client('s3', config=Config(signature_version=UNSIGNED))
59
+ try:
60
+ response = s3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
61
+ except Exception as e:
62
+ return f"Error accessing bucket: {e}"
63
+
64
+ if 'Contents' not in response:
65
+ return f"No data found for station {station.upper()} on {date_str} in {radar_type}."
66
+
67
+ # Sort objects by LastModified and choose the latest file.
68
+ objects = response['Contents']
69
+ objects_sorted = sorted(objects, key=lambda x: x['LastModified'])
70
+ latest_object = objects_sorted[-1]
71
+
72
+ file_key = latest_object['Key']
73
+ file_size = latest_object['Size']
74
+ last_modified = latest_object['LastModified']
75
+
76
+ # Download the file into a BytesIO buffer.
77
+ buffer = io.BytesIO()
78
+ try:
79
+ s3.download_fileobj(bucket_name, file_key, buffer)
80
+ except Exception as e:
81
+ return f"Error downloading file: {e}"
82
+ buffer.seek(0)
83
+
84
+ # Read a short preview (first 64 bytes) and convert to hex.
85
+ data_preview = buffer.read(64)
86
+ hex_preview = data_preview.hex()
87
+
88
+ # Return a summary of the downloaded file.
89
+ summary = (
90
+ f"Downloaded file: {file_key}\n"
91
+ f"Size: {file_size} bytes\n"
92
+ f"Last Modified (UTC): {last_modified}\n"
93
+ f"Hex preview (first 64 bytes):\n{hex_preview}"
94
+ )
95
+ return summary
96
+
97
+
98
+ # Create the Gradio interface.
99
+ interface = gr.Interface(
100
+ fn=fetch_latest_radar_data,
101
+ inputs=[
102
+ gr.inputs.Radio(choices=["Level 2", "Level 3"], label="Radar Data Type"),
103
+ gr.inputs.Textbox(label="Radar Station (e.g. KTLX)", default="KTLX"),
104
+ gr.inputs.Textbox(label="Date (YYYY-MM-DD)", default=datetime.datetime.utcnow().strftime("%Y-%m-%d"))
105
+ ],
106
+ outputs="text",
107
+ title="Radar Data Downloader",
108
+ description=(
109
+ "This tool downloads the latest radar data file from public AWS S3 buckets "
110
+ "used by Supercell Wx. Choose a radar data type (Level 2 or Level 3), enter a "
111
+ "radar station (e.g. KTLX), and a date (UTC). In future iterations, you can add "
112
+ "visualization using OpenStreetMap (which requires no API keys)."
113
+ )
114
+ )
115
+
116
+ if __name__ == "__main__":
117
+ interface.launch()