euler314 commited on
Commit
15b9748
·
verified ·
1 Parent(s): e28f12c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +170 -223
app.py CHANGED
@@ -294,29 +294,6 @@ def generate_main_analysis(start_year, start_month, end_year, end_month, enso_ph
294
  return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
295
 
296
  # Path animation function
297
- def generate_path_animation(year, typhoon, standard):
298
- typhoon_id = typhoon.split('(')[-1].strip(')')
299
- storm = ibtracs.get_storm(typhoon_id)
300
- fig = go.Figure()
301
- fig.add_trace(go.Scattergeo(lon=storm.lon, lat=storm.lat, mode='lines', line=dict(width=2, color='gray')))
302
- fig.add_trace(go.Scattergeo(lon=[storm.lon[0]], lat=[storm.lat[0]], mode='markers',
303
- marker=dict(size=10, color='green', symbol='star'), name='Start'))
304
- frames = [
305
- go.Frame(data=[
306
- go.Scattergeo(lon=storm.lon[:i+1], lat=storm.lat[:i+1], mode='lines', line=dict(width=2, color='blue')),
307
- go.Scattergeo(lon=[storm.lon[i]], lat=[storm.lat[i]], mode='markers',
308
- marker=dict(size=10, color=categorize_typhoon_by_standard(storm.vmax[i], standard)[1]))
309
- ], name=f"frame{i}") for i in range(len(storm.time))
310
- ]
311
- fig.frames = frames
312
- fig.update_layout(
313
- title=f"{year} {storm.name} Typhoon Path",
314
- geo=dict(projection_type='natural earth', showland=True),
315
- updatemenus=[{"buttons": [{"label": "Play", "method": "animate", "args": [None, {"frame": {"duration": 100}}]},
316
- {"label": "Pause", "method": "animate", "args": [[None], {"mode": "immediate"}]}]}]
317
- )
318
- return fig
319
-
320
  def categorize_typhoon_by_standard(wind_speed, standard):
321
  if standard == 'taiwan':
322
  wind_speed_ms = wind_speed * 0.514444
@@ -342,6 +319,164 @@ def categorize_typhoon_by_standard(wind_speed, standard):
342
  return 'Tropical Storm', atlantic_standard['Tropical Storm']['color']
343
  return 'Tropical Depression', atlantic_standard['Tropical Depression']['color']
344
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
  # Logistic regression functions
346
  def perform_wind_regression(start_year, start_month, end_year, end_month):
347
  start_date = datetime(start_year, start_month, 1)
@@ -404,12 +539,9 @@ 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
-
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)
@@ -418,53 +550,34 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
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(
@@ -481,15 +594,12 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
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(
@@ -510,7 +620,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
510
  wind_scatter = gr.Plot(label="Wind Speed vs ONI")
511
  wind_regression_results = gr.Textbox(label="Wind Regression Results")
512
 
513
- # Fixed function for wind analysis
514
  def get_wind_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
515
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
516
  regression = perform_wind_regression(start_year, start_month, end_year, end_month)
@@ -534,7 +643,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
534
  pressure_scatter = gr.Plot(label="Pressure vs ONI")
535
  pressure_regression_results = gr.Textbox(label="Pressure Regression Results")
536
 
537
- # Fixed function for pressure analysis
538
  def get_pressure_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
539
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
540
  regression = perform_pressure_regression(start_year, start_month, end_year, end_month)
@@ -559,7 +667,6 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
559
  slopes_text = gr.Textbox(label="Regression Slopes")
560
  lon_regression_results = gr.Textbox(label="Longitude Regression Results")
561
 
562
- # Fixed function for longitude analysis
563
  def get_longitude_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
564
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
565
  regression = perform_longitude_regression(start_year, start_month, end_year, end_month)
@@ -575,183 +682,19 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
575
  with gr.Row():
576
  year_dropdown = gr.Dropdown(label="Year", choices=[str(y) for y in range(1950, 2025)], value="2024")
577
  typhoon_dropdown = gr.Dropdown(label="Typhoon")
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",
673
- geo=dict(
674
- projection_type='natural earth',
675
- showland=True,
676
- showcoastlines=True,
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
- },
692
- {
693
- "args": [[None], {"frame": {"duration": 0, "redraw": True}, "mode": "immediate"}],
694
- "label": "Pause",
695
- "method": "animate"
696
- }
697
- ],
698
- "direction": "left",
699
- "pad": {"r": 10, "t": 10},
700
- "type": "buttons",
701
- "x": 0.1,
702
- "y": 0
703
- }],
704
- sliders=[{
705
- "active": 0,
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,
716
- "x": 0.1,
717
- "y": 0,
718
- "steps": [
719
- {
720
- "args": [[f.name], {
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
755
  def update_typhoon_options(year):
756
  season = ibtracs.get_season(int(year))
757
  storm_summary = season.summary()
@@ -765,15 +708,19 @@ with gr.Blocks(title="Typhoon Analysis Dashboard") as demo:
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
 
 
294
  return tracks_fig, wind_scatter, pressure_scatter, regression_fig, slopes_text
295
 
296
  # Path animation function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  def categorize_typhoon_by_standard(wind_speed, standard):
298
  if standard == 'taiwan':
299
  wind_speed_ms = wind_speed * 0.514444
 
319
  return 'Tropical Storm', atlantic_standard['Tropical Storm']['color']
320
  return 'Tropical Depression', atlantic_standard['Tropical Depression']['color']
321
 
322
+ def generate_improved_animation(year, typhoon, standard):
323
+ if not typhoon:
324
+ return None
325
+
326
+ typhoon_id = typhoon.split('(')[-1].strip(')')
327
+ storm = ibtracs.get_storm(typhoon_id)
328
+
329
+ # Create frames for animation
330
+ frames = []
331
+ for i in range(len(storm.time)):
332
+ category, color = categorize_typhoon_by_standard(storm.vmax[i], standard)
333
+ frame_data = [
334
+ go.Scattergeo(
335
+ lon=storm.lon[:i+1],
336
+ lat=storm.lat[:i+1],
337
+ mode='lines',
338
+ line=dict(width=2, color='blue'),
339
+ name="Track",
340
+ hoverinfo="none"
341
+ ),
342
+ go.Scattergeo(
343
+ lon=[storm.lon[i]],
344
+ lat=[storm.lat[i]],
345
+ mode='markers',
346
+ marker=dict(size=12, color=color),
347
+ name=f"Current Position",
348
+ text=[f"Time: {storm.time[i].strftime('%Y-%m-%d %H:%M')}<br>Wind: {storm.vmax[i]:.1f} kt<br>Category: {category}"],
349
+ hoverinfo="text"
350
+ )
351
+ ]
352
+ if i > 0:
353
+ frame_data.append(
354
+ go.Scattergeo(
355
+ lon=storm.lon[:i],
356
+ lat=storm.lat[:i],
357
+ mode='markers',
358
+ marker=dict(size=5, color='rgba(100,100,100,0.5)'),
359
+ name="Previous Positions",
360
+ hoverinfo="none",
361
+ showlegend=False
362
+ )
363
+ )
364
+ frames.append(go.Frame(data=frame_data, name=str(i)))
365
+
366
+ # Initial figure
367
+ fig = go.Figure(
368
+ data=[
369
+ go.Scattergeo(
370
+ lon=[storm.lon[0]],
371
+ lat=[storm.lat[0]],
372
+ mode='markers',
373
+ marker=dict(size=12, color='green'),
374
+ name="Start",
375
+ text=[f"Start: {storm.time[0].strftime('%Y-%m-%d %H:%M')}"],
376
+ hoverinfo="text"
377
+ )
378
+ ],
379
+ frames=frames
380
+ )
381
+
382
+ # Add category legend
383
+ standard_dict = atlantic_standard if standard == 'atlantic' else taiwan_standard
384
+ for cat, details in standard_dict.items():
385
+ fig.add_trace(go.Scattergeo(
386
+ lon=[None], lat=[None], mode='markers',
387
+ marker=dict(size=10, color=details['color']),
388
+ name=cat,
389
+ showlegend=True
390
+ ))
391
+
392
+ # Map focus
393
+ min_lat, max_lat = min(storm.lat), max(storm.lat)
394
+ min_lon, max_lon = min(storm.lon), max(storm.lon)
395
+ lat_padding = max((max_lat - min_lat) * 0.3, 5)
396
+ lon_padding = max((max_lon - min_lon) * 0.3, 5)
397
+
398
+ # Update layout with animation controls
399
+ fig.update_layout(
400
+ title=f"{year} {storm.name} Typhoon Path",
401
+ geo=dict(
402
+ projection_type='natural earth',
403
+ showland=True,
404
+ showcoastlines=True,
405
+ landcolor='rgb(243, 243, 243)',
406
+ countrycolor='rgb(204, 204, 204)',
407
+ coastlinecolor='rgb(204, 204, 204)',
408
+ showocean=True,
409
+ oceancolor='rgb(230, 230, 255)',
410
+ lataxis={'range': [min_lat - lat_padding, max_lat + lat_padding]},
411
+ lonaxis={'range': [min_lon - lon_padding, max_lon + lon_padding]}
412
+ ),
413
+ updatemenus=[{
414
+ "buttons": [
415
+ {
416
+ "args": [None, {"frame": {"duration": 200, "redraw": True},
417
+ "fromcurrent": True,
418
+ "transition": {"duration": 0},
419
+ "mode": "immediate"}],
420
+ "label": "Play",
421
+ "method": "animate"
422
+ },
423
+ {
424
+ "args": [[None], {"frame": {"duration": 0, "redraw": True},
425
+ "mode": "immediate",
426
+ "transition": {"duration": 0}}],
427
+ "label": "Pause",
428
+ "method": "animate"
429
+ }
430
+ ],
431
+ "direction": "left",
432
+ "pad": {"r": 10, "t": 10},
433
+ "showactive": True,
434
+ "type": "buttons",
435
+ "x": 0.1,
436
+ "xanchor": "left",
437
+ "y": 0,
438
+ "yanchor": "bottom"
439
+ }],
440
+ sliders=[{
441
+ "active": 0,
442
+ "yanchor": "top",
443
+ "xanchor": "left",
444
+ "currentvalue": {
445
+ "font": {"size": 12},
446
+ "prefix": "Time: ",
447
+ "visible": True,
448
+ "xanchor": "right"
449
+ },
450
+ "transition": {"duration": 0},
451
+ "pad": {"b": 10, "t": 50},
452
+ "len": 0.9,
453
+ "x": 0.1,
454
+ "y": 0,
455
+ "steps": [
456
+ {
457
+ "args": [[str(i)], {"frame": {"duration": 200, "redraw": True},
458
+ "mode": "immediate",
459
+ "transition": {"duration": 0}}],
460
+ "label": storm.time[i].strftime('%m/%d %H:%M') if i < len(storm.time) else "",
461
+ "method": "animate"
462
+ } for i in range(len(storm.time))
463
+ ]
464
+ }],
465
+ height=700,
466
+ showlegend=True,
467
+ legend=dict(
468
+ yanchor="top",
469
+ y=0.99,
470
+ xanchor="left",
471
+ x=0.01,
472
+ bgcolor="rgba(255, 255, 255, 0.8)"
473
+ ),
474
+ # Ensure animation starts automatically
475
+ autosize=True
476
+ )
477
+
478
+ return fig
479
+
480
  # Logistic regression functions
481
  def perform_wind_regression(start_year, start_month, end_year, end_month):
482
  start_date = datetime(start_year, start_month, 1)
 
539
  enso_phase = gr.Dropdown(label="ENSO Phase", choices=['all', 'El Nino', 'La Nina', 'Neutral'], value='all')
540
  typhoon_search = gr.Textbox(label="Typhoon Search")
541
  analyze_btn = gr.Button("Generate Tracks")
 
 
542
  tracks_plot = gr.Plot(label="Typhoon Tracks", elem_id="tracks_plot")
543
  typhoon_count = gr.Textbox(label="Number of Typhoons Displayed")
544
 
 
545
  def get_full_tracks(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
546
  start_date = datetime(start_year, start_month, 1)
547
  end_date = datetime(end_year, end_month, 28)
 
550
  (merged_data['ISO_TIME'] <= end_date)
551
  ]
552
  filtered_data['ENSO_Phase'] = filtered_data['ONI'].apply(classify_enso_phases)
 
553
  if enso_phase != 'all':
554
  filtered_data = filtered_data[filtered_data['ENSO_Phase'] == enso_phase.capitalize()]
 
 
555
  unique_storms = filtered_data['SID'].unique()
556
  count = len(unique_storms)
 
 
557
  fig = go.Figure()
 
 
558
  for sid in unique_storms:
559
  storm_data = typhoon_data[typhoon_data['SID'] == sid]
560
  name = storm_data['NAME'].iloc[0] if not pd.isna(storm_data['NAME'].iloc[0]) else "Unnamed"
 
 
561
  storm_oni = filtered_data[filtered_data['SID'] == sid]['ONI'].iloc[0]
562
  color = 'red' if storm_oni >= 0.5 else ('blue' if storm_oni <= -0.5 else 'green')
 
 
563
  fig.add_trace(go.Scattergeo(
564
+ lon=storm_data['LON'], lat=storm_data['LAT'], mode='lines',
 
 
565
  name=f"{name} ({storm_data['SEASON'].iloc[0]})",
566
  line=dict(width=1.5, color=color),
567
  hoverinfo="name"
568
  ))
 
 
569
  if typhoon_search:
570
  search_mask = typhoon_data['NAME'].str.contains(typhoon_search, case=False, na=False)
571
  if search_mask.any():
572
  for sid in typhoon_data[search_mask]['SID'].unique():
573
  storm_data = typhoon_data[typhoon_data['SID'] == sid]
574
  fig.add_trace(go.Scattergeo(
575
+ lon=storm_data['LON'], lat=storm_data['LAT'], mode='lines+markers',
 
 
576
  name=f"MATCHED: {storm_data['NAME'].iloc[0]} ({storm_data['SEASON'].iloc[0]})",
577
  line=dict(width=3, color='yellow'),
578
  marker=dict(size=5),
579
  hoverinfo="name"
580
  ))
 
 
581
  fig.update_layout(
582
  title=f"Typhoon Tracks ({start_year}-{start_month} to {end_year}-{end_month})",
583
  geo=dict(
 
594
  showlegend=True,
595
  height=700
596
  )
 
 
597
  fig.add_annotation(
598
  x=0.02, y=0.98, xref="paper", yref="paper",
599
  text="Red: El Niño, Blue: La Niña, Green: Neutral",
600
  showarrow=False, align="left",
601
  bgcolor="rgba(255,255,255,0.8)"
602
  )
 
603
  return fig, f"Total typhoons displayed: {count}"
604
 
605
  analyze_btn.click(
 
620
  wind_scatter = gr.Plot(label="Wind Speed vs ONI")
621
  wind_regression_results = gr.Textbox(label="Wind Regression Results")
622
 
 
623
  def get_wind_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
624
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
625
  regression = perform_wind_regression(start_year, start_month, end_year, end_month)
 
643
  pressure_scatter = gr.Plot(label="Pressure vs ONI")
644
  pressure_regression_results = gr.Textbox(label="Pressure Regression Results")
645
 
 
646
  def get_pressure_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
647
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
648
  regression = perform_pressure_regression(start_year, start_month, end_year, end_month)
 
667
  slopes_text = gr.Textbox(label="Regression Slopes")
668
  lon_regression_results = gr.Textbox(label="Longitude Regression Results")
669
 
 
670
  def get_longitude_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search):
671
  results = generate_main_analysis(start_year, start_month, end_year, end_month, enso_phase, typhoon_search)
672
  regression = perform_longitude_regression(start_year, start_month, end_year, end_month)
 
682
  with gr.Row():
683
  year_dropdown = gr.Dropdown(label="Year", choices=[str(y) for y in range(1950, 2025)], value="2024")
684
  typhoon_dropdown = gr.Dropdown(label="Typhoon")
685
+ standard_dropdown = gr.Dropdown(label="Classification Standard", choices=['atlantic', 'taiwan'], value='atlantic')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
 
687
  animate_btn = gr.Button("Generate Animation")
688
  path_plot = gr.Plot(label="Typhoon Path Animation", elem_id="animation_plot")
689
  animation_info = gr.Markdown("""
690
  ### Animation Instructions
691
  1. Select a year and typhoon from the dropdowns
692
+ 2. Choose a classification standard (Atlantic or Taiwan)
693
+ 3. Click "Generate Animation"
694
+ 4. Use the "Play" button to start the animation or the slider to navigate through the typhoon's path
695
+ 5. Colors indicate typhoon intensity according to the selected standard
 
696
  """)
697
 
 
698
  def update_typhoon_options(year):
699
  season = ibtracs.get_season(int(year))
700
  storm_summary = season.summary()
 
708
  outputs=path_plot
709
  )
710
 
711
+ # Custom CSS for better spacing and visibility
712
  gr.HTML("""
713
  <style>
714
  #tracks_plot, #animation_plot {
715
  height: 700px !important;
716
+ width: 100%;
717
  }
718
  .plot-container {
719
  min-height: 600px;
720
  }
721
+ .gr-plotly {
722
+ width: 100% !important;
723
+ }
724
  </style>
725
  """)
726