updated v

This commit is contained in:
2025-07-09 16:47:17 +03:00
parent 35d3bb8442
commit 4fa7ed2a48
3 changed files with 440 additions and 1 deletions

View File

@@ -529,7 +529,9 @@ class Advanced3DGenerator:
frame_path = self.create_pydeck_frame(df, current_index, frame_num)
elif style == 'plotly':
frame_path = self.create_plotly_frame(df, current_index, frame_num)
else: # advanced
elif style == 'google_earth':
frame_path = self.create_google_earth_frame(df, current_index, frame_num)
else: # advanced (default)
frame_path = self.create_advanced_plotly_frame(df, current_index, frame_num)
if frame_path:
@@ -654,6 +656,237 @@ class Advanced3DGenerator:
progress_callback(-1, f"Error: {e}")
return False
def create_google_earth_frame(self, df, current_index, frame_num):
"""
Create a Google Earth-style flythrough frame with realistic terrain and camera following
"""
if not PLOTLY_AVAILABLE:
raise ImportError("Plotly is required for Google Earth-style frames")
# Get current position and context
current_row = df.iloc[current_index]
current_lat = current_row['latitude']
current_lon = current_row['longitude']
current_alt = current_row.get('elevation', 100)
# Get track up to current position (progressive reveal)
track_so_far = df.iloc[:current_index + 1]
# Calculate terrain bounds around the track
lat_margin = 0.02 # degrees
lon_margin = 0.02 # degrees
min_lat = track_so_far['latitude'].min() - lat_margin
max_lat = track_so_far['latitude'].max() + lat_margin
min_lon = track_so_far['longitude'].min() - lon_margin
max_lon = track_so_far['longitude'].max() + lon_margin
# Generate terrain mesh
terrain_resolution = 40
lat_range = np.linspace(min_lat, max_lat, terrain_resolution)
lon_range = np.linspace(min_lon, max_lon, terrain_resolution)
lat_mesh, lon_mesh = np.meshgrid(lat_range, lon_range)
# Generate realistic terrain heights
terrain_heights = self.generate_terrain_heights(lat_mesh, lon_mesh, current_lat, current_lon)
# Create the figure
fig = go.Figure()
# Add terrain surface
fig.add_trace(
go.Surface(
x=lon_mesh,
y=lat_mesh,
z=terrain_heights,
colorscale=[
[0.0, 'rgb(139,69,19)'], # Brown (low elevation)
[0.2, 'rgb(160,82,45)'], # Saddle brown
[0.4, 'rgb(107,142,35)'], # Olive drab (medium)
[0.6, 'rgb(34,139,34)'], # Forest green
[0.8, 'rgb(105,105,105)'], # Dim gray (high)
[1.0, 'rgb(255,255,255)'] # White (peaks)
],
opacity=0.9,
showscale=False,
name='Terrain',
lighting=dict(
ambient=0.3,
diffuse=0.8,
specular=0.3,
roughness=0.3
)
)
)
# Add GPS track so far (elevated above terrain)
if len(track_so_far) > 1:
track_elevation = track_so_far['elevation'].values + 80 # 80m above terrain
fig.add_trace(
go.Scatter3d(
x=track_so_far['longitude'],
y=track_so_far['latitude'],
z=track_elevation,
mode='lines+markers',
line=dict(
color='red',
width=10
),
marker=dict(
size=4,
color='orange',
opacity=0.8
),
name='GPS Track'
)
)
# Add current vehicle position
vehicle_height = current_alt + 120 # Above terrain
fig.add_trace(
go.Scatter3d(
x=[current_lon],
y=[current_lat],
z=[vehicle_height],
mode='markers',
marker=dict(
color='red',
size=20,
symbol='diamond',
line=dict(color='yellow', width=3)
),
name='Vehicle'
)
)
# Calculate dynamic camera position for cinematic following
# Camera follows behind and above the vehicle
follow_distance = 0.005 # degrees behind
camera_height_offset = 1000 # meters above vehicle
if current_index > 5:
# Calculate movement direction from last few points
recent_track = df.iloc[max(0, current_index-5):current_index+1]
lat_direction = recent_track['latitude'].iloc[-1] - recent_track['latitude'].iloc[0]
lon_direction = recent_track['longitude'].iloc[-1] - recent_track['longitude'].iloc[0]
# Normalize direction
direction_length = np.sqrt(lat_direction**2 + lon_direction**2)
if direction_length > 0:
lat_direction /= direction_length
lon_direction /= direction_length
# Position camera behind the vehicle
camera_lat = current_lat - lat_direction * follow_distance
camera_lon = current_lon - lon_direction * follow_distance
else:
camera_lat = current_lat - follow_distance
camera_lon = current_lon - follow_distance
camera_z = (current_alt + camera_height_offset) / 1000 # Convert to relative scale
# Update layout for cinematic Google Earth-style view
fig.update_layout(
title=dict(
text=f'GPS Flythrough - {current_row["timestamp"].strftime("%H:%M:%S")} - Frame {frame_num}',
x=0.5,
font=dict(size=24, color='white', family="Arial Black")
),
scene=dict(
camera=dict(
eye=dict(
x=1.2, # Camera position relative to scene
y=-1.5,
z=0.8
),
center=dict(
x=0,
y=0,
z=0.2
),
up=dict(x=0, y=0, z=1)
),
xaxis=dict(
title='',
showgrid=False,
zeroline=False,
showline=False,
showticklabels=False,
showbackground=False
),
yaxis=dict(
title='',
showgrid=False,
zeroline=False,
showline=False,
showticklabels=False,
showbackground=False
),
zaxis=dict(
title='',
showgrid=False,
zeroline=False,
showline=False,
showticklabels=False,
showbackground=False
),
aspectmode='cube',
bgcolor='rgb(135,206,235)', # Sky blue background
camera_projection_type='perspective'
),
paper_bgcolor='rgb(0,0,0)',
plot_bgcolor='rgb(0,0,0)',
showlegend=False,
width=1920,
height=1080,
margin=dict(l=0, r=0, t=60, b=0),
font=dict(color='white')
)
# Save frame
frame_path = os.path.join(self.frames_folder, f"GoogleEarth_Frame_{frame_num:04d}.png")
try:
fig.write_image(frame_path, engine="kaleido", width=1920, height=1080)
return frame_path
except Exception as e:
print(f"Error saving frame {frame_num}: {e}")
return None
def generate_terrain_heights(self, lat_mesh, lon_mesh, center_lat, center_lon):
"""
Generate realistic terrain heights using mathematical functions to simulate mountains/hills
"""
# Convert lat/lon to local coordinates (approximate)
lat_m = (lat_mesh - center_lat) * 111000 # degrees to meters
lon_m = (lon_mesh - center_lon) * 111000 * np.cos(np.radians(center_lat))
# Base terrain height
base_height = 300
# Create mountain ridges using sine waves
ridge1 = 400 * np.exp(-((lat_m - 1000)**2 + (lon_m + 500)**2) / (800**2))
ridge2 = 500 * np.exp(-((lat_m + 800)**2 + (lon_m - 1200)**2) / (1000**2))
ridge3 = 350 * np.exp(-((lat_m)**2 + (lon_m)**2) / (1500**2))
# Add rolling hills
hills = 150 * np.sin(lat_m / 400) * np.cos(lon_m / 600)
hills += 100 * np.sin(lat_m / 800) * np.sin(lon_m / 300)
# Add valleys
valleys = -80 * np.exp(-((lat_m - 500)**2 + (lon_m + 800)**2) / (600**2))
# Combine all terrain features
terrain = base_height + ridge1 + ridge2 + ridge3 + hills + valleys
# Add some noise for realism
noise = 30 * np.sin(lat_m / 100) * np.cos(lon_m / 150)
terrain += noise
# Ensure minimum elevation
terrain = np.maximum(terrain, 50)
return terrain
def generate_advanced_3d_video(positions_file, output_folder, filename_prefix="advanced_3d",
style='advanced', progress_callback=None):
"""