
# fliapp.py
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, WMTSTileSource
import numpy as np
import random
# Helper: WGS84 → Web Mercator (vectorized)
def wgs84_to_web_mercator(lon, lat):
k = 6378137
x = lon * (k * np.pi / 180.0)
y = np.log(np.tan((90 + lat) * np.pi / 360.0)) * k
return x, y
# Major global cities (optimized selection)
CITIES = {
"New York": (-74.006, 40.7128),
"Los Angeles": (-118.2437, 34.0522),
"London": (-0.1276, 51.5074),
"Paris": (2.3522, 48.8566),
"Tokyo": (139.6917, 35.6895),
"Dubai": (55.2708, 25.2048),
"Singapore": (103.8198, 1.3521),
"Sydney": (151.2093, -33.8688),
"São Paulo": (-46.6333, -23.5505),
"Mumbai": (72.8777, 19.0760),
"Beijing": (116.4074, 39.9042),
"Moscow": (37.6173, 55.7558),
"Istanbul": (28.9784, 41.0082),
"Bangkok": (100.5018, 13.7563),
"Toronto": (-79.3832, 43.6532),
"Hong Kong": (114.1694, 22.3193),
"Shanghai": (121.4737, 31.2304),
"Seoul": (126.9780, 37.5665),
"Madrid": (-3.7038, 40.4168),
"Rome": (12.4964, 41.9028),
"Amsterdam": (4.9041, 52.3676),
"Frankfurt": (8.6821, 50.1109),
"Chicago": (-87.6298, 41.8781),
"San Francisco": (-122.4194, 37.7749),
"Miami": (-80.1918, 25.7617),
"Delhi": (77.1025, 28.7041),
"Mexico City": (-99.1332, 19.4326),
"Cairo": (31.2357, 30.0444),
"Johannesburg": (28.0473, -26.2041),
"Melbourne": (144.9631, -37.8136)
}
# Optimized flight arc generation (reduced steps for performance)
def generate_flight_arc(lon1, lat1, lon2, lat2, steps=200):
t = np.linspace(0, 1, steps)
# Vectorized great circle interpolation
lon_arc = lon1 + (lon2 - lon1) * t
lat_arc = lat1 + (lat2 - lat1) * t
# Altitude curve
distance = np.sqrt((lon2 - lon1)**2 + (lat2 - lat1)**2)
altitude_factor = min(distance * 0.7, 18)
lat_arc += altitude_factor * np.sin(np.pi * t) * (1 - 0.3 * t)
return wgs84_to_web_mercator(lon_arc, lat_arc)
# Create Bokeh figure
p = figure(
x_range=(-2e7, 2e7),
y_range=(-1e7, 1e7),
x_axis_type="mercator",
y_axis_type="mercator",
width=1400,
height=800,
title="Live Global Flight Tracker",
)
# Dark map
dark_url = "https://basemaps.cartocdn.com/dark_all/{Z}/{X}/{Y}.png"
p.add_tile(WMTSTileSource(url=dark_url))
# Optimized styling
p.title.text_color = "#E0E0E0"
p.title.text_font_size = "18pt"
p.background_fill_color = "#0a0a0a"
p.border_fill_color = "#0a0a0a"
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
# Optimized parameters
N_FLIGHTS = 50
trail_length = 45
# Optimized color palette
FLIGHT_COLORS = [
("#00d4ff", "#0066ff"),
("#ff0080", "#ff6b00"),
("#00ff88", "#00cc66"),
("#ffdd00", "#ff8800"),
("#ff00ff", "#8800ff"),
("#00ffff", "#00aaff"),
]
# Pre-generate city list
city_list = list(CITIES.values())
# Storage for flights
flights = []
for _ in range(N_FLIGHTS):
# Random route
departure = random.choice(city_list)
arrival = random.choice(city_list)
while arrival == departure:
arrival = random.choice(city_list)
lon1, lat1 = departure
lon2, lat2 = arrival
x_arc, y_arc = generate_flight_arc(lon1, lat1, lon2, lat2)
# Color scheme
trail_color, glow_color = random.choice(FLIGHT_COLORS)
# Data sources
trail_src = ColumnDataSource(data=dict(x=[], y=[]))
fade_src = ColumnDataSource(data=dict(x=[], y=[]))
head_src = ColumnDataSource(data=dict(x=[], y=[]))
# Optimized rendering - fewer layers
# Outer glow
p.line('x', 'y', source=trail_src, line_width=5,
line_color=glow_color, line_alpha=0.18)
# Core trail
p.line('x', 'y', source=trail_src, line_width=2,
line_color=trail_color, line_alpha=0.85)
# Fade tail
p.line('x', 'y', source=fade_src, line_width=1.5,
line_color=trail_color, line_alpha=0.25)
# Airplane head - optimized layers
p.scatter('x', 'y', source=head_src, size=18,
color=glow_color, alpha=0.2)
p.scatter('x', 'y', source=head_src, size=8,
color=trail_color, alpha=1.0)
p.scatter('x', 'y', source=head_src, size=3,
color="white", alpha=1.0)
# Store flight data
flights.append({
"x_arc": x_arc,
"y_arc": y_arc,
"trail_src": trail_src,
"fade_src": fade_src,
"head_src": head_src,
"i": random.randint(0, len(x_arc) // 4),
"speed": random.uniform(2.0, 4.0), # speed
"trail_color": trail_color,
"glow_color": glow_color
})
# Optimized update function
def update():
for flight in flights:
i = int(flight["i"])
x_arc, y_arc = flight["x_arc"], flight["y_arc"]
if i < len(x_arc):
# Main trail
start = max(0, i - trail_length)
flight["trail_src"].data = dict(
x=x_arc[start:i],
y=y_arc[start:i]
)
# Fading tail
fade_start = max(0, i - trail_length * 2)
flight["fade_src"].data = dict(
x=x_arc[fade_start:start],
y=y_arc[fade_start:start]
)
# Airplane position
flight["head_src"].data = dict(
x=[x_arc[i]],
y=[y_arc[i]]
)
# Smooth speed curve
progress = i / len(x_arc)
if progress < 0.15: # Takeoff
speed_factor = 0.6 + (progress / 0.15) * 0.4
elif progress > 0.85: # Landing
speed_factor = 0.6 + ((1 - progress) / 0.15) * 0.4
else: # Cruise
speed_factor = 1.0
flight["i"] += flight["speed"] * speed_factor
else:
# Generate new route
departure = random.choice(city_list)
arrival = random.choice(city_list)
while arrival == departure:
arrival = random.choice(city_list)
lon1, lat1 = departure
lon2, lat2 = arrival
flight["x_arc"], flight["y_arc"] = generate_flight_arc(lon1, lat1, lon2, lat2)
flight["i"] = 0
# 30% chance to change color
if random.random() < 0.3:
trail_color, glow_color = random.choice(FLIGHT_COLORS)
flight["trail_color"] = trail_color
flight["glow_color"] = glow_color
# Run at 30fps
curdoc().add_periodic_callback(update, 33)
curdoc().add_root(p)
bokeh serve --show fliapp.py