euler314 commited on
Commit
bb0d660
·
verified ·
1 Parent(s): 757dba4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -51
app.py CHANGED
@@ -16,6 +16,8 @@ import csv
16
  from collections import defaultdict
17
  import filecmp
18
  import uuid
 
 
19
 
20
  # Command-line argument parsing
21
  parser = argparse.ArgumentParser(description='Typhoon Analysis Dashboard')
@@ -294,7 +296,7 @@ def generate_main_analysis(start_year, start_month, end_year, end_month, enso_ph
294
 
295
  return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
296
 
297
- # Path animation function using Gallery with image export
298
  def categorize_typhoon_by_standard(wind_speed, standard):
299
  if standard == 'taiwan':
300
  wind_speed_ms = wind_speed * 0.514444
@@ -320,9 +322,9 @@ def categorize_typhoon_by_standard(wind_speed, standard):
320
  return 'Tropical Storm', atlantic_standard['Tropical Storm']['color']
321
  return 'Tropical Depression', atlantic_standard['Tropical Depression']['color']
322
 
323
- def generate_track_gallery(year, typhoon, standard):
324
  if not typhoon:
325
- return []
326
 
327
  typhoon_id = typhoon.split('(')[-1].strip(')')
328
  storm = ibtracs.get_storm(typhoon_id)
@@ -333,15 +335,11 @@ def generate_track_gallery(year, typhoon, standard):
333
  lat_padding = max((max_lat - min_lat) * 0.3, 5)
334
  lon_padding = max((max_lon - min_lon) * 0.3, 5)
335
 
336
- # Temporary directory for images
337
- temp_dir = tempfile.mkdtemp()
338
- gallery = []
339
-
340
- # Generate a sequence of figures and save as images
341
  for i in range(len(storm.time)):
342
  fig = go.Figure()
343
-
344
- # Add the growing track up to the current point
345
  category, color = categorize_typhoon_by_standard(storm.vmax[i], standard)
346
  fig.add_trace(go.Scattergeo(
347
  lon=storm.lon[:i+1],
@@ -353,8 +351,6 @@ def generate_track_gallery(year, typhoon, standard):
353
  text=[f"Time: {storm.time[j].strftime('%Y-%m-%d %H:%M')}<br>Wind: {storm.vmax[j]:.1f} kt<br>Category: {categorize_typhoon_by_standard(storm.vmax[j], standard)[0]}" for j in range(i+1)],
354
  hoverinfo="text"
355
  ))
356
-
357
- # Add category legend
358
  standard_dict = atlantic_standard if standard == 'atlantic' else taiwan_standard
359
  for cat, details in standard_dict.items():
360
  fig.add_trace(go.Scattergeo(
@@ -363,10 +359,8 @@ def generate_track_gallery(year, typhoon, standard):
363
  name=cat,
364
  showlegend=True
365
  ))
366
-
367
- # Update layout
368
  fig.update_layout(
369
- title=f"{year} {storm.name} at {storm.time[i].strftime('%Y-%m-%d %H:%M')}",
370
  geo=dict(
371
  projection_type='natural earth',
372
  showland=True,
@@ -389,13 +383,53 @@ def generate_track_gallery(year, typhoon, standard):
389
  bgcolor="rgba(255, 255, 255, 0.8)"
390
  )
391
  )
392
-
393
- # Save figure as image
394
- image_path = os.path.join(temp_dir, f"frame_{i}_{uuid.uuid4()}.png")
395
- fig.write_image(image_path, width=1000, height=700)
396
- gallery.append(image_path)
397
-
398
- return gallery
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
  # Logistic regression functions
401
  def perform_wind_regression(start_year, start_month, end_year, end_month):
@@ -444,7 +478,7 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
444
  ### Features:
445
  - **Track Visualization**: View typhoon tracks by time period and ENSO phase
446
  - **Statistical Analysis**: Examine relationships between ONI values and typhoon characteristics
447
- - **Path Animation**: View a gallery of typhoon path progression over time
448
  - **Regression Analysis**: Perform statistical regression on typhoon data
449
 
450
  Select a tab above to begin your analysis.
@@ -604,15 +638,16 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
604
  typhoon_dropdown = gr.Dropdown(label="Typhoon")
605
  standard_dropdown = gr.Dropdown(label="Classification Standard", choices=['atlantic', 'taiwan'], value='atlantic')
606
 
607
- animate_btn = gr.Button("Generate Animation Gallery")
608
- path_gallery = gr.Gallery(label="Typhoon Path Progression", elem_id="path_gallery", columns=1, height="auto")
609
  animation_info = gr.Markdown("""
610
  ### Animation Instructions
611
  1. Select a year and typhoon from the dropdowns
612
  2. Choose a classification standard (Atlantic or Taiwan)
613
- 3. Click "Generate Animation Gallery"
614
- 4. Scroll through the gallery to see the typhoon track growing over time
615
- 5. Each image represents a step in the typhoon's path, with the blue line extending and markers showing intensity
 
616
  """)
617
 
618
  def update_typhoon_options(year):
@@ -623,29 +658,9 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
623
 
624
  year_dropdown.change(fn=update_typhoon_options, inputs=year_dropdown, outputs=typhoon_dropdown)
625
  animate_btn.click(
626
- fn=generate_track_gallery,
627
  inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
628
- outputs=path_gallery
629
  )
630
 
631
- # Custom CSS for better spacing and visibility
632
- gr.HTML("""
633
- <style>
634
- #tracks_plot {
635
- height: 700px !important;
636
- width: 100%;
637
- }
638
- #path_gallery .gallery-item {
639
- height: 700px !important;
640
- width: 100% !important;
641
- }
642
- .plot-container {
643
- min-height: 600px;
644
- }
645
- .gr-plotly {
646
- width: 100% !important;
647
- }
648
- </style>
649
- """)
650
-
651
- demo.launch(share=True) # Enable public link sharing
 
16
  from collections import defaultdict
17
  import filecmp
18
  import uuid
19
+ import base64
20
+ from io import BytesIO
21
 
22
  # Command-line argument parsing
23
  parser = argparse.ArgumentParser(description='Typhoon Analysis Dashboard')
 
296
 
297
  return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
298
 
299
+ # Custom animation function with HTML and JavaScript
300
  def categorize_typhoon_by_standard(wind_speed, standard):
301
  if standard == 'taiwan':
302
  wind_speed_ms = wind_speed * 0.514444
 
322
  return 'Tropical Storm', atlantic_standard['Tropical Storm']['color']
323
  return 'Tropical Depression', atlantic_standard['Tropical Depression']['color']
324
 
325
+ def generate_track_animation(year, typhoon, standard):
326
  if not typhoon:
327
+ return "<p>No typhoon selected.</p>"
328
 
329
  typhoon_id = typhoon.split('(')[-1].strip(')')
330
  storm = ibtracs.get_storm(typhoon_id)
 
335
  lat_padding = max((max_lat - min_lat) * 0.3, 5)
336
  lon_padding = max((max_lon - min_lon) * 0.3, 5)
337
 
338
+ # Generate frames as images and encode them in base64
339
+ frames = []
340
+ dates = []
 
 
341
  for i in range(len(storm.time)):
342
  fig = go.Figure()
 
 
343
  category, color = categorize_typhoon_by_standard(storm.vmax[i], standard)
344
  fig.add_trace(go.Scattergeo(
345
  lon=storm.lon[:i+1],
 
351
  text=[f"Time: {storm.time[j].strftime('%Y-%m-%d %H:%M')}<br>Wind: {storm.vmax[j]:.1f} kt<br>Category: {categorize_typhoon_by_standard(storm.vmax[j], standard)[0]}" for j in range(i+1)],
352
  hoverinfo="text"
353
  ))
 
 
354
  standard_dict = atlantic_standard if standard == 'atlantic' else taiwan_standard
355
  for cat, details in standard_dict.items():
356
  fig.add_trace(go.Scattergeo(
 
359
  name=cat,
360
  showlegend=True
361
  ))
 
 
362
  fig.update_layout(
363
+ title=f"{year} {storm.name}",
364
  geo=dict(
365
  projection_type='natural earth',
366
  showland=True,
 
383
  bgcolor="rgba(255, 255, 255, 0.8)"
384
  )
385
  )
386
+ # Convert figure to PNG and encode in base64
387
+ img_bytes = fig.to_image(format="png", width=1000, height=700)
388
+ img_base64 = base64.b64encode(img_bytes).decode('utf-8')
389
+ frames.append(f"data:image/png;base64,{img_base64}")
390
+ dates.append(storm.time[i].strftime('%Y-%m-%d %H:%M'))
391
+
392
+ # JavaScript to handle animation
393
+ js_script = """
394
+ <div style="text-align: center;">
395
+ <img id="animationFrame" style="max-width: 100%; height: 700px;" src="{frames[0]}">
396
+ <p id="dateDisplay" style="font-size: 18px; margin: 10px;">{dates[0]}</p>
397
+ <button id="startBtn" style="margin: 5px;">Start</button>
398
+ <button id="pauseBtn" style="margin: 5px;">Pause</button>
399
+ </div>
400
+ <script>
401
+ const frames = {frames_json};
402
+ const dates = {dates_json};
403
+ let currentFrame = 0;
404
+ let intervalId = null;
405
+
406
+ function updateFrame() {{
407
+ document.getElementById('animationFrame').src = frames[currentFrame];
408
+ document.getElementById('dateDisplay').textContent = dates[currentFrame];
409
+ currentFrame = (currentFrame + 1) % frames.length;
410
+ }}
411
+
412
+ document.getElementById('startBtn').addEventListener('click', function() {{
413
+ if (!intervalId) {{
414
+ intervalId = setInterval(updateFrame, 200); // 200ms per frame
415
+ }}
416
+ }});
417
+
418
+ document.getElementById('pauseBtn').addEventListener('click', function() {{
419
+ if (intervalId) {{
420
+ clearInterval(intervalId);
421
+ intervalId = null;
422
+ }}
423
+ }});
424
+ </script>
425
+ """.format(
426
+ frames_json=str(frames).replace("'", '"'), # JSON-safe string
427
+ dates_json=str(dates).replace("'", '"'),
428
+ frames=frames[0], # Initial frame
429
+ dates=dates[0] # Initial date
430
+ )
431
+
432
+ return js_script
433
 
434
  # Logistic regression functions
435
  def perform_wind_regression(start_year, start_month, end_year, end_month):
 
478
  ### Features:
479
  - **Track Visualization**: View typhoon tracks by time period and ENSO phase
480
  - **Statistical Analysis**: Examine relationships between ONI values and typhoon characteristics
481
+ - **Path Animation**: Watch an animated typhoon path with start/pause controls
482
  - **Regression Analysis**: Perform statistical regression on typhoon data
483
 
484
  Select a tab above to begin your analysis.
 
638
  typhoon_dropdown = gr.Dropdown(label="Typhoon")
639
  standard_dropdown = gr.Dropdown(label="Classification Standard", choices=['atlantic', 'taiwan'], value='atlantic')
640
 
641
+ animate_btn = gr.Button("Generate Animation")
642
+ path_animation = gr.HTML(label="Typhoon Path Animation")
643
  animation_info = gr.Markdown("""
644
  ### Animation Instructions
645
  1. Select a year and typhoon from the dropdowns
646
  2. Choose a classification standard (Atlantic or Taiwan)
647
+ 3. Click "Generate Animation"
648
+ 4. Use the "Start" button to begin the animation and "Pause" to stop it
649
+ 5. The date below the image updates with each frame
650
+ 6. The blue line grows to show the typhoon's path, with markers indicating intensity
651
  """)
652
 
653
  def update_typhoon_options(year):
 
658
 
659
  year_dropdown.change(fn=update_typhoon_options, inputs=year_dropdown, outputs=typhoon_dropdown)
660
  animate_btn.click(
661
+ fn=generate_track_animation,
662
  inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
663
+ outputs=path_animation
664
  )
665
 
666
+ demo.launch(share=True)