import streamlit as st import pandas as pd import plotly.express as px from datetime import date, timedelta # Import ObsPy for live data fetching import obspy from obspy.clients.fdsn import Client from obspy import UTCDateTime # --- Page Configuration --- st.set_page_config( page_title="Live Earthquake Explorer (ObsPy)", page_icon="🛰️", layout="wide", initial_sidebar_state="expanded" ) # --- Live Data Fetching Function using ObsPy --- @st.cache_data(ttl=900) # Cache the data for 15 minutes def fetch_live_data(start_date, end_date, min_mag): """ Fetches live earthquake data from the IRIS FDSN web service using ObsPy. """ client = Client("IRIS") start_time = UTCDateTime(start_date) end_time = UTCDateTime(end_date) + timedelta(days=1) try: catalog = client.get_events( starttime=start_time, endtime=end_time, minmagnitude=min_mag ) except Exception as e: st.error(f"Could not retrieve data from IRIS FDSN service: {e}") return pd.DataFrame() event_list = [] for event in catalog: if not (event.preferred_origin() and event.preferred_magnitude()): continue origin = event.preferred_origin() magnitude = event.preferred_magnitude() # Get location description safely. description = "N/A" if hasattr(event, 'descriptions'): if event.descriptions and event.descriptions[0].text: description = event.descriptions[0].text event_list.append({ "latitude": origin.latitude, "longitude": origin.longitude, "depth": origin.depth / 1000, "magnitude": magnitude.mag, "time": origin.time.datetime, "place": description, }) if not event_list: return pd.DataFrame() return pd.DataFrame(event_list) # --- UI Design --- st.title("🛰️ Live Earthquake Explorer with ObsPy") st.markdown("Data is fetched in real-time from the **IRIS FDSN** web service.") with st.sidebar: st.header("🔍 Search Parameters") today = date.today() thirty_days_ago = today - timedelta(days=30) start_date = st.date_input("Start Date", value=thirty_days_ago) end_date = st.date_input("End Date", value=today) min_magnitude = st.slider("Minimum Magnitude", 0.0, 10.0, 5.0, 0.1) search_button = st.button("Search Earthquakes", type="primary", use_container_width=True) # --- Main Content Area --- if search_button: if start_date > end_date: st.error("Error: Start date cannot be after end date.") else: with st.spinner(f"Fetching earthquakes from {start_date} to {end_date}..."): df = fetch_live_data(start_date, end_date, min_magnitude) if not df.empty: st.success(f"Found {len(df)} earthquakes matching your criteria.") st.subheader("📊 Summary Statistics") col1, col2, col3 = st.columns(3) col1.metric("Total Earthquakes", f"{len(df):,}") col2.metric("Largest Magnitude", f"{df['magnitude'].max():.1f}") col3.metric("Deepest Event (km)", f"{df['depth'].max():.1f}") st.markdown("---") st.subheader("🌍 Interactive Earthquake Map") st.markdown("Hover over points for details. Circle size represents magnitude.") fig = px.scatter_geo( df, lat='latitude', lon='longitude', size='magnitude', color='depth', hover_name='place', hover_data={'latitude': ':.2f', 'longitude': ':.2f', 'time': '|%Y-%m-%d %H:%M', 'magnitude': ':.1f', 'depth': ':.1f km'}, projection="natural earth", title=f"Live Earthquakes from {start_date} to {end_date} (Magnitude > {min_magnitude})", color_continuous_scale=px.colors.sequential.Plasma_r ) fig.update_layout(margin={"r":0,"t":40,"l":0,"b":0}, coloraxis_colorbar_title_text='Depth (km)') st.plotly_chart(fig, use_container_width=True) # --- Show Earthquakes in a Table --- st.subheader("📋 Detailed Earthquake Data") # This line is changed to show latitude and longitude instead of place. display_df = df[['time', 'latitude', 'longitude', 'magnitude', 'depth']].copy() st.dataframe( display_df.sort_values(by='time', ascending=False), use_container_width=True, height=400, column_config={"time": st.column_config.DatetimeColumn("Time (UTC)", format="YYYY-MM-DD HH:mm")} ) else: st.warning("No earthquakes were found for the specified criteria. Try expanding the date range or lowering the magnitude.") else: st.info("Set your desired parameters in the sidebar and click 'Search Earthquakes' to begin.")