euler314 commited on
Commit
4114999
·
verified ·
1 Parent(s): 6485687

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -153
app.py CHANGED
@@ -354,159 +354,159 @@ class TyphoonAnalyzer:
354
  }
355
 
356
  def create_tracks_plot(self, data):
357
- """Create typhoon tracks visualization"""
358
- fig = go.Figure()
359
-
360
- fig.update_layout(
361
- title={
362
- 'text': 'Typhoon Tracks',
363
- 'y':0.95,
364
- 'x':0.5,
365
- 'xanchor': 'center',
366
- 'yanchor': 'top'
367
- },
368
- showlegend=True,
369
- legend=dict(
370
- yanchor="top",
371
- y=0.99,
372
- xanchor="left",
373
- x=0.01,
374
- bgcolor='rgba(255, 255, 255, 0.8)'
375
- ),
376
- geo=dict(
377
- projection_type='mercator',
378
- showland=True,
379
- showcoastlines=True,
380
- landcolor='rgb(243, 243, 243)',
381
- countrycolor='rgb(204, 204, 204)',
382
- coastlinecolor='rgb(214, 214, 214)',
383
- showocean=True,
384
- oceancolor='rgb(230, 250, 255)',
385
- showlakes=True,
386
- lakecolor='rgb(230, 250, 255)',
387
- lataxis=dict(range=[0, 50]),
388
- lonaxis=dict(range=[100, 180]),
389
- center=dict(lat=20, lon=140),
390
- bgcolor='rgba(255, 255, 255, 0.5)'
391
- ),
392
- paper_bgcolor='rgba(255, 255, 255, 0.5)',
393
- plot_bgcolor='rgba(255, 255, 255, 0.5)'
394
- )
395
-
396
- for category in COLOR_MAP.keys():
397
- category_data = data[data['Category'] == category]
398
- for _, storm in category_data.groupby('SID'):
399
- track_data = self.typhoon_data[self.typhoon_data['SID'] == storm['SID'].iloc[0]]
400
- track_data = track_data.sort_values('ISO_TIME')
401
-
402
- fig.add_trace(go.Scattergeo(
403
- lon=track_data['LON'],
404
- lat=track_data['LAT'],
405
- mode='lines',
406
- line=dict(
407
- width=2,
408
- color=COLOR_MAP[category]
409
- ),
410
- name=category,
411
- legendgroup=category,
412
- showlegend=True if storm.iloc[0]['SID'] == category_data.iloc[0]['SID'] else False,
413
- hovertemplate=(
414
- f"Name: {storm['NAME'].iloc[0]}<br>" +
415
- f"Category: {category}<br>" +
416
- f"Wind Speed: {storm['USA_WIND'].iloc[0]:.1f} kt<br>" +
417
- f"Pressure: {storm['WMO_PRES'].iloc[0]:.1f} hPa<br>" +
418
- f"Date: {track_data['ISO_TIME'].dt.strftime('%Y-%m-%d %H:%M').iloc[0]}<br>" +
419
- f"Lat: {track_data['LAT'].iloc[0]:.2f}°N<br>" +
420
- f"Lon: {track_data['LON'].iloc[0]:.2f}°E<br>" +
421
- "<extra></extra>"
422
- )
423
- ))
424
-
425
- return fig
426
-
427
- def analyze_clusters(self, year, n_clusters):
428
- """Analyze typhoon clusters for a specific year"""
429
- year_data = self.typhoon_data[self.typhoon_data['SEASON'] == year]
430
- if year_data.empty:
431
- return go.Figure(), "No data available for selected year"
432
-
433
- # Prepare data for clustering
434
- routes = []
435
- for _, storm in year_data.groupby('SID'):
436
- if len(storm) > 1:
437
- # Standardize route length
438
- t = np.linspace(0, 1, len(storm))
439
- t_new = np.linspace(0, 1, 100)
440
- lon_interp = interp1d(t, storm['LON'], kind='linear')(t_new)
441
- lat_interp = interp1d(t, storm['LAT'], kind='linear')(t_new)
442
- routes.append(np.column_stack((lon_interp, lat_interp)))
443
-
444
- if len(routes) < n_clusters:
445
- return go.Figure(), f"Not enough typhoons ({len(routes)}) for {n_clusters} clusters"
446
-
447
- # Perform clustering
448
- routes_array = np.array(routes)
449
- routes_reshaped = routes_array.reshape(routes_array.shape[0], -1)
450
- kmeans = KMeans(n_clusters=n_clusters, random_state=42)
451
- clusters = kmeans.fit_predict(routes_reshaped)
452
-
453
- # Create visualization
454
- fig = go.Figure()
455
-
456
- # Set layout
457
- fig.update_layout(
458
- title=f'Typhoon Route Clusters ({year})',
459
- showlegend=True,
460
- geo=dict(
461
- projection_type='mercator',
462
- showland=True,
463
- showcoastlines=True,
464
- landcolor='rgb(243, 243, 243)',
465
- countrycolor='rgb(204, 204, 204)',
466
- coastlinecolor='rgb(214, 214, 214)',
467
- showocean=True,
468
- oceancolor='rgb(230, 250, 255)',
469
- lataxis=dict(range=[0, 50]),
470
- lonaxis=dict(range=[100, 180]),
471
- center=dict(lat=20, lon=140)
472
- )
473
- )
474
-
475
- # Plot routes colored by cluster
476
- for route, cluster_id in zip(routes, clusters):
477
- fig.add_trace(go.Scattergeo(
478
- lon=route[:, 0],
479
- lat=route[:, 1],
480
- mode='lines',
481
- line=dict(
482
- width=1,
483
- color=f'hsl({cluster_id * 360/n_clusters}, 50%, 50%)'
484
- ),
485
- name=f'Cluster {cluster_id + 1}',
486
- showlegend=False
487
- ))
488
-
489
- # Plot cluster centers
490
- for i in range(n_clusters):
491
- center = kmeans.cluster_centers_[i].reshape(-1, 2)
492
- fig.add_trace(go.Scattergeo(
493
- lon=center[:, 0],
494
- lat=center[:, 1],
495
- mode='lines',
496
- name=f'Cluster {i+1} Center',
497
- line=dict(
498
- width=3,
499
- color=f'hsl({i * 360/n_clusters}, 100%, 50%)'
500
- )
501
- ))
502
-
503
- # Generate statistics text
504
- stats_text = "### Clustering Results\n\n"
505
- cluster_counts = np.bincount(clusters)
506
- for i in range(n_clusters):
507
- stats_text += f"- Cluster {i+1}: {cluster_counts[i]} typhoons\n"
508
-
509
- return fig, stats_text
510
  def get_typhoons_for_year(self, year):
