euler314 commited on
Commit
e28f12c
·
verified ·
1 Parent(s): 09a0f4d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +195 -42
app.py CHANGED
@@ -404,17 +404,98 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
404
  enso_phase = gr.Dropdown(label="ENSO Phase", choices=['all', 'El Nino', 'La Nina', 'Neutral'], value='all')
405
  typhoon_search = gr.Textbox(label="Typhoon Search")
406
  analyze_btn = gr.Button("Generate Tracks")
407
- tracks_plot = gr.Plot(label="Typhoon Tracks")
408
 
409
- # Fixed function that extracts only the first return value
410
- def get_tracks_plot(*args):
411
- results = generate_main_analysis(*args)
412
- return results[0]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
414
  analyze_btn.click(
415
- fn=get_tracks_plot,
416
  inputs=[start_year, start_month, end_year, end_month, enso_phase, typhoon_search],
417
- outputs=tracks_plot
418
  )
419
 
420
  with gr.Tab("Wind Analysis"):
@@ -497,50 +578,95 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
497
  standard_dropdown = gr.Dropdown(label="Classification Standard",
498
  choices=['atlantic', 'taiwan'], value='atlantic')
499
 
500
- # Fix the animation with improved function
501
- def generate_fixed_path_animation(year, typhoon, standard):
502
  if not typhoon:
503
  return None
504
 
505
  typhoon_id = typhoon.split('(')[-1].strip(')')
506
  storm = ibtracs.get_storm(typhoon_id)
507
 
508
- # Create better frames for animation
509
  frames = []
 
 
510
  for i in range(len(storm.time)):
 
511
  category, color = categorize_typhoon_by_standard(storm.vmax[i], standard)
512
- frames.append(
513
- go.Frame(
514
- data=[
515
- go.Scattergeo(
516
- lon=storm.lon[:i+1], lat=storm.lat[:i+1],
517
- mode='lines', line=dict(width=2, color='blue'),
518
- name="Path"
519
- ),
520
- go.Scattergeo(
521
- lon=[storm.lon[i]], lat=[storm.lat[i]],
522
- mode='markers',
523
- marker=dict(size=10, color=color),
524
- name=f"Position at {storm.time[i].strftime('%Y-%m-%d %H:%M')}",
525
- text=f"Wind: {storm.vmax[i]} kt<br>Category: {category}"
526
- )
527
- ],
528
- name=f"frame{i}"
 
 
529
  )
530
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
 
532
- # Initial plot
533
  fig = go.Figure(
534
  data=[
535
  go.Scattergeo(
536
- lon=[storm.lon[0]], lat=[storm.lat[0]],
537
- mode='markers', marker=dict(size=10, color='green'),
538
- name="Start Position"
 
 
 
539
  )
540
  ],
541
  frames=frames
542
  )
543
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
  # Update layout with better animation controls
545
  fig.update_layout(
546
  title=f"{year} {storm.name} Typhoon Path",
@@ -551,13 +677,15 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
551
  landcolor='rgb(243, 243, 243)',
552
  countrycolor='rgb(204, 204, 204)',
553
  coastlinecolor='rgb(204, 204, 204)',
554
- lataxis={'range': [min(storm.lat)-5, max(storm.lat)+5]},
555
- lonaxis={'range': [min(storm.lon)-10, max(storm.lon)+10]}
 
 
556
  ),
557
  updatemenus=[{
558
  "buttons": [
559
  {
560
- "args": [None, {"frame": {"duration": 200, "redraw": True}, "fromcurrent": True, "mode": "immediate"}],
561
  "label": "Play",
562
  "method": "animate"
563
  },
@@ -578,7 +706,10 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
578
  "yanchor": "top",
579
  "xanchor": "left",
580
  "currentvalue": {
581
- "prefix": "Frame: "
 
 
 
582
  },
583
  "pad": {"b": 10, "t": 50},
584
  "len": 0.9,
@@ -590,24 +721,34 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
590
  "frame": {"duration": 0, "redraw": True},
591
  "mode": "immediate"
592
  }],
593
- "label": str(i),
594
  "method": "animate"
595
  } for i, f in enumerate(frames)
596
  ]
