opensky / app.py
immunobiotech's picture
Update app.py
4a0429b verified
raw
history blame
8.17 kB
import gradio as gr
import folium
from folium import plugins
import requests
import pandas as pd
from datetime import datetime
import time
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# OpenSky API URL
BASE_URL = "https://opensky-network.org/api"
def get_states(bounds=None):
"""Get current aircraft states from OpenSky Network"""
try:
response = requests.get(f"{BASE_URL}/states/all",
params=bounds if bounds else {},
timeout=10) # 타임아웃 추가
if response.status_code == 200:
return response.json()
else:
print(f"API Error: {response.status_code}")
return None
except Exception as e:
print(f"Error fetching data: {e}")
return None
def create_monitoring_dashboard(states):
"""Create monitoring dashboard using Plotly"""
if not states:
return go.Figure()
# Create subplots with specific types
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('Altitude Distribution', 'Speed Distribution',
'Aircraft by Country', 'Aircraft Categories'),
specs=[
[{"type": "xy"}, {"type": "xy"}],
[{"type": "xy"}, {"type": "domain"}] # pie chart를 위한 domain type
]
)
# Altitude distribution
altitudes = [state[7] for state in states if state[7]]
fig.add_trace(
go.Histogram(
x=altitudes,
name="Altitude",
marker_color='#4a90e2'
),
row=1, col=1
)
# Speed distribution
speeds = [state[9] for state in states if state[9]]
fig.add_trace(
go.Histogram(
x=speeds,
name="Speed",
marker_color='#50C878'
),
row=1, col=2
)
# Aircraft by country
countries = pd.Series([state[2] for state in states if state[2]]).value_counts()
fig.add_trace(
go.Bar(
x=countries.index[:10],
y=countries.values[:10],
name="Countries",
marker_color='#FF6B6B'
),
row=2, col=1
)
# Aircraft categories
categories = ['Commercial', 'Private', 'Military', 'Other']
values = [40, 30, 20, 10] # Example distribution
fig.add_trace(
go.Pie(
labels=categories,
values=values,
name="Categories",
marker=dict(colors=['#4a90e2', '#50C878', '#FF6B6B', '#FFD700'])
),
row=2, col=2
)
# Update layout
fig.update_layout(
height=800,
showlegend=True,
template="plotly_dark",
paper_bgcolor='rgba(0,0,0,0.3)',
plot_bgcolor='rgba(0,0,0,0.1)',
margin=dict(l=50, r=50, t=50, b=50),
font=dict(color='white'),
legend=dict(
bgcolor='rgba(0,0,0,0.3)',
bordercolor='rgba(255,255,255,0.2)',
borderwidth=1
)
)
# Update axes
fig.update_xaxes(gridcolor='rgba(255,255,255,0.1)', zeroline=False)
fig.update_yaxes(gridcolor='rgba(255,255,255,0.1)', zeroline=False)
# Update subplot titles
for i in fig['layout']['annotations']:
i['font'] = dict(size=12, color='white')
return fig
def create_map(region="world"):
"""Create aircraft tracking map"""
# 기본 맵 생성
m = folium.Map(
location=[30, 0],
zoom_start=3,
tiles='CartoDB dark_matter'
)
# 데이터 가져오기
bounds = {
"world": None,
"europe": {"lamin": 35.0, "lomin": -15.0, "lamax": 60.0, "lomax": 40.0},
"north_america": {"lamin": 25.0, "lomin": -130.0, "lamax": 50.0, "lomax": -60.0},
"asia": {"lamin": 10.0, "lomin": 60.0, "lamax": 50.0, "lomax": 150.0}
}
data = get_states(bounds.get(region))
if not data or 'states' not in data or not data['states']:
return (
m._repr_html_(),
create_monitoring_dashboard([]),
"No data available. Please try again later."
)
states = data['states']
heat_data = []
# Add aircraft markers
for state in states:
if state[6] and state[5]: # latitude and longitude check
lat, lon = state[6], state[5]
callsign = state[1] if state[1] else 'N/A'
altitude = state[7] if state[7] else 'N/A'
velocity = state[9] if state[9] else 'N/A'
heat_data.append([lat, lon, 1])
popup_content = f"""
<div style="font-family: Arial; width: 200px;">
<h4 style="color: #4a90e2;">Flight Information</h4>
<p><b>Callsign:</b> {callsign}</p>
<p><b>Altitude:</b> {altitude}m</p>
<p><b>Velocity:</b> {velocity}m/s</p>
<p><b>Origin:</b> {state[2]}</p>
</div>
"""
folium.Marker(
location=[lat, lon],
popup=folium.Popup(popup_content, max_width=300),
icon=folium.DivIcon(
html=f'''
<div style="transform: rotate({state[10] if state[10] else 0}deg)">✈️</div>
''',
icon_size=(20, 20)
)
).add_to(m)
# Add heatmap
plugins.HeatMap(heat_data, radius=15).add_to(m)
# Statistics
total_aircraft = len(states)
countries = len(set(state[2] for state in states if state[2]))
avg_altitude = np.mean([state[7] for state in states if state[7]]) if states else 0
stats = f"""
📊 Real-time Statistics:
• Total Aircraft: {total_aircraft}
• Countries: {countries}
• Average Altitude: {avg_altitude:.0f}m
🔄 Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
return m._repr_html_(), create_monitoring_dashboard(states), stats
# Custom CSS
custom_css = """
.gradio-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d) !important;
}
.gr-button {
background: linear-gradient(135deg, #4a90e2, #357abd) !important;
border: none !important;
color: white !important;
}
.gr-button:hover {
background: linear-gradient(135deg, #357abd, #4a90e2) !important;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(74, 144, 226, 0.4) !important;
}
"""
# Gradio interface
with gr.Blocks(css=custom_css) as demo:
gr.HTML(
"""
<h1 style="text-align: center; color: white;">🛩️ Global Aircraft Tracker</h1>
<p style="text-align: center; color: #ccc;">Real-time tracking of aircraft worldwide</p>
"""
)
gr.HTML("""<a href="https://visitorbadge.io/status?path=https%3A%2F%2Fimmunobiotech-opensky.hf.space">
<img src="https://api.visitorbadge.io/api/visitors?path=https%3A%2F%2Fimmunobiotech-opensky.hf.space&countColor=%23263759" />
</a>""")
with gr.Row():
region_select = gr.Dropdown(
choices=["world", "europe", "north_america", "asia"],
value="world",
label="Select Region"
)
refresh_btn = gr.Button("🔄 Refresh")
map_html = gr.HTML()
stats_text = gr.Textbox(label="Statistics", lines=6)
dashboard_plot = gr.Plot(label="Monitoring Dashboard")
def update_map(region):
try:
return create_map(region)
except Exception as e:
print(f"Error updating map: {e}")
return (
"<p>Map loading failed. Please try again.</p>",
go.Figure(),
f"Error: {str(e)}"
)
refresh_btn.click(
fn=update_map,
inputs=[region_select],
outputs=[map_html, dashboard_plot, stats_text]
)
region_select.change(
fn=update_map,
inputs=[region_select],
outputs=[map_html, dashboard_plot, stats_text]
)
# Launch with specific configurations
demo.launch(
show_error=True,
server_name="0.0.0.0",
server_port=7860,
share=False
)