Spaces:
Build error
Build error
improve layout
Browse files
app.py
CHANGED
@@ -130,17 +130,33 @@ def run_forecast(date, lead_time, device):
|
|
130 |
def plot_forecast(state, selected_variable):
|
131 |
latitudes, longitudes = state["latitudes"], state["longitudes"]
|
132 |
values = state["fields"][selected_variable]
|
133 |
-
fig, ax = plt.subplots(figsize=(11, 6), subplot_kw={"projection": ccrs.PlateCarree()})
|
134 |
-
ax.coastlines()
|
135 |
-
ax.add_feature(cfeature.BORDERS, linestyle=":")
|
136 |
-
triangulation = tri.Triangulation(longitudes, latitudes)
|
137 |
|
138 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
139 |
contour = ax.tricontourf(triangulation, values, levels=20,
|
140 |
transform=ccrs.PlateCarree(),
|
141 |
cmap='RdBu_r')
|
|
|
142 |
plt.title(f"{selected_variable} at {state['date']}")
|
143 |
-
plt.colorbar(contour)
|
|
|
144 |
return fig
|
145 |
|
146 |
# Create dropdown choices with groups
|
@@ -152,37 +168,61 @@ for group_name, variables in VARIABLE_GROUPS.items():
|
|
152 |
for var_id, desc in sorted(variables.items()):
|
153 |
DROPDOWN_CHOICES.append((f"{desc} ({var_id})", var_id))
|
154 |
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
maximum=48,
|
167 |
-
step=6,
|
168 |
-
value=12,
|
169 |
-
label="Forecast Hours Ahead",
|
170 |
-
info=f"Latest data available from: {DEFAULT_DATE.strftime('%Y-%m-%d %H:%M UTC')}"
|
171 |
-
),
|
172 |
-
gr.Dropdown(
|
173 |
-
choices=DROPDOWN_CHOICES,
|
174 |
-
value="2t", # Default to 2m temperature
|
175 |
-
label="Select Variable to Plot",
|
176 |
-
info="Choose a meteorological variable to visualize"
|
177 |
-
)
|
178 |
-
],
|
179 |
-
outputs=gr.Plot(),
|
180 |
-
title="AIFS Weather Forecast",
|
181 |
-
description=f"""
|
182 |
-
Interactive visualization of ECMWF AIFS weather forecasts.
|
183 |
-
Starting from the latest available data ({DEFAULT_DATE.strftime('%Y-%m-%d %H:%M UTC')}),
|
184 |
select how many hours ahead you want to forecast and which meteorological variable to visualize.
|
185 |
-
"""
|
186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
187 |
|
188 |
demo.launch()
|
|
|
130 |
def plot_forecast(state, selected_variable):
|
131 |
latitudes, longitudes = state["latitudes"], state["longitudes"]
|
132 |
values = state["fields"][selected_variable]
|
|
|
|
|
|
|
|
|
133 |
|
134 |
+
# Create figure with specific projection centered on 0°
|
135 |
+
fig = plt.figure(figsize=(15, 8))
|
136 |
+
ax = plt.axes(projection=ccrs.PlateCarree(central_longitude=0))
|
137 |
+
|
138 |
+
ax.set_global()
|
139 |
+
ax.set_extent([-180, 180, -90, 90], crs=ccrs.PlateCarree())
|
140 |
+
|
141 |
+
# Add map features
|
142 |
+
ax.coastlines(resolution='50m')
|
143 |
+
ax.add_feature(cfeature.BORDERS, linestyle=":", alpha=0.5)
|
144 |
+
ax.gridlines(draw_labels=True)
|
145 |
+
|
146 |
+
# Fix longitudes to be -180 to 180
|
147 |
+
fixed_lons = np.where(longitudes > 180, longitudes - 360, longitudes)
|
148 |
+
|
149 |
+
# Create triangulation with fixed longitudes
|
150 |
+
triangulation = tri.Triangulation(fixed_lons, latitudes)
|
151 |
+
|
152 |
+
# Create the contour plot
|
153 |
contour = ax.tricontourf(triangulation, values, levels=20,
|
154 |
transform=ccrs.PlateCarree(),
|
155 |
cmap='RdBu_r')
|
156 |
+
|
157 |
plt.title(f"{selected_variable} at {state['date']}")
|
158 |
+
plt.colorbar(contour, orientation='horizontal', pad=0.05)
|
159 |
+
|
160 |
return fig
|
161 |
|
162 |
# Create dropdown choices with groups
|
|
|
168 |
for var_id, desc in sorted(variables.items()):
|
169 |
DROPDOWN_CHOICES.append((f"{desc} ({var_id})", var_id))
|
170 |
|
171 |
+
with gr.Blocks(css="""
|
172 |
+
.centered-header {
|
173 |
+
text-align: center;
|
174 |
+
margin-bottom: 20px;
|
175 |
+
}
|
176 |
+
""") as demo:
|
177 |
+
# Centered header section
|
178 |
+
gr.Markdown(f"""
|
179 |
+
# AIFS Weather Forecast
|
180 |
+
|
181 |
+
Interactive visualization of ECMWF AIFS weather forecasts. Starting from the latest available data ({DEFAULT_DATE.strftime('%Y-%m-%d %H:%M UTC')}),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
select how many hours ahead you want to forecast and which meteorological variable to visualize.
|
183 |
+
""", elem_classes=["centered-header"])
|
184 |
+
|
185 |
+
with gr.Row():
|
186 |
+
# Controls column - takes up 1/3 of the width
|
187 |
+
with gr.Column(scale=1):
|
188 |
+
lead_time = gr.Slider(
|
189 |
+
minimum=6,
|
190 |
+
maximum=48,
|
191 |
+
step=6,
|
192 |
+
value=12,
|
193 |
+
label="Forecast Hours Ahead"
|
194 |
+
)
|
195 |
+
variable = gr.Dropdown(
|
196 |
+
choices=DROPDOWN_CHOICES,
|
197 |
+
value="2t",
|
198 |
+
label="Select Variable to Plot",
|
199 |
+
info="Choose a meteorological variable to visualize"
|
200 |
+
)
|
201 |
+
|
202 |
+
# Add buttons in a row
|
203 |
+
with gr.Row():
|
204 |
+
clear_btn = gr.Button("Clear")
|
205 |
+
submit_btn = gr.Button("Submit", variant="primary")
|
206 |
+
|
207 |
+
# Map column - takes up 2/3 of the width
|
208 |
+
with gr.Column(scale=2):
|
209 |
+
plot_output = gr.Plot()
|
210 |
+
|
211 |
+
# Connect the inputs to the forecast function
|
212 |
+
def update_plot(lead_time, variable):
|
213 |
+
state = run_forecast(DEFAULT_DATE, lead_time, "cuda")
|
214 |
+
return plot_forecast(state, variable)
|
215 |
+
|
216 |
+
# Clear function to reset to defaults
|
217 |
+
def clear():
|
218 |
+
return [
|
219 |
+
gr.Slider.update(value=12),
|
220 |
+
gr.Dropdown.update(value="2t"),
|
221 |
+
None # Clear the plot
|
222 |
+
]
|
223 |
+
|
224 |
+
# Connect the buttons
|
225 |
+
submit_btn.click(fn=update_plot, inputs=[lead_time, variable], outputs=plot_output)
|
226 |
+
clear_btn.click(fn=clear, inputs=[], outputs=[lead_time, variable, plot_output])
|
227 |
|
228 |
demo.launch()
|