597
- }]
 
 
 
 
 
 
 
 
 
598
  )
599
 
600
  return fig
601
 
602
  animate_btn = gr.Button("Generate Animation")
603
- path_plot = gr.Plot(label="Typhoon Path Animation")
604
  animation_info = gr.Markdown("""
605
  ### Animation Instructions
606
  1. Select a year and typhoon from the dropdowns
607
  2. Click "Generate Animation"
608
  3. Use the play button to start the animation
609
- 4. Use the slider to scrub through different positions
610
- 5. If animation doesn't play automatically, try using the slider to view frames
 
611
  """)
612
 
613
  # Year dropdown change function
@@ -619,9 +760,21 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
619
 
620
  year_dropdown.change(fn=update_typhoon_options, inputs=year_dropdown, outputs=typhoon_dropdown)
621
  animate_btn.click(
622
- fn=generate_fixed_path_animation,
623
  inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
624
  outputs=path_plot
625
  )
626
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  demo.launch()
 
404
  enso_phase = gr.Dropdown(label="ENSO Phase", choices=['all', 'El Nino', 'La Nina', 'Neutral'], value='all')
405
  typhoon_search = gr.Textbox(label="Typhoon Search")
406
  analyze_btn = gr.Button("Generate Tracks")
 
407
 
408
+ # Display all tracks
409
+ tracks_plot = gr.Plot(label="Typhoon Tracks", elem_id="tracks_plot")
410
+ typhoon_count = gr.Textbox(label="Number of Typhoons Displayed")
411
+
412
+ # Enhanced function to show all track data
413
+ def get_full_tracks(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
414
+ start_date = datetime(start_year, start_month, 1)
415
+ end_date = datetime(end_year, end_month, 28)
416
+ filtered_data = merged_data[
417
+ (merged_data['ISO_TIME'] >= start_date) &
418
+ (merged_data['ISO_TIME'] <= end_date)
419
+ ]
420
+ filtered_data['ENSO_Phase'] = filtered_data['ONI'].apply(classify_enso_phases)
421
+
422
+ if enso_phase != 'all':
423
+ filtered_data = filtered_data[filtered_data['ENSO_Phase'] == enso_phase.capitalize()]
424
+
425
+ # Get all unique storms
426
+ unique_storms = filtered_data['SID'].unique()
427
+ count = len(unique_storms)
428
+
429
+ # Create the map with all tracks
430
+ fig = go.Figure()
431
+
432
+ # Add all tracks
433
+ for sid in unique_storms:
434
+ storm_data = typhoon_data[typhoon_data['SID'] == sid]
435
+ name = storm_data['NAME'].iloc[0] if not pd.isna(storm_data['NAME'].iloc[0]) else "Unnamed"
436
+
437
+ # Get ENSO phase color
438
+ storm_oni = filtered_data[filtered_data['SID'] == sid]['ONI'].iloc[0]
439
+ color = 'red' if storm_oni >= 0.5 else ('blue' if storm_oni <= -0.5 else 'green')
440
+
441
+ # Add the track line
442
+ fig.add_trace(go.Scattergeo(
443
+ lon=storm_data['LON'],
444
+ lat=storm_data['LAT'],
445
+ mode='lines',
446
+ name=f"{name} ({storm_data['SEASON'].iloc[0]})",
447
+ line=dict(width=1.5, color=color),
448
+ hoverinfo="name"
449
+ ))
450
+
451
+ # Highlight searched typhoon if specified
452
+ if typhoon_search:
453
+ search_mask = typhoon_data['NAME'].str.contains(typhoon_search, case=False, na=False)
454
+ if search_mask.any():
455
+ for sid in typhoon_data[search_mask]['SID'].unique():
456
+ storm_data = typhoon_data[typhoon_data['SID'] == sid]
457
+ fig.add_trace(go.Scattergeo(
458
+ lon=storm_data['LON'],
459
+ lat=storm_data['LAT'],
460
+ mode='lines+markers',
461
+ name=f"MATCHED: {storm_data['NAME'].iloc[0]} ({storm_data['SEASON'].iloc[0]})",
462
+ line=dict(width=3, color='yellow'),
463
+ marker=dict(size=5),
464
+ hoverinfo="name"
465
+ ))
466
+
467
+ # Add colorbar/legend for ENSO phases
468
+ fig.update_layout(
469
+ title=f"Typhoon Tracks ({start_year}-{start_month} to {end_year}-{end_month})",
470
+ geo=dict(
471
+ projection_type='natural earth',
472
+ showland=True,
473
+ showcoastlines=True,
474
+ landcolor='rgb(243, 243, 243)',
475
+ countrycolor='rgb(204, 204, 204)',
476
+ coastlinecolor='rgb(204, 204, 204)',
477
+ center=dict(lon=140, lat=20),
478
+ projection_scale=3
479
+ ),
480
+ legend_title="Typhoons by ENSO Phase",
481
+ showlegend=True,
482
+ height=700
483
+ )
484
+
485
+ # Add annotations explaining colors
486
+ fig.add_annotation(
487
+ x=0.02, y=0.98, xref="paper", yref="paper",
488
+ text="Red: El Niño, Blue: La Niña, Green: Neutral",
489
+ showarrow=False, align="left",
490
+ bgcolor="rgba(255,255,255,0.8)"
491
+ )
492
+
493
+ return fig, f"Total typhoons displayed: {count}"
494
 
495
  analyze_btn.click(
496
+ fn=get_full_tracks,
497
  inputs=[start_year, start_month, end_year, end_month, enso_phase, typhoon_search],
498
+ outputs=[tracks_plot, typhoon_count]
499
  )
500
 
501
  with gr.Tab("Wind Analysis"):
 
578
  standard_dropdown = gr.Dropdown(label="Classification Standard",
579
  choices=['atlantic', 'taiwan'], value='atlantic')
580
 
581
+ # Completely redesigned animation function to show track movement clearly
582
+ def generate_improved_animation(year, typhoon, standard):
583
  if not typhoon:
584
  return None
585
 
586
  typhoon_id = typhoon.split('(')[-1].strip(')')
587
  storm = ibtracs.get_storm(typhoon_id)
588
 
589
+ # Create frames that show the growing track
590
  frames = []
591
+
592
+ # Add frames showing growing track
593
  for i in range(len(storm.time)):
594
+ # Get wind category and color
595
  category, color = categorize_typhoon_by_standard(storm.vmax[i], standard)
596
+
597
+ # Create frame showing path up to current point
598
+ frame_data = [
599
+ # Path line up to current point
600
+ go.Scattergeo(
601
+ lon=storm.lon[:i+1],
602
+ lat=storm.lat[:i+1],
603
+ mode='lines',
604
+ line=dict(width=2, color='blue'),
605
+ name="Track"
606
+ ),
607
+ # Current position
608
+ go.Scattergeo(
609
+ lon=[storm.lon[i]],
610
+ lat=[storm.lat[i]],
611
+ mode='markers',
612
+ marker=dict(size=12, color=color),
613
+ name=f"Current Position",
614
+ text=f"Time: {storm.time[i].strftime('%Y-%m-%d %H:%M')}<br>Wind: {storm.vmax[i]} kt<br>Category: {category}"
615
  )
616
+ ]
617
+
618
+ # Add previous positions as smaller markers
619
+ if i > 0:
620
+ frame_data.append(
621
+ go.Scattergeo(
622
+ lon=storm.lon[:i],
623
+ lat=storm.lat[:i],
624
+ mode='markers',
625
+ marker=dict(size=5, color='rgba(100,100,100,0.5)'),
626
+ name="Previous Positions",
627
+ showlegend=False
628
+ )
629
+ )
630
+
631
+ frames.append(go.Frame(data=frame_data, name=f"frame{i}"))
632
 
633
+ # Initial figure showing start point
634
  fig = go.Figure(
635
  data=[
636
  go.Scattergeo(
637
+ lon=[storm.lon[0]],
638
+ lat=[storm.lat[0]],
639
+ mode='markers',
640
+ marker=dict(size=12, color='green'),
641
+ name="Starting Position",
642
+ text=f"Start: {storm.time[0].strftime('%Y-%m-%d %H:%M')}"
643
  )
644
  ],
645
  frames=frames
646
  )
647
 
648
+ # Add category legend
649
+ if standard == 'atlantic':
650
+ for cat, details in atlantic_standard.items():
651
+ fig.add_trace(go.Scattergeo(
652
+ lon=[None], lat=[None], mode='markers',
653
+ marker=dict(size=10, color=details['color']),
654
+ name=cat
655
+ ))
656
+ else:
657
+ for cat, details in taiwan_standard.items():
658
+ fig.add_trace(go.Scattergeo(
659
+ lon=[None], lat=[None], mode='markers',
660
+ marker=dict(size=10, color=details['color']),
661
+ name=cat
662
+ ))
663
+
664
+ # Focus map on storm area
665
+ min_lat, max_lat = min(storm.lat), max(storm.lat)
666
+ min_lon, max_lon = min(storm.lon), max(storm.lon)
667
+ lat_padding = (max_lat - min_lat) * 0.3 or 5
668
+ lon_padding = (max_lon - min_lon) * 0.3 or 5
669
+
670
  # Update layout with better animation controls
671
  fig.update_layout(
672
  title=f"{year} {storm.name} Typhoon Path",
 
677
  landcolor='rgb(243, 243, 243)',
678
  countrycolor='rgb(204, 204, 204)',
679
  coastlinecolor='rgb(204, 204, 204)',
680
+ showocean=True,
681
+ oceancolor='rgb(230, 230, 255)',
682
+ lataxis={'range': [min_lat - lat_padding, max_lat + lat_padding]},
683
+ lonaxis={'range': [min_lon - lon_padding, max_lon + lon_padding]}
684
  ),
685
  updatemenus=[{
686
  "buttons": [
687
  {
688
+ "args": [None, {"frame": {"duration": 100, "redraw": True}, "fromcurrent": True, "mode": "immediate"}],
689
  "label": "Play",
690
  "method": "animate"
691
  },
 
706
  "yanchor": "top",
707
  "xanchor": "left",
708
  "currentvalue": {
709
+ "font": {"size": 12},
710
+ "prefix": "Time: ",
711
+ "visible": True,
712
+ "xanchor": "right"
713
  },
714
  "pad": {"b": 10, "t": 50},
715
  "len": 0.9,
 
721
  "frame": {"duration": 0, "redraw": True},
722
  "mode": "immediate"
723
  }],
724
+ "label": storm.time[i].strftime('%m/%d %H:00') if i < len(storm.time) else "",
725
  "method": "animate"
726
  } for i, f in enumerate(frames)
727
  ]
