nakas commited on
Commit
964b46c
Β·
verified Β·
1 Parent(s): 1fa2eff

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +372 -280
app.py CHANGED
@@ -580,164 +580,179 @@ def add_coordinates_to_incidents(df, max_incidents=30):
580
  print(f"Successfully extracted coordinates for {success_count}/{len(sample_df)} incidents")
581
  return df
582
 
583
- # Enhanced map generation with FIRMS data
584
  def generate_enhanced_map(df, firms_df):
585
- """Generate map with both InciWeb incidents and FIRMS hotspots - with robust error handling"""
586
 
587
  try:
588
- print("Starting map generation...")
589
 
590
  # Create map centered on the US
591
  m = folium.Map(location=[39.8283, -98.5795], zoom_start=4)
592
 
593
- # Add FIRMS heat map layer for all USA hotspots (even if no InciWeb coordinates)
594
- if not firms_df.empty:
595
- print(f"Adding {len(firms_df)} FIRMS hotspots to map...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  try:
597
- # Limit to first 1000 hotspots for performance
598
- sample_firms = firms_df.head(1000)
599
  heat_data = []
600
-
601
- for _, row in sample_firms.iterrows():
602
  try:
603
- lat, lon = float(row['latitude']), float(row['longitude'])
604
- frp = float(row.get('frp', 1))
605
  if -90 <= lat <= 90 and -180 <= lon <= 180: # Valid coordinates
606
  heat_data.append([lat, lon, min(frp, 100)])
607
- except (ValueError, TypeError):
608
  continue
609
 
610
  if heat_data:
611
  HeatMap(
612
  heat_data,
613
- name="Fire Intensity Heatmap (NASA FIRMS)",
614
  radius=15,
615
  blur=10,
616
  max_zoom=1,
617
  gradient={0.2: 'blue', 0.4: 'lime', 0.6: 'orange', 1: 'red'}
618
  ).add_to(m)
619
- print(f"Added heatmap with {len(heat_data)} valid hotspots")
620
 
621
- # Add some sample FIRMS points as individual markers
622
- for i, (_, hotspot) in enumerate(sample_firms.head(50).iterrows()):
623
  try:
624
- lat, lon = float(hotspot['latitude']), float(hotspot['longitude'])
625
- frp = float(hotspot.get('frp', 1))
626
- conf = hotspot.get('confidence', 'N/A')
627
- acq_time = hotspot.get('acq_time', 'N/A')
628
 
629
  if -90 <= lat <= 90 and -180 <= lon <= 180:
630
  folium.CircleMarker(
631
  location=[lat, lon],
632
  radius=2 + min(frp / 10, 8),
633
- popup=f"πŸ”₯ FIRMS Hotspot<br>FRP: {frp} MW<br>Confidence: {conf}%<br>Time: {acq_time}",
634
  color='red',
635
  fillColor='orange',
636
  fillOpacity=0.7,
637
  weight=1
638
  ).add_to(m)
639
- except (ValueError, TypeError, KeyError):
640
  continue
641
 
642
  except Exception as e:
643
  print(f"Error adding FIRMS data to map: {e}")
644
 
645
- # Add incident markers if we have coordinates
646
- incidents_with_coords = df[(df['latitude'].notna()) & (df['longitude'].notna())] if not df.empty else pd.DataFrame()
647
-
648
- active_incidents = 0
649
- inactive_incidents = 0
650
 
651
- if not incidents_with_coords.empty:
652
- print(f"Adding {len(incidents_with_coords)} InciWeb incidents with coordinates to map...")
653
 
654
- # Add incident markers with error handling
655
- try:
656
- incident_cluster = MarkerCluster(name="InciWeb Incidents").add_to(m)
657
-
658
- for _, row in incidents_with_coords.iterrows():
659
- try:
660
- lat, lon = float(row['latitude']), float(row['longitude'])
661
-
662
- if not (-90 <= lat <= 90 and -180 <= lon <= 180):
663
- continue
664
-
665
- # Determine marker color based on activity and type
666
- is_active = row.get('is_active', False)
667
- if is_active:
668
- active_incidents += 1
669
- activity_level = row.get('activity_level', 'Unknown')
670
- if activity_level == 'Very High':
671
- color = 'red'
672
- elif activity_level == 'High':
673
- color = 'orange'
674
- elif activity_level == 'Medium':
675
- color = 'yellow'
676
- else:
677
- color = 'lightred'
678
- else:
679
- inactive_incidents += 1
680
- color = 'gray'
681
-
682
- # Create popup content safely
683
- name = str(row.get('name', 'Unknown'))
684
- incident_type = str(row.get('type', 'N/A'))
685
- location = str(row.get('location', 'N/A'))
686
- size = row.get('size', 'N/A')
687
- updated = str(row.get('updated', 'N/A'))
 
 
688
 
689
- firms_hotspots = int(row.get('firms_hotspots', 0))
690
- total_frp = float(row.get('total_frp', 0))
691
- avg_confidence = float(row.get('avg_confidence', 0))
692
- activity_level = str(row.get('activity_level', 'Unknown'))
 
 
 
693
 
694
- popup_content = f"""
695
- <div style="width: 300px;">
696
- <h4>{name}</h4>
697
- <b>Type:</b> {incident_type}<br>
698
- <b>Location:</b> {location}<br>
699
- <b>Size:</b> {size} acres<br>
700
- <b>Last Updated:</b> {updated}<br>
701
-
702
- <hr style="margin: 10px 0;">
703
- <h5>πŸ”₯ Fire Activity (NASA FIRMS)</h5>
704
- <b>Status:</b> {'πŸ”΄ ACTIVE' if is_active else '⚫ Inactive'}<br>
705
- <b>Activity Level:</b> {activity_level}<br>
706
- <b>Hotspots (24h):</b> {firms_hotspots}<br>
707
- <b>Total Fire Power:</b> {total_frp:.1f} MW<br>
708
- <b>Avg Confidence:</b> {avg_confidence:.1f}%<br>
709
  </div>
710
- """
711
-
712
- folium.Marker(
713
- location=[lat, lon],
714
- popup=folium.Popup(popup_content, max_width=350),
715
- icon=folium.Icon(color=color)
716
- ).add_to(incident_cluster)
717
-
718
- except Exception as e:
719
- print(f"Error adding incident marker: {e}")
720
- continue
721
-
722
- except Exception as e:
723
- print(f"Error creating incident markers: {e}")
724
- else:
725
- print("No InciWeb incidents have coordinates, showing FIRMS data only")
726
- inactive_incidents = len(df) if not df.empty else 0
727
 
728
- # Add custom legend
729
- total_hotspots = len(firms_df) if not firms_df.empty else 0
730
- total_incidents = len(df) if not df.empty else 0
731
 
732
  legend_html = f'''
733
  <div style="position: fixed;
734
- bottom: 50px; left: 50px; width: 250px; height: 320px;
735
  border:2px solid grey; z-index:9999; font-size:12px;
736
  background-color:white; padding: 10px;
737
  border-radius: 5px; font-family: Arial;">
738
- <div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">πŸ”₯ Wildfire Activity Status</div>
739
 
740
- <div style="margin-bottom: 8px;"><b>InciWeb Incidents:</b></div>
741
  <div style="display: flex; align-items: center; margin-bottom: 3px;">
742
  <div style="background-color: red; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
743
  <div>Very High Activity</div>
@@ -750,29 +765,25 @@ def generate_enhanced_map(df, firms_df):
750
  <div style="background-color: yellow; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
751
  <div>Medium Activity</div>
752
  </div>
753
- <div style="display: flex; align-items: center; margin-bottom: 3px;">
754
  <div style="background-color: lightcoral; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
755
  <div>Low Activity</div>
756
  </div>
757
- <div style="display: flex; align-items: center; margin-bottom: 8px;">
758
- <div style="background-color: gray; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
759
- <div>Inactive/No Data</div>
760
- </div>
761
 
762
- <div style="margin-bottom: 5px;"><b>NASA FIRMS Data:</b></div>
763
- <div style="display: flex; align-items: center; margin-bottom: 3px;">
764
  <div style="background-color: orange; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
765
- <div>Fire Hotspots (24h)</div>
766
  </div>
767
- <div style="margin-bottom: 8px; font-style: italic;">Heat map shows fire intensity</div>
768
 
769
  <div style="font-size: 11px; margin-top: 10px; padding-top: 5px; border-top: 1px solid #ccc;">
770
- <b>Statistics:</b><br>
771
- πŸ”΄ Active InciWeb: {active_incidents}<br>
772
- ⚫ Inactive InciWeb: {inactive_incidents}<br>
773
- πŸ“ Total InciWeb: {total_incidents}<br>
774
- 🌑️ Total FIRMS Hotspots: {total_hotspots}<br>
775
- πŸ“Š Incidents with Coords: {len(incidents_with_coords)}
 
776
  </div>
777
  </div>
778
  '''
@@ -787,194 +798,251 @@ def generate_enhanced_map(df, firms_df):
787
  try:
788
  map_html = m._repr_html_()
789
  map_with_legend = map_html.replace('</body>', legend_html + '</body>')
790
- print("Map generation completed successfully")
791
  return map_with_legend
792
  except Exception as e:
793
  print(f"Error generating final map HTML: {e}")
794
  return f"<div style='padding: 20px; text-align: center;'>Map generation error: {str(e)}</div>"
795
 
796
  except Exception as e:
797
- print(f"Critical error in map generation: {e}")
798
  import traceback
799
  traceback.print_exc()
800
  return f"<div style='padding: 20px; text-align: center;'>Critical map error: {str(e)}</div>"
801
 
802
- # Enhanced visualization functions
803
  def generate_enhanced_visualizations(df, firms_df):
804
- """Generate enhanced visualizations with FIRMS data integration - with robust error handling"""
805
  figures = []
806
 
807
  try:
808
- print("Starting visualization generation...")
809
 
810
  if df.empty:
811
  print("Warning: Empty dataframe for visualizations")
812
  return [px.bar(title="No data available")]
813
 
814
- # 1. Activity Status Overview
815
- try:
816
- if 'is_active' in df.columns:
817
- activity_counts = df['is_active'].value_counts()
818
- if not activity_counts.empty:
819
- activity_summary = activity_counts.reset_index()
820
- activity_summary.columns = ['is_active', 'count']
821
- activity_summary['status'] = activity_summary['is_active'].map({
822
- True: 'Active (FIRMS detected)',
823
- False: 'Inactive/Unknown'
824
- })
825
-
826
- fig1 = px.pie(
827
- activity_summary,
828
- values='count',
829
- names='status',
830
- title="πŸ”₯ Wildfire Activity Status (Based on NASA FIRMS Data)",
831
- color_discrete_map={
832
- 'Active (FIRMS detected)': 'red',
833
- 'Inactive/Unknown': 'gray'
834
- }
835
- )
836
- fig1.update_traces(textinfo='label+percent+value')
837
- else:
838
- fig1 = px.bar(title="No activity status data available")
839
- else:
840
- fig1 = px.bar(title="Activity status data not available")
841
- except Exception as e:
842
- print(f"Error creating activity status chart: {e}")
843
- fig1 = px.bar(title=f"Activity status error: {str(e)}")
844
- figures.append(fig1)
845
 
846
- # 2. Incident Type Distribution (Simple fallback)
847
  try:
848
- if 'type' in df.columns and df['type'].notna().any():
849
- type_counts = df['type'].value_counts().head(10).reset_index()
850
- type_counts.columns = ['incident_type', 'count']
851
 
852
- fig2 = px.bar(
853
- type_counts,
854
- x='incident_type',
 
 
 
 
 
 
 
 
 
 
855
  y='count',
856
- title="πŸ“Š Incidents by Type",
857
- labels={'incident_type': 'Incident Type', 'count': 'Count'}
 
 
 
 
 
 
 
858
  )
859
  else:
860
- fig2 = px.bar(title="No incident type data available")
861
  except Exception as e:
862
- print(f"Error creating type distribution chart: {e}")
863
- fig2 = px.bar(title=f"Type distribution error: {str(e)}")
864
- figures.append(fig2)
865
 
866
- # 3. State Distribution
867
  try:
868
- if 'state' in df.columns and df['state'].notna().any():
869
- state_counts = df['state'].value_counts().head(10).reset_index()
870
  state_counts.columns = ['state_name', 'count']
871
 
872
- fig3 = px.bar(
873
  state_counts,
874
  x='state_name',
875
  y='count',
876
- title="πŸ—ΊοΈ Incidents by State (Top 10)",
877
- labels={'state_name': 'State', 'count': 'Count'}
 
 
 
 
 
 
878
  )
879
  else:
880
- fig3 = px.bar(title="No state data available")
881
  except Exception as e:
882
  print(f"Error creating state distribution chart: {e}")
883
- fig3 = px.bar(title=f"State distribution error: {str(e)}")
884
- figures.append(fig3)
885
 
886
- # 4. FIRMS Hotspot Timeline (if available)
887
  try:
888
- if not firms_df.empty and 'datetime' in firms_df.columns:
889
- # Group by hour to show detection pattern
890
- firms_df_copy = firms_df.copy()
891
- firms_df_copy['hour'] = pd.to_datetime(firms_df_copy['datetime']).dt.floor('H')
892
- hourly_detections = firms_df_copy.groupby('hour').size().reset_index(name='detections')
 
 
893
 
894
- if not hourly_detections.empty:
895
- fig4 = px.line(
896
- hourly_detections,
897
- x='hour',
898
- y='detections',
899
- title="πŸ• Fire Hotspot Detections Over Time (Last 24 Hours)",
900
- labels={'hour': 'Time', 'detections': 'Number of Hotspots Detected'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  )
902
- fig4.update_traces(line_color='red')
903
  else:
904
- fig4 = px.bar(title="No FIRMS temporal data available")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
905
  else:
906
  fig4 = px.bar(title="FIRMS temporal data not available")
907
  except Exception as e:
908
- print(f"Error creating FIRMS timeline chart: {e}")
909
- fig4 = px.bar(title=f"FIRMS timeline error: {str(e)}")
910
  figures.append(fig4)
911
 
912
- # 5. Fire Activity Level Distribution (if available)
913
  try:
914
- if 'activity_level' in df.columns and df['activity_level'].notna().any():
915
- # Filter out Unknown values
916
- activity_df = df[df['activity_level'] != 'Unknown']
917
- if not activity_df.empty and activity_df['activity_level'].notna().any():
918
- activity_levels = activity_df['activity_level'].value_counts().reset_index()
919
- activity_levels.columns = ['activity_level', 'count']
920
-
921
- # Define order and colors
922
- level_order = ['Very High', 'High', 'Medium', 'Low', 'Minimal']
923
- color_map = {
924
- 'Very High': 'darkred',
925
- 'High': 'red',
926
- 'Medium': 'orange',
927
- 'Low': 'yellow',
928
- 'Minimal': 'lightblue'
 
 
929
  }
930
-
931
- fig5 = px.bar(
932
- activity_levels,
933
- x='activity_level',
934
- y='count',
935
- title="πŸ“Š Fire Activity Levels (NASA FIRMS Intensity)",
936
- labels={'activity_level': 'Activity Level', 'count': 'Number of Incidents'},
937
- color='activity_level',
938
- color_discrete_map=color_map,
939
- category_orders={'activity_level': level_order}
940
- )
941
- else:
942
- fig5 = px.bar(title="No activity level data with known values")
943
  else:
944
- fig5 = px.bar(title="Activity level data not available")
945
  except Exception as e:
946
- print(f"Error creating activity level chart: {e}")
947
- fig5 = px.bar(title=f"Activity level error: {str(e)}")
948
  figures.append(fig5)
949
 
950
- print(f"Generated {len(figures)} visualizations")
951
  return figures
952
 
953
  except Exception as e:
954
- print(f"Critical error in visualization generation: {e}")
955
  import traceback
956
  traceback.print_exc()
957
  return [px.bar(title=f"Critical visualization error: {str(e)}")]
958
 
959
  # Main application function
960
- def create_enhanced_wildfire_app():
961
- """Create the enhanced Gradio application"""
962
 
963
- with gr.Blocks(title="Enhanced InciWeb + NASA FIRMS Wildfire Tracker", theme=gr.themes.Soft()) as app:
964
  gr.Markdown("""
965
- # πŸ”₯ Enhanced Wildfire Tracker
966
  ## InciWeb Incidents + NASA FIRMS Real-Time Fire Detection
967
 
968
- This application combines wildfire incident reports from InciWeb with real-time satellite fire detection data from NASA FIRMS to provide:
969
- - **Active fire status** based on satellite hotspot detection
970
- - **Fire intensity metrics** using Fire Radiative Power (FRP)
971
- - **Real-time hotspot mapping** from the last 24 hours
972
- - **Enhanced situational awareness** for wildfire management
 
 
 
 
 
 
 
 
 
973
  """)
974
 
975
  with gr.Row():
976
- fetch_btn = gr.Button("πŸš€ Fetch Latest Data (InciWeb + NASA FIRMS)", variant="primary", size="lg")
977
- status_text = gr.Textbox(label="Status", interactive=False, value="Ready to fetch data...")
978
 
979
  with gr.Tabs():
980
  with gr.TabItem("πŸ—ΊοΈ Enhanced Map"):
@@ -984,23 +1052,25 @@ def create_enhanced_wildfire_app():
984
  with gr.Row():
985
  plot_selector = gr.Dropdown(
986
  choices=[
987
- "Activity Status Overview",
988
- "Fire Activity Levels",
989
- "Intensity vs Size Analysis",
990
  "Hotspot Detection Timeline",
991
- "State Activity Breakdown"
992
  ],
993
  label="Select Visualization",
994
- value="Activity Status Overview"
995
  )
996
- plot_display = gr.Plot(label="Enhanced Analytics")
997
 
998
  with gr.TabItem("πŸ“‹ Data Tables"):
999
  with gr.Tabs():
1000
- with gr.TabItem("InciWeb Incidents"):
1001
- inciweb_table = gr.Dataframe(label="InciWeb Incidents with FIRMS Integration")
1002
- with gr.TabItem("NASA FIRMS Hotspots"):
1003
- firms_table = gr.Dataframe(label="NASA FIRMS Fire Hotspots (USA, 24h)")
 
 
1004
 
1005
  with gr.TabItem("πŸ“ Export Data"):
1006
  gr.Markdown("### Download Enhanced Dataset")
@@ -1014,22 +1084,22 @@ def create_enhanced_wildfire_app():
1014
  def fetch_and_process_data():
1015
  """Main data processing function with comprehensive error handling and debugging"""
1016
  try:
1017
- yield "πŸ“‘ Fetching InciWeb incident data...", None, None, None, None, None, None
1018
 
1019
  # Fetch InciWeb data with error handling
1020
  try:
1021
  print("Step 1: Fetching InciWeb data...")
1022
  inciweb_df = fetch_inciweb_data()
1023
  if inciweb_df.empty:
1024
- yield "❌ Failed to fetch InciWeb data", None, None, None, None, None, None
1025
  return
1026
  print(f"Step 1 SUCCESS: Got {len(inciweb_df)} incidents")
1027
  except Exception as e:
1028
  print(f"Step 1 ERROR: {e}")
1029
- yield f"❌ Error fetching InciWeb data: {str(e)}", None, None, None, None, None, None
1030
  return
1031
 
1032
- yield f"βœ… Found {len(inciweb_df)} InciWeb incidents. Getting coordinates...", None, None, None, None, None, None
1033
 
1034
  # Get coordinates for sample incidents with error handling
1035
  try:
@@ -1041,7 +1111,7 @@ def create_enhanced_wildfire_app():
1041
  print(f"Step 2 ERROR: {e}")
1042
  # Continue with the data we have
1043
 
1044
- yield "πŸ›°οΈ Fetching NASA FIRMS fire detection data...", None, None, None, None, None, None
1045
 
1046
  # Fetch FIRMS data with error handling
1047
  try:
@@ -1050,7 +1120,7 @@ def create_enhanced_wildfire_app():
1050
  if firms_df.empty:
1051
  print("Step 3 WARNING: FIRMS data empty")
1052
  # Still useful to show InciWeb data even without FIRMS
1053
- yield "⚠️ FIRMS data unavailable, generating basic visualization...", None, None, None, None, None, None
1054
 
1055
  # Generate basic map and visualizations without FIRMS data
1056
  try:
@@ -1065,23 +1135,26 @@ def create_enhanced_wildfire_app():
1065
  inciweb_df.to_csv(csv_file.name, index=False)
1066
  csv_file.close()
1067
 
 
 
 
1068
  final_status = f"βœ… Partial success! Found {len(inciweb_df)} InciWeb incidents (FIRMS data unavailable)"
1069
- yield (final_status, map_html, plots[0], inciweb_df, pd.DataFrame(), csv_file.name,
1070
  {"inciweb_df": inciweb_df, "firms_df": pd.DataFrame(), "plots": plots})
1071
  return
1072
  except Exception as e:
1073
  print(f"Error in basic visualization: {e}")
1074
- yield f"❌ Error in basic visualization: {str(e)}", None, None, inciweb_df, pd.DataFrame(), None, None
1075
  return
1076
 
1077
  print(f"Step 3 SUCCESS: Got {len(firms_df)} FIRMS hotspots")
1078
 
1079
  except Exception as e:
1080
  print(f"Step 3 ERROR: {e}")
1081
- yield f"❌ Error fetching FIRMS data: {str(e)}", None, None, inciweb_df, pd.DataFrame(), None, None
1082
  return
1083
 
1084
- yield f"βœ… Found {len(firms_df)} USA fire hotspots. Matching with incidents...", None, None, None, None, None, None
1085
 
1086
  # Match FIRMS data to InciWeb incidents with error handling
1087
  try:
@@ -1094,15 +1167,15 @@ def create_enhanced_wildfire_app():
1094
  enhanced_df = inciweb_df
1095
  print("Using original InciWeb data without FIRMS matching")
1096
 
1097
- yield "πŸ—ΊοΈ Generating enhanced map...", None, None, None, None, None, None
1098
 
1099
  # Generate map and visualizations with error handling
1100
  try:
1101
- print("Step 5: Generating map...")
1102
  map_html = generate_enhanced_map(enhanced_df, firms_df)
1103
  print("Step 5a SUCCESS: Map generated")
1104
 
1105
- print("Step 5: Generating visualizations...")
1106
  plots = generate_enhanced_visualizations(enhanced_df, firms_df)
1107
  print("Step 5b SUCCESS: Visualizations generated")
1108
  except Exception as e:
@@ -1111,40 +1184,59 @@ def create_enhanced_wildfire_app():
1111
  map_html = f"<div style='padding: 20px; text-align: center;'>Map generation failed: {str(e)}<br>Data is available in tables below.</div>"
1112
  plots = [px.bar(title=f"Visualization generation failed: {str(e)}")]
1113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1114
  # Prepare export data - create temporary files
1115
  try:
1116
- print("Step 6: Creating CSV export...")
1117
  import tempfile
1118
  csv_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
1119
  enhanced_df.to_csv(csv_file.name, index=False)
1120
  csv_file.close()
1121
- print("Step 6 SUCCESS: CSV created")
1122
  except Exception as e:
1123
- print(f"Step 6 ERROR: {e}")
1124
  csv_file = None
1125
 
1126
  # Calculate final statistics
1127
  try:
1128
- active_count = (enhanced_df.get('is_active', pd.Series([False])) == True).sum()
 
1129
  total_hotspots = len(firms_df) if not firms_df.empty else 0
1130
  coords_count = len(enhanced_df[(enhanced_df['latitude'].notna()) & (enhanced_df['longitude'].notna())])
1131
 
1132
- final_status = f"βœ… Complete! {active_count} active fires, {total_hotspots} hotspots, {coords_count} with coordinates"
1133
  print(f"FINAL SUCCESS: {final_status}")
1134
 
1135
- yield (final_status, map_html, plots[0], enhanced_df, firms_df, csv_file.name if csv_file else None,
1136
- {"inciweb_df": enhanced_df, "firms_df": firms_df, "plots": plots})
1137
  except Exception as e:
1138
  print(f"Error calculating final statistics: {e}")
1139
  final_status = "βœ… Process completed with some errors"
1140
- yield (final_status, map_html, plots[0], enhanced_df, firms_df, csv_file.name if csv_file else None,
1141
- {"inciweb_df": enhanced_df, "firms_df": firms_df, "plots": plots})
1142
 
1143
  except Exception as e:
1144
  import traceback
1145
  error_details = traceback.format_exc()
1146
  print(f"CRITICAL ERROR in main process: {error_details}")
1147
- yield f"❌ Critical Error: {str(e)}", None, None, None, None, None, None
1148
 
1149
  def update_plot(plot_name, state_data):
1150
  """Update plot based on selection"""
@@ -1152,11 +1244,11 @@ def create_enhanced_wildfire_app():
1152
  return px.bar(title="No data available")
1153
 
1154
  plot_options = [
1155
- "Activity Status Overview",
1156
- "Fire Activity Levels",
1157
- "Intensity vs Size Analysis",
1158
  "Hotspot Detection Timeline",
1159
- "State Activity Breakdown"
1160
  ]
1161
 
1162
  try:
@@ -1168,7 +1260,7 @@ def create_enhanced_wildfire_app():
1168
  # Wire up the interface
1169
  fetch_btn.click(
1170
  fetch_and_process_data,
1171
- outputs=[status_text, map_display, plot_display, inciweb_table, firms_table, download_csv, app_state]
1172
  )
1173
 
1174
  plot_selector.change(
@@ -1181,5 +1273,5 @@ def create_enhanced_wildfire_app():
1181
 
1182
  # Create and launch the application
1183
  if __name__ == "__main__":
1184
- app = create_enhanced_wildfire_app()
1185
  app.launch(share=True, debug=True)
 
580
  print(f"Successfully extracted coordinates for {success_count}/{len(sample_df)} incidents")
581
  return df
582
 
583
+ # Enhanced map generation focusing only on active fires and nearby FIRMS data
584
  def generate_enhanced_map(df, firms_df):
585
+ """Generate map showing only active InciWeb incidents and their associated FIRMS hotspots"""
586
 
587
  try:
588
+ print("Starting focused map generation (active fires only)...")
589
 
590
  # Create map centered on the US
591
  m = folium.Map(location=[39.8283, -98.5795], zoom_start=4)
592
 
593
+ # Filter to only show active incidents (those with nearby FIRMS data)
594
+ active_incidents = df[df.get('is_active', False) == True].copy()
595
+
596
+ if active_incidents.empty:
597
+ print("No active incidents found - showing basic map")
598
+ legend_html = '''
599
+ <div style="position: fixed;
600
+ bottom: 50px; left: 50px; width: 250px; height: 100px;
601
+ border:2px solid grey; z-index:9999; font-size:12px;
602
+ background-color:white; padding: 10px;
603
+ border-radius: 5px; font-family: Arial;">
604
+ <div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">πŸ”₯ No Active Fires Detected</div>
605
+ <div>No InciWeb incidents have nearby FIRMS hotspots in the last 24 hours.</div>
606
+ </div>
607
+ '''
608
+ map_html = m._repr_html_()
609
+ return map_html.replace('</body>', legend_html + '</body>')
610
+
611
+ print(f"Found {len(active_incidents)} active incidents to display")
612
+
613
+ # Collect all FIRMS hotspots that are near active incidents
614
+ all_nearby_hotspots = []
615
+
616
+ for _, incident in active_incidents.iterrows():
617
+ # Parse hotspot coordinates from stored string
618
+ hotspot_coords_str = incident.get('hotspot_coords', '')
619
+ if hotspot_coords_str and hotspot_coords_str != 'None':
620
+ try:
621
+ # Safely evaluate the coordinate string
622
+ import ast
623
+ hotspot_coords = ast.literal_eval(hotspot_coords_str)
624
+ all_nearby_hotspots.extend(hotspot_coords)
625
+ except:
626
+ continue
627
+
628
+ # Add FIRMS heat map layer ONLY for hotspots near active incidents
629
+ if all_nearby_hotspots:
630
+ print(f"Adding {len(all_nearby_hotspots)} FIRMS hotspots near active incidents...")
631
  try:
 
 
632
  heat_data = []
633
+ for coord in all_nearby_hotspots:
 
634
  try:
635
+ lat, lon, frp = float(coord[0]), float(coord[1]), float(coord[2])
 
636
  if -90 <= lat <= 90 and -180 <= lon <= 180: # Valid coordinates
637
  heat_data.append([lat, lon, min(frp, 100)])
638
+ except (ValueError, TypeError, IndexError):
639
  continue
640
 
641
  if heat_data:
642
  HeatMap(
643
  heat_data,
644
+ name="Active Fire Intensity (NASA FIRMS)",
645
  radius=15,
646
  blur=10,
647
  max_zoom=1,
648
  gradient={0.2: 'blue', 0.4: 'lime', 0.6: 'orange', 1: 'red'}
649
  ).add_to(m)
650
+ print(f"Added heatmap with {len(heat_data)} hotspots near active incidents")
651
 
652
+ # Add individual FIRMS hotspot markers for active areas only
653
+ for i, coord in enumerate(all_nearby_hotspots[:100]): # Limit to 100 for performance
654
  try:
655
+ lat, lon, frp = float(coord[0]), float(coord[1]), float(coord[2])
 
 
 
656
 
657
  if -90 <= lat <= 90 and -180 <= lon <= 180:
658
  folium.CircleMarker(
659
  location=[lat, lon],
660
  radius=2 + min(frp / 10, 8),
661
+ popup=f"πŸ”₯ Active Hotspot<br>FRP: {frp:.1f} MW<br>Near active wildfire",
662
  color='red',
663
  fillColor='orange',
664
  fillOpacity=0.7,
665
  weight=1
666
  ).add_to(m)
667
+ except (ValueError, TypeError, IndexError):
668
  continue
669
 
670
  except Exception as e:
671
  print(f"Error adding FIRMS data to map: {e}")
672
 
673
+ # Add ONLY active incident markers
674
+ print(f"Adding {len(active_incidents)} active InciWeb incidents to map...")
 
 
 
675
 
676
+ try:
677
+ incident_cluster = MarkerCluster(name="Active Wildfire Incidents").add_to(m)
678
 
679
+ for _, row in active_incidents.iterrows():
680
+ try:
681
+ lat, lon = float(row['latitude']), float(row['longitude'])
682
+
683
+ if not (-90 <= lat <= 90 and -180 <= lon <= 180):
684
+ continue
685
+
686
+ # Determine marker color based on activity level
687
+ activity_level = row.get('activity_level', 'Unknown')
688
+ if activity_level == 'Very High':
689
+ color = 'red'
690
+ elif activity_level == 'High':
691
+ color = 'orange'
692
+ elif activity_level == 'Medium':
693
+ color = 'yellow'
694
+ else:
695
+ color = 'lightred'
696
+
697
+ # Create popup content safely
698
+ name = str(row.get('name', 'Unknown'))
699
+ incident_type = str(row.get('type', 'N/A'))
700
+ location = str(row.get('location', 'N/A'))
701
+ size = row.get('size', 'N/A')
702
+ updated = str(row.get('updated', 'N/A'))
703
+
704
+ firms_hotspots = int(row.get('firms_hotspots', 0))
705
+ total_frp = float(row.get('total_frp', 0))
706
+ avg_confidence = float(row.get('avg_confidence', 0))
707
+
708
+ popup_content = f"""
709
+ <div style="width: 300px;">
710
+ <h4>πŸ”₯ {name}</h4>
711
+ <b>Type:</b> {incident_type}<br>
712
+ <b>Location:</b> {location}<br>
713
+ <b>Size:</b> {size} acres<br>
714
+ <b>Last Updated:</b> {updated}<br>
715
 
716
+ <hr style="margin: 10px 0;">
717
+ <h5>πŸ“‘ Satellite Fire Activity</h5>
718
+ <b>Status:</b> πŸ”΄ ACTIVE (FIRMS confirmed)<br>
719
+ <b>Activity Level:</b> {activity_level}<br>
720
+ <b>Hotspots (24h):</b> {firms_hotspots}<br>
721
+ <b>Total Fire Power:</b> {total_frp:.1f} MW<br>
722
+ <b>Detection Confidence:</b> {avg_confidence:.1f}%<br>
723
 
724
+ <div style="margin-top: 8px; padding: 5px; background-color: #ffe6e6; border-radius: 3px;">
725
+ <small><b>πŸ›°οΈ Real-time confirmed:</b> This fire has active satellite hotspots detected in the last 24 hours</small>
 
 
 
 
 
 
 
 
 
 
 
 
 
726
  </div>
727
+ </div>
728
+ """
729
+
730
+ folium.Marker(
731
+ location=[lat, lon],
732
+ popup=folium.Popup(popup_content, max_width=350),
733
+ icon=folium.Icon(color=color, icon='fire', prefix='fa')
734
+ ).add_to(incident_cluster)
735
+
736
+ except Exception as e:
737
+ print(f"Error adding active incident marker: {e}")
738
+ continue
739
+
740
+ except Exception as e:
741
+ print(f"Error creating active incident markers: {e}")
 
 
742
 
743
+ # Add focused legend for active fires only
744
+ total_active = len(active_incidents)
745
+ total_hotspots = len(all_nearby_hotspots)
746
 
747
  legend_html = f'''
748
  <div style="position: fixed;
749
+ bottom: 50px; left: 50px; width: 280px; height: 280px;
750
  border:2px solid grey; z-index:9999; font-size:12px;
751
  background-color:white; padding: 10px;
752
  border-radius: 5px; font-family: Arial;">
753
+ <div style="font-weight: bold; margin-bottom: 8px; font-size: 14px;">πŸ”₯ Active Wildfire Detection</div>
754
 
755
+ <div style="margin-bottom: 8px;"><b>Fire Activity Levels:</b></div>
756
  <div style="display: flex; align-items: center; margin-bottom: 3px;">
757
  <div style="background-color: red; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
758
  <div>Very High Activity</div>
 
765
  <div style="background-color: yellow; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
766
  <div>Medium Activity</div>
767
  </div>
768
+ <div style="display: flex; align-items: center; margin-bottom: 8px;">
769
  <div style="background-color: lightcoral; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
770
  <div>Low Activity</div>
771
  </div>
 
 
 
 
772
 
773
+ <div style="margin-bottom: 5px;"><b>Satellite Data:</b></div>
774
+ <div style="display: flex; align-items: center; margin-bottom: 8px;">
775
  <div style="background-color: orange; width: 12px; height: 12px; margin-right: 5px; border-radius: 50%;"></div>
776
+ <div>NASA FIRMS Hotspots</div>
777
  </div>
 
778
 
779
  <div style="font-size: 11px; margin-top: 10px; padding-top: 5px; border-top: 1px solid #ccc;">
780
+ <b>🎯 Filtered Results:</b><br>
781
+ πŸ”₯ Active Fires: {total_active}<br>
782
+ πŸ“‘ Satellite Hotspots: {total_hotspots}<br>
783
+
784
+ <div style="margin-top: 5px; font-style: italic; color: #666;">
785
+ Only showing incidents with recent satellite fire detection
786
+ </div>
787
  </div>
788
  </div>
789
  '''
 
798
  try:
799
  map_html = m._repr_html_()
800
  map_with_legend = map_html.replace('</body>', legend_html + '</body>')
801
+ print(f"Map generation completed successfully - showing {total_active} active fires")
802
  return map_with_legend
803
  except Exception as e:
804
  print(f"Error generating final map HTML: {e}")
805
  return f"<div style='padding: 20px; text-align: center;'>Map generation error: {str(e)}</div>"
806
 
807
  except Exception as e:
808
+ print(f"Critical error in focused map generation: {e}")
809
  import traceback
810
  traceback.print_exc()
811
  return f"<div style='padding: 20px; text-align: center;'>Critical map error: {str(e)}</div>"
812
 
813
+ # Enhanced visualization functions focusing on active fires only
814
  def generate_enhanced_visualizations(df, firms_df):
815
+ """Generate enhanced visualizations focusing only on active fires with FIRMS data integration"""
816
  figures = []
817
 
818
  try:
819
+ print("Starting focused visualization generation (active fires only)...")
820
 
821
  if df.empty:
822
  print("Warning: Empty dataframe for visualizations")
823
  return [px.bar(title="No data available")]
824
 
825
+ # Filter to only active incidents for most visualizations
826
+ active_df = df[df.get('is_active', False) == True].copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
827
 
828
+ # 1. Active Fire Activity Levels (only active fires)
829
  try:
830
+ if not active_df.empty and 'activity_level' in active_df.columns:
831
+ activity_levels = active_df['activity_level'].value_counts().reset_index()
832
+ activity_levels.columns = ['activity_level', 'count']
833
 
834
+ # Define order and colors
835
+ level_order = ['Very High', 'High', 'Medium', 'Low', 'Minimal']
836
+ color_map = {
837
+ 'Very High': 'darkred',
838
+ 'High': 'red',
839
+ 'Medium': 'orange',
840
+ 'Low': 'yellow',
841
+ 'Minimal': 'lightblue'
842
+ }
843
+
844
+ fig1 = px.bar(
845
+ activity_levels,
846
+ x='activity_level',
847
  y='count',
848
+ title="πŸ”₯ Active Fire Intensity Levels (NASA FIRMS Confirmed)",
849
+ labels={'activity_level': 'Fire Activity Level', 'count': 'Number of Active Fires'},
850
+ color='activity_level',
851
+ color_discrete_map=color_map,
852
+ category_orders={'activity_level': level_order}
853
+ )
854
+ fig1.update_layout(
855
+ title_font_size=16,
856
+ showlegend=False
857
  )
858
  else:
859
+ fig1 = px.bar(title="No active fires detected with FIRMS data")
860
  except Exception as e:
861
+ print(f"Error creating activity level chart: {e}")
862
+ fig1 = px.bar(title=f"Activity level error: {str(e)}")
863
+ figures.append(fig1)
864
 
865
+ # 2. Active Fires by State (only active fires)
866
  try:
867
+ if not active_df.empty and 'state' in active_df.columns:
868
+ state_counts = active_df['state'].value_counts().reset_index()
869
  state_counts.columns = ['state_name', 'count']
870
 
871
+ fig2 = px.bar(
872
  state_counts,
873
  x='state_name',
874
  y='count',
875
+ title="πŸ—ΊοΈ Active Fires by State (FIRMS Confirmed)",
876
+ labels={'state_name': 'State', 'count': 'Number of Active Fires'},
877
+ color='count',
878
+ color_continuous_scale='Reds'
879
+ )
880
+ fig2.update_layout(
881
+ title_font_size=16,
882
+ showlegend=False
883
  )
884
  else:
885
+ fig2 = px.bar(title="No active fires by state data available")
886
  except Exception as e:
887
  print(f"Error creating state distribution chart: {e}")
888
+ fig2 = px.bar(title=f"State distribution error: {str(e)}")
889
+ figures.append(fig2)
890
 
891
+ # 3. Fire Intensity vs Size Scatter (only active fires)
892
  try:
893
+ if not active_df.empty and 'total_frp' in active_df.columns and 'size' in active_df.columns:
894
+ # Filter to fires with both size and FRP data
895
+ scatter_df = active_df[
896
+ (active_df['total_frp'] > 0) &
897
+ (active_df['size'].notna()) &
898
+ (active_df['size'] > 0)
899
+ ].copy()
900
 
901
+ if not scatter_df.empty:
902
+ fig3 = px.scatter(
903
+ scatter_df,
904
+ x='size',
905
+ y='total_frp',
906
+ size='firms_hotspots',
907
+ color='activity_level',
908
+ hover_data=['name', 'state', 'firms_hotspots'],
909
+ title="πŸ”₯ Fire Intensity vs Size (Active Fires Only)",
910
+ labels={
911
+ 'size': 'Fire Size (acres)',
912
+ 'total_frp': 'Satellite Fire Power (MW)',
913
+ 'firms_hotspots': 'Hotspot Count'
914
+ },
915
+ color_discrete_map={
916
+ 'Very High': 'darkred',
917
+ 'High': 'red',
918
+ 'Medium': 'orange',
919
+ 'Low': 'yellow'
920
+ }
921
+ )
922
+ fig3.update_layout(
923
+ title_font_size=16,
924
+ xaxis_type="log",
925
+ yaxis_type="log"
926
  )
 
927
  else:
928
+ fig3 = px.bar(title="No active fires with size and intensity data")
929
+ else:
930
+ fig3 = px.bar(title="Fire intensity vs size data not available")
931
+ except Exception as e:
932
+ print(f"Error creating scatter plot: {e}")
933
+ fig3 = px.bar(title=f"Scatter plot error: {str(e)}")
934
+ figures.append(fig3)
935
+
936
+ # 4. FIRMS Hotspot Detection Timeline (only hotspots near active incidents)
937
+ try:
938
+ if not firms_df.empty and 'datetime' in firms_df.columns and not active_df.empty:
939
+ # Get all hotspots that are near active incidents
940
+ all_nearby_hotspots_coords = []
941
+ for _, incident in active_df.iterrows():
942
+ hotspot_coords_str = incident.get('hotspot_coords', '')
943
+ if hotspot_coords_str and hotspot_coords_str != 'None':
944
+ try:
945
+ import ast
946
+ hotspot_coords = ast.literal_eval(hotspot_coords_str)
947
+ all_nearby_hotspots_coords.extend(hotspot_coords)
948
+ except:
949
+ continue
950
+
951
+ if all_nearby_hotspots_coords:
952
+ # Create timeline based on FIRMS data filtered to active areas
953
+ firms_copy = firms_df.copy()
954
+ firms_copy['hour'] = pd.to_datetime(firms_copy['datetime']).dt.floor('H')
955
+ hourly_detections = firms_copy.groupby('hour').size().reset_index(name='detections')
956
+
957
+ if not hourly_detections.empty:
958
+ fig4 = px.line(
959
+ hourly_detections,
960
+ x='hour',
961
+ y='detections',
962
+ title="πŸ• Active Fire Hotspot Detections Over Time (Near Active Incidents)",
963
+ labels={'hour': 'Time', 'detections': 'Hotspots Detected'},
964
+ markers=True
965
+ )
966
+ fig4.update_traces(line_color='red', marker_color='orange')
967
+ fig4.update_layout(title_font_size=16)
968
+ else:
969
+ fig4 = px.bar(title="No temporal FIRMS data available")
970
+ else:
971
+ fig4 = px.bar(title="No hotspots near active incidents found")
972
  else:
973
  fig4 = px.bar(title="FIRMS temporal data not available")
974
  except Exception as e:
975
+ print(f"Error creating timeline chart: {e}")
976
+ fig4 = px.bar(title=f"Timeline error: {str(e)}")
977
  figures.append(fig4)
978
 
979
+ # 5. Active vs Inactive Fire Summary
980
  try:
981
+ active_count = len(active_df)
982
+ inactive_count = len(df) - active_count
983
+
984
+ if active_count > 0 or inactive_count > 0:
985
+ summary_data = pd.DataFrame({
986
+ 'status': ['πŸ”₯ Active (FIRMS Confirmed)', '⚫ Inactive/No Data'],
987
+ 'count': [active_count, inactive_count]
988
+ })
989
+
990
+ fig5 = px.pie(
991
+ summary_data,
992
+ values='count',
993
+ names='status',
994
+ title="πŸ“Š Fire Detection Summary (InciWeb vs FIRMS)",
995
+ color_discrete_map={
996
+ 'πŸ”₯ Active (FIRMS Confirmed)': 'red',
997
+ '⚫ Inactive/No Data': 'gray'
998
  }
999
+ )
1000
+ fig5.update_traces(textinfo='label+percent+value')
1001
+ fig5.update_layout(title_font_size=16)
 
 
 
 
 
 
 
 
 
 
1002
  else:
1003
+ fig5 = px.bar(title="No fire status data available")
1004
  except Exception as e:
1005
+ print(f"Error creating summary chart: {e}")
1006
+ fig5 = px.bar(title=f"Summary error: {str(e)}")
1007
  figures.append(fig5)
1008
 
1009
+ print(f"Generated {len(figures)} focused visualizations for {len(active_df)} active fires")
1010
  return figures
1011
 
1012
  except Exception as e:
1013
+ print(f"Critical error in focused visualization generation: {e}")
1014
  import traceback
1015
  traceback.print_exc()
1016
  return [px.bar(title=f"Critical visualization error: {str(e)}")]
1017
 
1018
  # Main application function
1019
+ def create_focused_wildfire_app():
1020
+ """Create the focused active wildfire Gradio application"""
1021
 
1022
+ with gr.Blocks(title="Focused Active Wildfire Tracker", theme=gr.themes.Soft()) as app:
1023
  gr.Markdown("""
1024
+ # πŸ”₯ Focused Active Wildfire Tracker
1025
  ## InciWeb Incidents + NASA FIRMS Real-Time Fire Detection
1026
 
1027
+ This application identifies **currently active wildfires** by combining official incident reports from InciWeb with real-time satellite fire detection data from NASA FIRMS:
1028
+
1029
+ ### 🎯 **What You'll See:**
1030
+ - **πŸ”₯ Active Fires Only**: InciWeb incidents that have nearby satellite-detected hotspots (confirmed burning)
1031
+ - **πŸ“‘ Real-Time Data**: NASA FIRMS satellite fire detection from the last 24 hours
1032
+ - **πŸ›°οΈ Fire Intensity**: Fire Radiative Power (FRP) measurements showing fire strength
1033
+ - **πŸ—ΊοΈ Focused Map**: Clean visualization showing only confirmed active wildfires and their satellite data
1034
+
1035
+ ### 🚫 **What's Filtered Out:**
1036
+ - InciWeb incidents without recent satellite fire activity (likely contained/inactive)
1037
+ - Random FIRMS hotspots not near known incidents
1038
+ - Outdated or inactive fire reports
1039
+
1040
+ **Result: A precise view of what's actually burning right now!** πŸ”₯πŸ›°οΈ
1041
  """)
1042
 
1043
  with gr.Row():
1044
+ fetch_btn = gr.Button("πŸš€ Fetch Active Wildfire Data (InciWeb + NASA FIRMS)", variant="primary", size="lg")
1045
+ status_text = gr.Textbox(label="Status", interactive=False, value="Ready to fetch active wildfire data...")
1046
 
1047
  with gr.Tabs():
1048
  with gr.TabItem("πŸ—ΊοΈ Enhanced Map"):
 
1052
  with gr.Row():
1053
  plot_selector = gr.Dropdown(
1054
  choices=[
1055
+ "Active Fire Intensity Levels",
1056
+ "Active Fires by State",
1057
+ "Fire Intensity vs Size",
1058
  "Hotspot Detection Timeline",
1059
+ "Active vs Inactive Summary"
1060
  ],
1061
  label="Select Visualization",
1062
+ value="Active Fire Intensity Levels"
1063
  )
1064
+ plot_display = gr.Plot(label="Enhanced Analytics (Active Fires Focus)")
1065
 
1066
  with gr.TabItem("πŸ“‹ Data Tables"):
1067
  with gr.Tabs():
1068
+ with gr.TabItem("πŸ”₯ Active Fires"):
1069
+ active_fires_table = gr.Dataframe(label="Active Fires (FIRMS Confirmed)")
1070
+ with gr.TabItem("πŸ“‹ All InciWeb Incidents"):
1071
+ inciweb_table = gr.Dataframe(label="All InciWeb Incidents")
1072
+ with gr.TabItem("πŸ›°οΈ NASA FIRMS Data"):
1073
+ firms_table = gr.Dataframe(label="NASA FIRMS Fire Hotspots (Near Active Incidents)")
1074
 
1075
  with gr.TabItem("πŸ“ Export Data"):
1076
  gr.Markdown("### Download Enhanced Dataset")
 
1084
  def fetch_and_process_data():
1085
  """Main data processing function with comprehensive error handling and debugging"""
1086
  try:
1087
+ yield "πŸ“‘ Fetching InciWeb incident data...", None, None, None, None, None, None, None
1088
 
1089
  # Fetch InciWeb data with error handling
1090
  try:
1091
  print("Step 1: Fetching InciWeb data...")
1092
  inciweb_df = fetch_inciweb_data()
1093
  if inciweb_df.empty:
1094
+ yield "❌ Failed to fetch InciWeb data", None, None, None, None, None, None, None
1095
  return
1096
  print(f"Step 1 SUCCESS: Got {len(inciweb_df)} incidents")
1097
  except Exception as e:
1098
  print(f"Step 1 ERROR: {e}")
1099
+ yield f"❌ Error fetching InciWeb data: {str(e)}", None, None, None, None, None, None, None
1100
  return
1101
 
1102
+ yield f"βœ… Found {len(inciweb_df)} InciWeb incidents. Getting coordinates...", None, None, None, None, None, None, None
1103
 
1104
  # Get coordinates for sample incidents with error handling
1105
  try:
 
1111
  print(f"Step 2 ERROR: {e}")
1112
  # Continue with the data we have
1113
 
1114
+ yield "πŸ›°οΈ Fetching NASA FIRMS fire detection data...", None, None, None, None, None, None, None
1115
 
1116
  # Fetch FIRMS data with error handling
1117
  try:
 
1120
  if firms_df.empty:
1121
  print("Step 3 WARNING: FIRMS data empty")
1122
  # Still useful to show InciWeb data even without FIRMS
1123
+ yield "⚠️ FIRMS data unavailable, generating basic visualization...", None, None, None, None, None, None, None
1124
 
1125
  # Generate basic map and visualizations without FIRMS data
1126
  try:
 
1135
  inciweb_df.to_csv(csv_file.name, index=False)
1136
  csv_file.close()
1137
 
1138
+ # Create empty active fires table
1139
+ active_fires_df = pd.DataFrame()
1140
+
1141
  final_status = f"βœ… Partial success! Found {len(inciweb_df)} InciWeb incidents (FIRMS data unavailable)"
1142
+ yield (final_status, map_html, plots[0], active_fires_df, inciweb_df, pd.DataFrame(), csv_file.name,
1143
  {"inciweb_df": inciweb_df, "firms_df": pd.DataFrame(), "plots": plots})
1144
  return
1145
  except Exception as e:
1146
  print(f"Error in basic visualization: {e}")
1147
+ yield f"❌ Error in basic visualization: {str(e)}", None, None, inciweb_df, None, pd.DataFrame(), None, None
1148
  return
1149
 
1150
  print(f"Step 3 SUCCESS: Got {len(firms_df)} FIRMS hotspots")
1151
 
1152
  except Exception as e:
1153
  print(f"Step 3 ERROR: {e}")
1154
+ yield f"❌ Error fetching FIRMS data: {str(e)}", None, None, inciweb_df, None, pd.DataFrame(), None, None
1155
  return
1156
 
1157
+ yield f"βœ… Found {len(firms_df)} USA fire hotspots. Matching with incidents...", None, None, None, None, None, None, None
1158
 
1159
  # Match FIRMS data to InciWeb incidents with error handling
1160
  try:
 
1167
  enhanced_df = inciweb_df
1168
  print("Using original InciWeb data without FIRMS matching")
1169
 
1170
+ yield "πŸ—ΊοΈ Generating focused map and analytics (active fires only)...", None, None, None, None, None, None, None
1171
 
1172
  # Generate map and visualizations with error handling
1173
  try:
1174
+ print("Step 5: Generating focused map...")
1175
  map_html = generate_enhanced_map(enhanced_df, firms_df)
1176
  print("Step 5a SUCCESS: Map generated")
1177
 
1178
+ print("Step 5: Generating focused visualizations...")
1179
  plots = generate_enhanced_visualizations(enhanced_df, firms_df)
1180
  print("Step 5b SUCCESS: Visualizations generated")
1181
  except Exception as e:
 
1184
  map_html = f"<div style='padding: 20px; text-align: center;'>Map generation failed: {str(e)}<br>Data is available in tables below.</div>"
1185
  plots = [px.bar(title=f"Visualization generation failed: {str(e)}")]
1186
 
1187
+ # Create separate tables for active vs all incidents
1188
+ try:
1189
+ print("Step 6: Creating data tables...")
1190
+ # Active fires table (only incidents with FIRMS activity)
1191
+ active_fires_df = enhanced_df[enhanced_df.get('is_active', False) == True].copy()
1192
+
1193
+ # Filter FIRMS data to only hotspots near active incidents
1194
+ firms_near_active = pd.DataFrame()
1195
+ if not active_fires_df.empty and not firms_df.empty:
1196
+ # This is a simplified version - in a real implementation you'd filter more precisely
1197
+ firms_near_active = firms_df.head(100) # Limit for display
1198
+
1199
+ print(f"Step 6 SUCCESS: {len(active_fires_df)} active fires, {len(firms_near_active)} nearby FIRMS hotspots")
1200
+ except Exception as e:
1201
+ print(f"Step 6 ERROR: {e}")
1202
+ active_fires_df = pd.DataFrame()
1203
+ firms_near_active = pd.DataFrame()
1204
+
1205
  # Prepare export data - create temporary files
1206
  try:
1207
+ print("Step 7: Creating CSV export...")
1208
  import tempfile
1209
  csv_file = tempfile.NamedTemporaryFile(mode='w', suffix='.csv', delete=False)
1210
  enhanced_df.to_csv(csv_file.name, index=False)
1211
  csv_file.close()
1212
+ print("Step 7 SUCCESS: CSV created")
1213
  except Exception as e:
1214
+ print(f"Step 7 ERROR: {e}")
1215
  csv_file = None
1216
 
1217
  # Calculate final statistics
1218
  try:
1219
+ active_count = len(active_fires_df)
1220
+ total_incidents = len(enhanced_df)
1221
  total_hotspots = len(firms_df) if not firms_df.empty else 0
1222
  coords_count = len(enhanced_df[(enhanced_df['latitude'].notna()) & (enhanced_df['longitude'].notna())])
1223
 
1224
+ final_status = f"🎯 Focused Results: {active_count} active fires detected with satellite confirmation"
1225
  print(f"FINAL SUCCESS: {final_status}")
1226
 
1227
+ yield (final_status, map_html, plots[0], active_fires_df, enhanced_df, firms_near_active, csv_file.name if csv_file else None,
1228
+ {"inciweb_df": enhanced_df, "firms_df": firms_df, "plots": plots, "active_df": active_fires_df})
1229
  except Exception as e:
1230
  print(f"Error calculating final statistics: {e}")
1231
  final_status = "βœ… Process completed with some errors"
1232
+ yield (final_status, map_html, plots[0], active_fires_df, enhanced_df, firms_near_active, csv_file.name if csv_file else None,
1233
+ {"inciweb_df": enhanced_df, "firms_df": firms_df, "plots": plots, "active_df": active_fires_df})
1234
 
1235
  except Exception as e:
1236
  import traceback
1237
  error_details = traceback.format_exc()
1238
  print(f"CRITICAL ERROR in main process: {error_details}")
1239
+ yield f"❌ Critical Error: {str(e)}", None, None, None, None, None, None, None
1240
 
1241
  def update_plot(plot_name, state_data):
1242
  """Update plot based on selection"""
 
1244
  return px.bar(title="No data available")
1245
 
1246
  plot_options = [
1247
+ "Active Fire Intensity Levels",
1248
+ "Active Fires by State",
1249
+ "Fire Intensity vs Size",
1250
  "Hotspot Detection Timeline",
1251
+ "Active vs Inactive Summary"
1252
  ]
1253
 
1254
  try:
 
1260
  # Wire up the interface
1261
  fetch_btn.click(
1262
  fetch_and_process_data,
1263
+ outputs=[status_text, map_display, plot_display, active_fires_table, inciweb_table, firms_table, download_csv, app_state]
1264
  )
1265
 
1266
  plot_selector.change(
 
1273
 
1274
  # Create and launch the application
1275
  if __name__ == "__main__":
1276
+ app = create_focused_wildfire_app()
1277
  app.launch(share=True, debug=True)