Spaces:
Sleeping
Sleeping
Shreya Mendi commited on
Commit ·
723cf28
1
Parent(s): b29efb9
new features
Browse files- src/__pycache__/fetchapi.cpython-310.pyc +0 -0
- src/fetchapi.py +129 -0
- src/streamlit_app.py +290 -37
src/__pycache__/fetchapi.cpython-310.pyc
ADDED
|
Binary file (3.53 kB). View file
|
|
|
src/fetchapi.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
import requests
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from dotenv import load_dotenv
|
| 6 |
+
import os
|
| 7 |
+
import time
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
|
| 10 |
+
OPENSKY_URL = "https://opensky-network.org/api/states/all"
|
| 11 |
+
OPENSKY_URL_DEPARTURES = "https://opensky-network.org/api/flights/departure"
|
| 12 |
+
|
| 13 |
+
def fetch_opensky_snapshot() -> pd.DataFrame:
|
| 14 |
+
"""
|
| 15 |
+
Fetches a snapshot of current flights from the OpenSky API.
|
| 16 |
+
Returns a pandas DataFrame of flight state vectors.
|
| 17 |
+
"""
|
| 18 |
+
r = requests.get(OPENSKY_URL, timeout=20)
|
| 19 |
+
if r.status_code != 200:
|
| 20 |
+
raise RuntimeError(f"Failed to fetch OpenSky data: {r.status_code} {r.reason} -> {r.text[:200]}")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
data = r.json()
|
| 24 |
+
states = data.get("states", [])
|
| 25 |
+
timestamp = data.get("time", datetime.utcnow().timestamp())
|
| 26 |
+
|
| 27 |
+
cols = [
|
| 28 |
+
"icao24", "callsign", "origin_country", "time_position", "last_contact",
|
| 29 |
+
"longitude", "latitude", "baro_altitude", "on_ground", "velocity",
|
| 30 |
+
"true_track", "vertical_rate", "sensors", "geo_altitude", "squawk",
|
| 31 |
+
"spi", "position_source"
|
| 32 |
+
]
|
| 33 |
+
df = pd.DataFrame(states, columns=cols)
|
| 34 |
+
df["last_contact"] = pd.to_datetime(df["last_contact"], unit="s")
|
| 35 |
+
df.attrs["timestamp"] = datetime.utcfromtimestamp(timestamp)
|
| 36 |
+
return df
|
| 37 |
+
|
| 38 |
+
def fetch_aviation_API_airlines_endpoint():
|
| 39 |
+
"""
|
| 40 |
+
Fetches airline data from the AviationStack API airlines endpoint.
|
| 41 |
+
|
| 42 |
+
Parameters:
|
| 43 |
+
- None
|
| 44 |
+
|
| 45 |
+
Returns:
|
| 46 |
+
- dict: The JSON response from the AviationStack API containing the airline data.
|
| 47 |
+
"""
|
| 48 |
+
#api_key = os.environ.get("AVIATION_KEY") # Retrieve the API key (when running on HuggingFace)
|
| 49 |
+
# Comment the line above and uncomment the two lines below if you are running the app locally (not on HuggingFace) and have a .env file with the AviationStack API key
|
| 50 |
+
load_dotenv()
|
| 51 |
+
api_key = os.getenv("AVIATION_KEY") # Retrieve the API key
|
| 52 |
+
url = f"https://api.aviationstack.com/v1/airlines?access_key={api_key}"
|
| 53 |
+
response = requests.get(url)
|
| 54 |
+
return response.json()
|
| 55 |
+
|
| 56 |
+
def fetch_rdu_departures(hours=6) -> pd.DataFrame:
|
| 57 |
+
"""
|
| 58 |
+
Fetch recent departures from RDU (KRDU) within the last n hours (default is 6).
|
| 59 |
+
Returns a pandas DataFrame.
|
| 60 |
+
"""
|
| 61 |
+
end = int(time.time())
|
| 62 |
+
begin = end - hours * 3600
|
| 63 |
+
params = {
|
| 64 |
+
"airport": "KRDU",
|
| 65 |
+
"begin": begin,
|
| 66 |
+
"end": end
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
response = requests.get(OPENSKY_URL_DEPARTURES, params=params, timeout=20)
|
| 70 |
+
if response.status_code != 200:
|
| 71 |
+
raise RuntimeError(f"Failed to fetch data, {response.headers}")
|
| 72 |
+
|
| 73 |
+
data = response.json()
|
| 74 |
+
columns = [
|
| 75 |
+
"icao24", "firstSeen", "estDepartureAirport", "lastSeen", "estArrivalAirport", "callsign",
|
| 76 |
+
"estDepartureAirportHorizDistance", "estDepartureAirportVertDistance", "estArrivalAirportHorizDistance",
|
| 77 |
+
"estArrivalAirportVertDistance", "departureAirportCandidatesCount", "arrivalAirportCandidatesCount"
|
| 78 |
+
]
|
| 79 |
+
data_df = pd.DataFrame(data, columns=columns)
|
| 80 |
+
|
| 81 |
+
flights = []
|
| 82 |
+
for _, flight in data_df.iterrows():
|
| 83 |
+
flights.append({
|
| 84 |
+
"icao24": flight["icao24"],
|
| 85 |
+
"callsign": flight["callsign"],
|
| 86 |
+
"departure": flight["estDepartureAirport"],
|
| 87 |
+
"arrival": flight["estArrivalAirport"]
|
| 88 |
+
})
|
| 89 |
+
return pd.DataFrame(flights)
|
| 90 |
+
|
| 91 |
+
def fetch_aviation_API_airlines_endpoint():
|
| 92 |
+
"""
|
| 93 |
+
Fetches airline data from the AviationStack API airlines endpoint.
|
| 94 |
+
|
| 95 |
+
Parameters:
|
| 96 |
+
- None
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
- dict: The JSON response from the AviationStack API containing the airline data.
|
| 100 |
+
"""
|
| 101 |
+
#api_key = os.environ.get("AVIATION_KEY") # Retrieve the API key (when running on HuggingFace)
|
| 102 |
+
# Comment the line above and uncomment the two lines below if you are running the app locally (not on HuggingFace) and have a .env file with the AviationStack API key
|
| 103 |
+
load_dotenv()
|
| 104 |
+
api_key = os.getenv("AVIATION_KEY") # Retrieve the API key
|
| 105 |
+
url = f"https://api.aviationstack.com/v1/airlines?access_key={api_key}"
|
| 106 |
+
response = requests.get(url)
|
| 107 |
+
return response.json()
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
if __name__ == "__main__":
|
| 111 |
+
print("Fetching live flight data from OpenSky…")
|
| 112 |
+
try:
|
| 113 |
+
df = fetch_opensky_snapshot()
|
| 114 |
+
print(f"Fetched {len(df)} flights at {df.attrs['timestamp']}")
|
| 115 |
+
print(df.head())
|
| 116 |
+
|
| 117 |
+
df_2 = fetch_rdu_departures(hours=6)
|
| 118 |
+
print(f"Fetched {len(df)} flights at {df.attrs['timestamp']}")
|
| 119 |
+
print(df.head())
|
| 120 |
+
except Exception as e:
|
| 121 |
+
print("Error:", e)
|
| 122 |
+
|
| 123 |
+
print("Fetching airline data from AviationStack…")
|
| 124 |
+
try:
|
| 125 |
+
airline_data = fetch_aviation_API_airlines_endpoint()
|
| 126 |
+
print(f"Fetched {len(airline_data.get('data', []))} airlines")
|
| 127 |
+
print(airline_data)
|
| 128 |
+
except Exception as e:
|
| 129 |
+
print("Error:", e)
|
src/streamlit_app.py
CHANGED
|
@@ -1,40 +1,293 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
import pandas as pd
|
| 4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
""
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
"
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
color=alt.Color("idx", legend=None, scale=alt.Scale()),
|
| 39 |
-
size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
|
| 40 |
-
))
|
|
|
|
| 1 |
+
# streamlit_app.py
|
| 2 |
+
|
| 3 |
import pandas as pd
|
| 4 |
import streamlit as st
|
| 5 |
+
import matplotlib.pyplot as plt
|
| 6 |
+
from fetchapi import fetch_opensky_snapshot, fetch_rdu_departures, fetch_aviation_API_airlines_endpoint
|
| 7 |
+
import pandas as pd
|
| 8 |
+
|
| 9 |
+
st.set_page_config(page_title="Flight Volume by Country (OpenSky)", layout="wide")
|
| 10 |
+
st.title("🌍 Global Flight Snapshot (via OpenSky Network)")
|
| 11 |
+
|
| 12 |
+
st.caption("Showing a snapshot of the most recent ~1,800 aircraft globally. Data is live and limited by OpenSky’s API.")
|
| 13 |
+
|
| 14 |
+
run = st.button("Fetch Live Flights")
|
| 15 |
+
|
| 16 |
+
# ---------- Main ----------
|
| 17 |
+
if run:
|
| 18 |
+
st.info("Fetching live data from OpenSky…")
|
| 19 |
+
try:
|
| 20 |
+
df = fetch_opensky_snapshot()
|
| 21 |
+
except Exception as e:
|
| 22 |
+
st.error(f"Failed to fetch data: {type(e).__name__} -> {e}")
|
| 23 |
+
st.stop()
|
| 24 |
+
|
| 25 |
+
st.metric("Flights in snapshot", len(df))
|
| 26 |
+
|
| 27 |
+
if df.empty:
|
| 28 |
+
st.warning("No flights found in snapshot.")
|
| 29 |
+
st.stop()
|
| 30 |
+
|
| 31 |
+
# Aggregate by country
|
| 32 |
+
summary = df.groupby("origin_country").size().reset_index(name="flights")
|
| 33 |
+
summary = summary.sort_values("flights", ascending=False).head(30)
|
| 34 |
+
|
| 35 |
+
# ---------- Plot Top 30 Countries ----------
|
| 36 |
+
st.subheader("✈️ Top 30 Countries by Active Flights")
|
| 37 |
+
|
| 38 |
+
fig, ax = plt.subplots(figsize=(10, 8))
|
| 39 |
+
ax.barh(summary["origin_country"], summary["flights"])
|
| 40 |
+
ax.set_xlabel("Flights (current snapshot)")
|
| 41 |
+
ax.set_ylabel("Country")
|
| 42 |
+
ax.set_title("Top 30 Countries by Active Flights")
|
| 43 |
+
ax.invert_yaxis() # Largest at top
|
| 44 |
+
st.pyplot(fig)
|
| 45 |
+
|
| 46 |
+
# ---------- Plot Flight Scatter Map ----------
|
| 47 |
+
st.subheader("🌐 Flight Positions (Scatter Map)")
|
| 48 |
+
df_map = df.dropna(subset=["latitude", "longitude"])
|
| 49 |
+
|
| 50 |
+
if df_map.empty:
|
| 51 |
+
st.warning("No geolocation data available for mapping.")
|
| 52 |
+
else:
|
| 53 |
+
fig2, ax2 = plt.subplots(figsize=(12, 6))
|
| 54 |
+
ax2.scatter(df_map["longitude"], df_map["latitude"], s=2, alpha=0.5)
|
| 55 |
+
ax2.set_title("Global Flight Positions")
|
| 56 |
+
ax2.set_xlabel("Longitude")
|
| 57 |
+
ax2.set_ylabel("Latitude")
|
| 58 |
+
st.pyplot(fig2)
|
| 59 |
+
|
| 60 |
+
# with st.expander("Raw Country Data"):
|
| 61 |
+
# st.dataframe(summary)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
############# Omkar's Code #############
|
| 65 |
+
st.header("📊 Other Analyses (OpenSky)")
|
| 66 |
+
|
| 67 |
+
col1, col2, col3 = st.columns(3)
|
| 68 |
+
|
| 69 |
+
# 1. Flights by Altitude Band
|
| 70 |
+
with col1:
|
| 71 |
+
if "baro_altitude" in df.columns:
|
| 72 |
+
# Convert meters to feet
|
| 73 |
+
df["alt_ft"] = df["baro_altitude"] * 3.28084
|
| 74 |
+
|
| 75 |
+
bins = [-1000, 10000, 20000, 30000, 60000] # feet
|
| 76 |
+
labels = ["<10k", "10–20k", "20–30k", "30k+"]
|
| 77 |
+
df["alt_band"] = pd.cut(df["alt_ft"], bins=bins, labels=labels)
|
| 78 |
+
|
| 79 |
+
alt_counts = df["alt_band"].value_counts().reindex(labels, fill_value=0)
|
| 80 |
+
|
| 81 |
+
fig_alt, ax_alt = plt.subplots(figsize=(4,3))
|
| 82 |
+
ax_alt.bar(alt_counts.index, alt_counts.values, color="mediumseagreen", alpha=0.8)
|
| 83 |
+
ax_alt.set_title("Flights by Altitude Band (feet)")
|
| 84 |
+
ax_alt.set_xlabel("Altitude band")
|
| 85 |
+
ax_alt.set_ylabel("Aircraft")
|
| 86 |
+
st.pyplot(fig_alt, use_container_width=False)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
# 2. Top Airlines by Callsign Prefix
|
| 90 |
+
with col2:
|
| 91 |
+
if "callsign" in df.columns:
|
| 92 |
+
# Clean callsigns
|
| 93 |
+
cs = df["callsign"].astype(str).str.upper().str.strip()
|
| 94 |
+
|
| 95 |
+
# Extract exactly 3 leading letters (ICAO airline code)
|
| 96 |
+
prefix = cs.str.extract(r'^([A-Z]{3})', expand=False)
|
| 97 |
+
|
| 98 |
+
# Tag N-registered private aircraft
|
| 99 |
+
n_reg_mask = prefix.isna() & cs.str.match(r'^N[0-9A-Z]+', na=False)
|
| 100 |
+
prefix = prefix.where(~n_reg_mask, "Private/GA")
|
| 101 |
+
|
| 102 |
+
# Fill remaining blanks
|
| 103 |
+
prefix = prefix.fillna("No Name")
|
| 104 |
+
|
| 105 |
+
# Map common airline codes → names
|
| 106 |
+
airline_map = {
|
| 107 |
+
"AAL": "American Airlines",
|
| 108 |
+
"DAL": "Delta Air Lines",
|
| 109 |
+
"UAL": "United Airlines",
|
| 110 |
+
"SWA": "Southwest Airlines",
|
| 111 |
+
"JBU": "Jet Blue Airways",
|
| 112 |
+
"FFT": "Frontier Airlines",
|
| 113 |
+
"NKS": "Spirit Airlines",
|
| 114 |
+
"ASA": "Alaska Airlines",
|
| 115 |
+
"UPS": "UPS Airlines",
|
| 116 |
+
"FDX": "Fed Ex Express",
|
| 117 |
+
"BAW": "British Airways",
|
| 118 |
+
"DLH": "Lufthansa",
|
| 119 |
+
"AFR": "Air France",
|
| 120 |
+
"KLM": "KLM Royal Dutch Airlines",
|
| 121 |
+
"UAE": "Emirates",
|
| 122 |
+
"Private/GA": "Private/GA",
|
| 123 |
+
"No Name": "No Name",
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
# Replace codes with names where possible
|
| 127 |
+
airline_name = prefix.map(airline_map).fillna(prefix)
|
| 128 |
+
|
| 129 |
+
airline_counts = airline_name.value_counts().head(15)
|
| 130 |
+
|
| 131 |
+
fig_airline, ax_airline = plt.subplots(figsize=(8, 6))
|
| 132 |
+
ax_airline.barh(airline_counts.index, airline_counts.values, color="slateblue", alpha=0.85)
|
| 133 |
+
ax_airline.set_title("Top 15 Airlines by Callsign")
|
| 134 |
+
ax_airline.set_xlabel("Aircraft")
|
| 135 |
+
ax_airline.invert_yaxis()
|
| 136 |
+
st.pyplot(fig_airline, use_container_width=False)
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
# 3. Flights by Broad Region (Pie)
|
| 140 |
+
with col3:
|
| 141 |
+
if {"latitude","longitude"}.issubset(df.columns):
|
| 142 |
+
df["region"] = pd.cut(
|
| 143 |
+
df["longitude"],
|
| 144 |
+
bins=[-180, -30, 60, 180],
|
| 145 |
+
labels=["Americas", "Europe/Africa", "Asia-Pacific"]
|
| 146 |
+
)
|
| 147 |
+
region_counts = df["region"].value_counts()
|
| 148 |
+
|
| 149 |
+
fig_region, ax_region = plt.subplots(figsize=(3.5,3.5))
|
| 150 |
+
ax_region.pie(region_counts.values, labels=region_counts.index, autopct="%1.0f%%")
|
| 151 |
+
ax_region.set_title("Regions")
|
| 152 |
+
st.pyplot(fig_region, use_container_width=False)
|
| 153 |
+
else:
|
| 154 |
+
st.info("Click 'Fetch Live Flights' to view global snapshot.")
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
## ---------- RDU Specific Analysis (Arnav) ---------- ##
|
| 159 |
+
st.header("🛫 Raleigh-Durham (RDU) Airport Stats")
|
| 160 |
+
run_rdu = st.button("Fetch RDU Stats")
|
| 161 |
+
|
| 162 |
+
if run_rdu:
|
| 163 |
+
with st.spinner("Fetching RDU-specific flight data..."):
|
| 164 |
+
df_departures = fetch_rdu_departures(hours=6)
|
| 165 |
+
|
| 166 |
+
st.metric("Departures (last 6h)", len(df_departures))
|
| 167 |
+
|
| 168 |
+
if not df_departures.empty:
|
| 169 |
+
# ---- Top Airlines ----
|
| 170 |
+
def airline_from_callsign(callsign):
|
| 171 |
+
if not callsign or len(callsign) < 3:
|
| 172 |
+
return "Unknown"
|
| 173 |
+
prefix = callsign[:3].upper()
|
| 174 |
+
mapping = {
|
| 175 |
+
"AAL": "American Airlines",
|
| 176 |
+
"DAL": "Delta",
|
| 177 |
+
"UAL": "United",
|
| 178 |
+
"SWA": "Southwest",
|
| 179 |
+
"JBU": "JetBlue",
|
| 180 |
+
"FDX": "FedEx",
|
| 181 |
+
"UPS": "UPS",
|
| 182 |
+
"NKS": "Spirit",
|
| 183 |
+
"ASA": "Alaska",
|
| 184 |
+
"FFT": "Frontier"
|
| 185 |
+
}
|
| 186 |
+
return mapping.get(prefix, prefix)
|
| 187 |
+
|
| 188 |
+
df_departures["Airline"] = df_departures["callsign"].apply(airline_from_callsign)
|
| 189 |
+
top_airlines = df_departures["Airline"].value_counts().head(10).reset_index()
|
| 190 |
+
top_airlines.columns = ["Airline", "Flights"]
|
| 191 |
+
st.subheader("🏢 Top 10 Airlines from RDU (last 6h)")
|
| 192 |
+
st.bar_chart(top_airlines.set_index("Airline"))
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
#### ----------- Airline Profile Comparison (AviationAPI - Ethan Dominic's Code) ----------- ####
|
| 196 |
+
airline_data = fetch_aviation_API_airlines_endpoint()
|
| 197 |
+
|
| 198 |
+
def get_airline_feature_dict(feature_type, cast_type):
|
| 199 |
+
"""
|
| 200 |
+
Return a dictionary of airline names along with their values for the specified feature type.
|
| 201 |
+
|
| 202 |
+
Parameters:
|
| 203 |
+
- feature_type (str): The specified feature type to extract (e.g., "fleet_size", "fleet_average_age", "date_founded").
|
| 204 |
+
- cast_type (str): The type to cast the feature value to ("int", "float", or "str")
|
| 205 |
+
|
| 206 |
+
Returns:
|
| 207 |
+
- dict: A dictionary whose keys are airline names and values are the corresponding feature values.
|
| 208 |
+
"""
|
| 209 |
+
airline_feature_dict = {}
|
| 210 |
+
for i in range(len(airline_data["data"])):
|
| 211 |
+
airline_name = airline_data["data"][i]["airline_name"]
|
| 212 |
+
if airline_data["data"][i][feature_type] is not None and airline_data["data"][i][feature_type] != "":
|
| 213 |
+
if cast_type == "int":
|
| 214 |
+
airline_feature_value = int(airline_data["data"][i][feature_type])
|
| 215 |
+
elif cast_type == "str":
|
| 216 |
+
airline_feature_value = str(airline_data["data"][i][feature_type])
|
| 217 |
+
else:
|
| 218 |
+
airline_feature_value = float(airline_data["data"][i][feature_type])
|
| 219 |
+
airline_feature_dict[airline_name] = airline_feature_value
|
| 220 |
+
return airline_feature_dict
|
| 221 |
+
|
| 222 |
+
def plot_bar_graph(feature_series, title, ylabel, bottom_ylim=0):
|
| 223 |
+
"""
|
| 224 |
+
Plot a bar graph for the given feature Series.
|
| 225 |
+
|
| 226 |
+
Parameters:
|
| 227 |
+
- feature_series (pd.Series): A pandas Series where the index is airline names and the values are the feature values.
|
| 228 |
+
- title (str): The desired title of the graph.
|
| 229 |
+
- ylabel (str): The desired label for the y-axis.
|
| 230 |
+
- bottom_ylim (int, optional): The minimum limit for the y-axis. Defaults to 0.
|
| 231 |
+
|
| 232 |
+
Returns:
|
| 233 |
+
- None: Displays the bar graph using Streamlit.
|
| 234 |
+
"""
|
| 235 |
+
fig, ax = plt.subplots()
|
| 236 |
+
bars = ax.bar(feature_series.index.astype(str), feature_series.values)
|
| 237 |
+
ax.set_title(title)
|
| 238 |
+
ax.set_xlabel("Airline")
|
| 239 |
+
ax.set_ylabel(ylabel)
|
| 240 |
+
ax.bar(feature_series.index, feature_series.values)
|
| 241 |
+
ax.bar_label(bars, padding=3)
|
| 242 |
+
plt.xticks(rotation=90)
|
| 243 |
+
plt.ylim(bottom=bottom_ylim)
|
| 244 |
+
st.pyplot(fig)
|
| 245 |
+
|
| 246 |
+
# Main Program Execution
|
| 247 |
+
st.title("Airline Profile Comparison")
|
| 248 |
+
|
| 249 |
+
comparison_option = st.radio(
|
| 250 |
+
"Pick the type of comparison you would like to see: ",
|
| 251 |
+
("Fleet Size", "Fleet Average Age", "Founding Year")
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
countries_of_origin = pd.Series(get_airline_feature_dict("country_name", "str"))
|
| 255 |
+
country_filters = countries_of_origin.unique().tolist()
|
| 256 |
+
country_filters.append("All Countries") # Add option for user to see all countries
|
| 257 |
+
country_filter_option = st.radio(
|
| 258 |
+
"Pick a country of origin to filter by: ",
|
| 259 |
+
(country_filters)
|
| 260 |
+
)
|
| 261 |
|
| 262 |
+
if country_filter_option == "All Countries":
|
| 263 |
+
if comparison_option == "Fleet Size":
|
| 264 |
+
fleet_sizes = (pd.Series(get_airline_feature_dict("fleet_size", "int"))).dropna() # Remove airlines with no fleet size data
|
| 265 |
+
sorted_fleet_sizes = fleet_sizes.sort_values(ascending=True)
|
| 266 |
+
top10_sorted_fleet_sizes = sorted_fleet_sizes.tail(10) # Get the top 10 largest airlines by fleet size
|
| 267 |
+
plot_bar_graph(top10_sorted_fleet_sizes, "Airline Fleet Sizes", "Fleet Size")
|
| 268 |
+
elif comparison_option == "Fleet Average Age":
|
| 269 |
+
fleet_avg_ages = (pd.Series(get_airline_feature_dict("fleet_average_age", "float"))).dropna() # Remove airlines with no fleet average age data
|
| 270 |
+
sorted_fleet_avg_ages = fleet_avg_ages.sort_values(ascending=True)
|
| 271 |
+
top10_sorted_fleet_avg_ages = sorted_fleet_avg_ages.head(10) # Get the top 10 youngest airlines by fleet average age
|
| 272 |
+
plot_bar_graph(top10_sorted_fleet_avg_ages, "Airline Fleet Average Ages", "Fleet Average Age")
|
| 273 |
+
elif comparison_option == "Founding Year":
|
| 274 |
+
founding_years = (pd.Series(get_airline_feature_dict("date_founded", "int"))).dropna() # Remove airlines with no founding year data
|
| 275 |
+
sorted_founding_years = founding_years.sort_values(ascending=True)
|
| 276 |
+
top10_sorted_founding_years = sorted_founding_years.head(10) # Get the top 10 oldest airlines by founding year
|
| 277 |
+
plot_bar_graph(top10_sorted_founding_years, "Airline Founding Years", "Founding Year", bottom_ylim=1900) # Set y-axis minimum so years before 1900 since no airlines were founded before then
|
| 278 |
+
else:
|
| 279 |
+
if comparison_option == "Fleet Size":
|
| 280 |
+
fleet_sizes = (pd.Series(get_airline_feature_dict("fleet_size", "int"))).dropna() # Remove airlines with no fleet size data
|
| 281 |
+
filtered_fleet_sizes = fleet_sizes[countries_of_origin == country_filter_option] # Ensure only airlines from the selected country are included
|
| 282 |
+
sorted_fleet_sizes = filtered_fleet_sizes.sort_values(ascending=True)
|
| 283 |
+
plot_bar_graph(sorted_fleet_sizes, "Airline Fleet Sizes", "Fleet Size")
|
| 284 |
+
elif comparison_option == "Fleet Average Age":
|
| 285 |
+
fleet_avg_ages = (pd.Series(get_airline_feature_dict("fleet_average_age", "float"))).dropna() # Remove airlines with no fleet average age data
|
| 286 |
+
filtered_fleet_avg_ages = fleet_avg_ages[countries_of_origin == country_filter_option] # Ensure only airlines from the selected country are included
|
| 287 |
+
sorted_fleet_avg_ages = filtered_fleet_avg_ages.sort_values(ascending=True)
|
| 288 |
+
plot_bar_graph(sorted_fleet_avg_ages, "Airline Fleet Average Ages", "Fleet Average Age")
|
| 289 |
+
elif comparison_option == "Founding Year":
|
| 290 |
+
founding_years = (pd.Series(get_airline_feature_dict("date_founded", "int"))).dropna() # Remove airlines with no founding year data
|
| 291 |
+
filtered_founding_years = founding_years[countries_of_origin == country_filter_option] # Ensure only airlines from the selected country are included
|
| 292 |
+
sorted_founding_years = filtered_founding_years.sort_values(ascending=True)
|
| 293 |
+
plot_bar_graph(sorted_founding_years, "Airline Founding Years", "Founding Year", bottom_ylim=1900) # Set y-axis minimum so years before 1900 since no airlines were founded before then
|
|
|
|
|
|
|
|
|