728
+ }],
729
+ height=700,
730
+ showlegend=True,
731
+ legend=dict(
732
+ yanchor="top",
733
+ y=0.99,
734
+ xanchor="left",
735
+ x=0.01,
736
+ bgcolor="rgba(255, 255, 255, 0.8)"
737
+ )
738
  )
739
 
740
  return fig
741
 
742
  animate_btn = gr.Button("Generate Animation")
743
+ path_plot = gr.Plot(label="Typhoon Path Animation", elem_id="animation_plot")
744
  animation_info = gr.Markdown("""
745
  ### Animation Instructions
746
  1. Select a year and typhoon from the dropdowns
747
  2. Click "Generate Animation"
748
  3. Use the play button to start the animation
749
+ 4. Use the slider to manually control the animation timeline
750
+ 5. The animation shows the typhoon track developing over time, with the current position highlighted
751
+ 6. Colors indicate typhoon intensity according to the selected classification standard
752
  """)
753
 
754
  # Year dropdown change function
 
760
 
761
  year_dropdown.change(fn=update_typhoon_options, inputs=year_dropdown, outputs=typhoon_dropdown)
762
  animate_btn.click(
763
+ fn=generate_improved_animation,
764
  inputs=[year_dropdown, typhoon_dropdown, standard_dropdown],
765
  outputs=path_plot
766
  )
767
 
768
+ # Custom CSS for better spacing and to ensure plots are visible
769
+ gr.HTML("""
770
+ <style>
771
+ #tracks_plot, #animation_plot {
772
+ height: 700px !important;
773
+ }
774
+ .plot-container {
775
+ min-height: 600px;
776
+ }
777
+ </style>
778
+ """)
779
+
780
  demo.launch()