opensky / app.py
immunobiotech's picture
Update app.py
24c922b verified
raw
history blame
8.72 kB
import gradio as gr
import folium
from folium import plugins
import requests
import pandas as pd
from datetime import datetime
import time
import branca.colormap as cm
import numpy as np
import io
from PIL import Image
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import threading
# OpenSky API URL
BASE_URL = "https://opensky-network.org/api"
# Aircraft photos API (예시 - 실제 구현시에는 적절한 API로 대체 필요)
AIRCRAFT_PHOTOS_API = "https://api.planespotters.net/pub/photos/hex/{icao24}"
def get_aircraft_photo(icao24):
"""Get aircraft photo from Planespotters API"""
try:
response = requests.get(AIRCRAFT_PHOTOS_API.format(icao24=icao24))
data = response.json()
if data.get('photos'):
return data['photos'][0]['thumbnail_large']['src']
except:
# 기본 항공기 이미지 URL 반환
return "https://example.com/default-aircraft.jpg"
def get_states(bounds=None):
"""Get current aircraft states from OpenSky Network"""
params = {}
if bounds:
params.update({
'lamin': bounds[0],
'lomin': bounds[1],
'lamax': bounds[2],
'lomax': bounds[3]
})
try:
response = requests.get(f"{BASE_URL}/states/all", params=params)
data = response.json()
return data
except Exception as e:
print(f"Error fetching data: {e}")
return None
def create_monitoring_dashboard(data):
"""Create monitoring dashboard using Plotly"""
if not data or 'states' not in data:
return None
states = data['states']
# Create subplots
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('Altitude Distribution', 'Speed Distribution',
'Aircraft by Country', 'Aircraft Categories')
)
# Altitude distribution
altitudes = [state[7] for state in states if state[7]]
fig.add_trace(
go.Histogram(x=altitudes, name="Altitude"),
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"),
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"),
row=2, col=1
)
# Aircraft categories
categories = pd.Series([state[17] for state in states if state[17]]).value_counts()
fig.add_trace(
go.Pie(labels=categories.index, values=categories.values, name="Categories"),
row=2, col=2
)
fig.update_layout(
height=800,
showlegend=False,
template="plotly_dark",
paper_bgcolor='rgba(0,0,0,0)',
plot_bgcolor='rgba(0,0,0,0)'
)
return fig
def create_map(region="world"):
"""Create aircraft tracking map"""
bounds = {
"world": None,
"europe": [35.0, -15.0, 60.0, 40.0],
"north_america": [25.0, -130.0, 50.0, -60.0],
"asia": [10.0, 60.0, 50.0, 150.0]
}
data = get_states(bounds.get(region))
if not data or 'states' not in data:
return None, None, "Failed to fetch aircraft data"
m = folium.Map(
location=[30, 0],
zoom_start=3,
tiles='CartoDB dark_matter'
)
heat_data = []
# Add aircraft markers
for state in data['states']:
if state[6] and state[5]:
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'
icao24 = state[0]
heat_data.append([lat, lon, 1])
# Get aircraft photo
photo_url = get_aircraft_photo(icao24)
popup_content = f"""
<div style="font-family: Arial; width: 300px;">
<h4 style="color: #4a90e2;">Flight Information</h4>
<img src="{photo_url}" style="width: 100%; max-height: 200px; object-fit: cover; margin-bottom: 10px;">
<p><b>Callsign:</b> {callsign}</p>
<p><b>ICAO24:</b> {icao24}</p>
<p><b>Altitude:</b> {altitude}m</p>
<p><b>Velocity:</b> {velocity}m/s</p>
<p><b>Origin:</b> {state[2]}</p>
<p><b>Status:</b> {'On Ground' if state[8] else 'In Air'}</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]}deg)">✈️</div>
''',
icon_size=(20, 20)
)
).add_to(m)
plugins.HeatMap(heat_data, radius=15).add_to(m)
folium.LayerControl().add_to(m)
# Create monitoring dashboard
dashboard = create_monitoring_dashboard(data)
# Create statistics
total_aircraft = len(data['states'])
countries = len(set(state[2] for state in data['states'] if state[2]))
avg_altitude = np.mean([state[7] for state in data['states'] if state[7]]) if data['states'] else 0
in_air = sum(1 for state in data['states'] if not state[8])
on_ground = sum(1 for state in data['states'] if state[8])
stats = f"""
📊 Real-time Statistics:
• Total Aircraft: {total_aircraft}
• Aircraft in Air: {in_air}
• Aircraft on Ground: {on_ground}
• Countries: {countries}
• Average Altitude: {avg_altitude:.0f}m
🔄 Last Updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
"""
return m._repr_html_(), dashboard, stats
# Custom CSS
custom_css = """
.gradio-container {
background: linear-gradient(135deg, #1a1a1a, #2d2d2d) !important;
color: #ffffff !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;
}
.title-text {
text-align: center !important;
color: #ffffff !important;
font-size: 2.5em !important;
margin-bottom: 0.5em !important;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5) !important;
}
.dashboard {
background: rgba(0, 0, 0, 0.3) !important;
border-radius: 10px !important;
padding: 20px !important;
}
"""
with gr.Blocks(css=custom_css) as demo:
gr.HTML(
"""
<div class="title-text">🛩️ Global Aircraft Tracker</div>
"""
)
with gr.Row():
with gr.Column(scale=2):
region_select = gr.Dropdown(
choices=["world", "europe", "north_america", "asia"],
value="world",
label="Select Region"
)
with gr.Column(scale=1):
refresh_btn = gr.Button("🔄 Refresh")
auto_refresh = gr.Checkbox(label="Auto Refresh", value=False)
with gr.Row():
with gr.Column(scale=2):
map_html = gr.HTML()
with gr.Column(scale=1):
stats_text = gr.Textbox(label="Statistics", lines=8)
with gr.Row():
dashboard_plot = gr.Plot(label="Monitoring Dashboard")
def update_map(region):
return create_map(region)
def auto_refresh_function(auto_refresh_state):
while auto_refresh_state:
time.sleep(30) # 30초 대기
map_data, dashboard_data, stats_data = create_map(region_select.value)
map_html.update(value=map_data)
dashboard_plot.update(value=dashboard_data)
stats_text.update(value=stats_data)
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]
)
def handle_auto_refresh(auto_refresh_state):
if auto_refresh_state:
threading.Thread(target=auto_refresh_function, args=(True,), daemon=True).start()
auto_refresh.change(
fn=handle_auto_refresh,
inputs=[auto_refresh]
)
# Initial map load
map_html, dashboard_plot, stats_text = create_map("world")
demo.launch()