511
  """Get list of typhoons for a specific year"""
512
  year_data = self.typhoon_data[self.typhoon_data['ISO_TIME'].dt.year == year]
 
354
  }
355
 
356
  def create_tracks_plot(self, data):
357
+ """Create typhoon tracks visualization"""
358
+ fig = go.Figure()
359
+
360
+ fig.update_layout(
361
+ title={
362
+ 'text': 'Typhoon Tracks',
363
+ 'y':0.95,
364
+ 'x':0.5,
365
+ 'xanchor': 'center',
366
+ 'yanchor': 'top'
367
+ },
368
+ showlegend=True,
369
+ legend=dict(
370
+ yanchor="top",
371
+ y=0.99,
372
+ xanchor="left",
373
+ x=0.01,
374
+ bgcolor='rgba(255, 255, 255, 0.8)'
375
+ ),
376
+ geo=dict(
377
+ projection_type='mercator',
378
+ showland=True,
379
+ showcoastlines=True,
380
+ landcolor='rgb(243, 243, 243)',
381
+ countrycolor='rgb(204, 204, 204)',
382
+ coastlinecolor='rgb(214, 214, 214)',
383
+ showocean=True,
384
+ oceancolor='rgb(230, 250, 255)',
385
+ showlakes=True,
386
+ lakecolor='rgb(230, 250, 255)',
387
+ lataxis=dict(range=[0, 50]),
388
+ lonaxis=dict(range=[100, 180]),
389
+ center=dict(lat=20, lon=140),
390
+ bgcolor='rgba(255, 255, 255, 0.5)'
391
+ ),
392
+ paper_bgcolor='rgba(255, 255, 255, 0.5)',
393
+ plot_bgcolor='rgba(255, 255, 255, 0.5)'
394
+ )
395
+
396
+ for category in COLOR_MAP.keys():
397
+ category_data = data[data['Category'] == category]
398
+ for _, storm in category_data.groupby('SID'):
399
+ track_data = self.typhoon_data[self.typhoon_data['SID'] == storm['SID'].iloc[0]]
400
+ track_data = track_data.sort_values('ISO_TIME')
401
+
402
+ fig.add_trace(go.Scattergeo(
403
+ lon=track_data['LON'],
404
+ lat=track_data['LAT'],
405
+ mode='lines',
406
+ line=dict(
407
+ width=2,
408
+ color=COLOR_MAP[category]
409
+ ),
410
+ name=category,
411
+ legendgroup=category,
412
+ showlegend=True if storm.iloc[0]['SID'] == category_data.iloc[0]['SID'] else False,
413
+ hovertemplate=(
414
+ f"Name: {storm['NAME'].iloc[0]}<br>" +
415
+ f"Category: {category}<br>" +
416
+ f"Wind Speed: {storm['USA_WIND'].iloc[0]:.1f} kt<br>" +
417
+ f"Pressure: {storm['WMO_PRES'].iloc[0]:.1f} hPa<br>" +
418
+ f"Date: {track_data['ISO_TIME'].dt.strftime('%Y-%m-%d %H:%M').iloc[0]}<br>" +
419
+ f"Lat: {track_data['LAT'].iloc[0]:.2f}°N<br>" +
420
+ f"Lon: {track_data['LON'].iloc[0]:.2f}°E<br>" +
421
+ "<extra></extra>"
422
+ )
423
+ ))
424
+
425
+ return fig
426
+
427
+ def analyze_clusters(self, year, n_clusters):
428
+ """Analyze typhoon clusters for a specific year"""
429
+ year_data = self.typhoon_data[self.typhoon_data['SEASON'] == year]
430
+ if year_data.empty:
431
+ return go.Figure(), "No data available for selected year"
432
+
433
+ # Prepare data for clustering
434
+ routes = []
435
+ for _, storm in year_data.groupby('SID'):
436
+ if len(storm) > 1:
437
+ # Standardize route length
438
+ t = np.linspace(0, 1, len(storm))
439
+ t_new = np.linspace(0, 1, 100)
440
+ lon_interp = interp1d(t, storm['LON'], kind='linear')(t_new)
441
+ lat_interp = interp1d(t, storm['LAT'], kind='linear')(t_new)
442
+ routes.append(np.column_stack((lon_interp, lat_interp)))
443
+
444
+ if len(routes) < n_clusters:
445
+ return go.Figure(), f"Not enough typhoons ({len(routes)}) for {n_clusters} clusters"
446
+
447
+ # Perform clustering
448
+ routes_array = np.array(routes)
449
+ routes_reshaped = routes_array.reshape(routes_array.shape[0], -1)
450
+ kmeans = KMeans(n_clusters=n_clusters, random_state=42)
451
+ clusters = kmeans.fit_predict(routes_reshaped)
452
+
453
+ # Create visualization
454
+ fig = go.Figure()
455
+
456
+ # Set layout
457
+ fig.update_layout(
458
+ title=f'Typhoon Route Clusters ({year})',
459
+ showlegend=True,
460
+ geo=dict(
461
+ projection_type='mercator',
462
+ showland=True,
463
+ showcoastlines=True,
464
+ landcolor='rgb(243, 243, 243)',
465
+ countrycolor='rgb(204, 204, 204)',
466
+ coastlinecolor='rgb(214, 214, 214)',
467
+ showocean=True,
468
+ oceancolor='rgb(230, 250, 255)',
469
+ lataxis=dict(range=[0, 50]),
470
+ lonaxis=dict(range=[100, 180]),
471
+ center=dict(lat=20, lon=140)
472
+ )
473
+ )
474
+
475
+ # Plot routes colored by cluster
476
+ for route, cluster_id in zip(routes, clusters):
477
+ fig.add_trace(go.Scattergeo(
478
+ lon=route[:, 0],
479
+ lat=route[:, 1],
480
+ mode='lines',
481
+ line=dict(
482
+ width=1,
483
+ color=f'hsl({cluster_id * 360/n_clusters}, 50%, 50%)'
484
+ ),
485
+ name=f'Cluster {cluster_id + 1}',
486
+ showlegend=False
487
+ ))
488
+
489
+ # Plot cluster centers
490
+ for i in range(n_clusters):
491
+ center = kmeans.cluster_centers_[i].reshape(-1, 2)
492
+ fig.add_trace(go.Scattergeo(
493
+ lon=center[:, 0],
494
+ lat=center[:, 1],
495
+ mode='lines',
496
+ name=f'Cluster {i+1} Center',
497
+ line=dict(
498
+ width=3,
499
+ color=f'hsl({i * 360/n_clusters}, 100%, 50%)'
500
+ )
501
+ ))
502
+
503
+ # Generate statistics text
504
+ stats_text = "### Clustering Results\n\n"
505
+ cluster_counts = np.bincount(clusters)
506
+ for i in range(n_clusters):
507
+ stats_text += f"- Cluster {i+1}: {cluster_counts[i]} typhoons\n"
508
+
509
+ return fig, stats_text
510
  def get_typhoons_for_year(self, year):
511
  """Get list of typhoons for a specific year"""
512
  year_data = self.typhoon_data[self.typhoon_data['ISO_TIME'].dt.year == year]