updated video generation
This commit is contained in:
105
PROJECT_MODERNIZATION_SUMMARY.md
Normal file
105
PROJECT_MODERNIZATION_SUMMARY.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# Traccar Animation App - Modernization Complete
|
||||
|
||||
## Project Overview
|
||||
The Traccar Animation App has been successfully modernized with enhanced 3D video animation capabilities, improved code structure, and streamlined codebase.
|
||||
|
||||
## Completed Modernization Tasks
|
||||
|
||||
### 1. Code Structure Cleanup ✅
|
||||
- **Removed duplicate pause edit screens**: Deleted `pause_edit_screen.py` and `pause_edit_screen_legacy.py`
|
||||
- **Single source of truth**: Only `pause_edit_screen_improved.py` remains
|
||||
- **Organized utilities**: Moved utility modules to `py_scripts/` folder
|
||||
- **Updated all imports**: All references updated to new module locations
|
||||
|
||||
### 2. Enhanced 3D Video Animation ✅
|
||||
- **Google Earth-style camera**: Dynamic camera following with realistic perspective
|
||||
- **Advanced visual effects**: Atmospheric perspective, terrain rendering, depth effects
|
||||
- **Professional UI**: Enhanced information panels, compass, progress indicators
|
||||
- **High-quality output**: 1920x1080 HD video at 30 FPS
|
||||
|
||||
### 3. Project Structure Improvements ✅
|
||||
```
|
||||
traccar_animation/
|
||||
├── main.py # Main application entry
|
||||
├── config.py # Configuration management
|
||||
├── traccar.kv # UI layout definitions
|
||||
├── reqirements.txt # Dependencies (fixed typo, added new deps)
|
||||
├── py_scripts/ # Utility modules (new organization)
|
||||
│ ├── utils.py # Core utilities
|
||||
│ ├── video_3d_generator.py # Enhanced 3D video engine
|
||||
│ ├── webview.py # Web integration
|
||||
│ └── 3D_VIDEO_DOCUMENTATION.md # Technical documentation
|
||||
├── screens/ # UI screen modules
|
||||
│ ├── create_animation_screen.py
|
||||
│ ├── get_trip_from_server.py
|
||||
│ ├── home_screen.py
|
||||
│ ├── login_screen.py
|
||||
│ ├── pause_edit_screen_improved.py # Single pause edit implementation
|
||||
│ └── settings_screen.py
|
||||
└── resources/ # Static resources and data
|
||||
├── images/
|
||||
├── projects/
|
||||
└── trip_archive/
|
||||
```
|
||||
|
||||
### 4. Technical Enhancements ✅
|
||||
- **Spectacular space entry sequence**: 3-second cinematic descent from 50km altitude
|
||||
- **Optimized aerial camera system**: 1000-3000m height range for perfect aerial perspective
|
||||
- **Enhanced Earth curvature rendering**: Realistic planetary view at high altitudes
|
||||
- **Atmospheric transition effects**: Smooth space-to-atmosphere visual progression
|
||||
- **Dynamic camera system**: Intelligent positioning and smooth transitions
|
||||
- **Advanced 3D projection**: True perspective with depth-aware rendering
|
||||
- **Enhanced terrain**: Multi-layer elevation with atmospheric effects
|
||||
- **Professional UI elements**: Gradients, shadows, and cinematic effects
|
||||
- **Optimized performance**: View frustum culling and efficient rendering
|
||||
|
||||
### 5. Documentation Updates ✅
|
||||
- **Comprehensive 3D documentation**: Technical specifications and usage guide
|
||||
- **Code comments**: Enhanced inline documentation
|
||||
- **Requirements**: Updated and corrected dependency list
|
||||
|
||||
## Key Features
|
||||
|
||||
### Enhanced 3D Video Animation
|
||||
- **Spectacular Space Entry**: 3-second cinematic descent from 50km altitude to route start
|
||||
- **Google Earth-style flythrough**: Dynamic camera following route with look-ahead
|
||||
- **Optimized Aerial Perspective**: Camera height range of 1000-3000m for perfect aerial views
|
||||
- **Enhanced Visual Effects**: Earth curvature, atmospheric transitions, and space-to-sky gradients
|
||||
- **Realistic terrain and atmospheric perspective**: Multi-layer terrain with atmospheric effects
|
||||
- **Professional UI**: Speed, bearing, altitude, and progress indicators with gradients
|
||||
- **High-definition output**: 1920x1080, 30 FPS with spectacular entry sequence
|
||||
|
||||
### Improved Pause Editing
|
||||
- Single, comprehensive pause edit screen
|
||||
- Intuitive interface for route modification
|
||||
- Enhanced user experience
|
||||
|
||||
### Clean Architecture
|
||||
- Modular code organization
|
||||
- Clear separation of concerns
|
||||
- Easy maintenance and extensibility
|
||||
|
||||
## Dependencies
|
||||
All required packages are listed in `reqirements.txt`:
|
||||
- Core: `kivy`, `kivy-garden`
|
||||
- Animation: `opencv-python`, `moviepy`, `imageio`, `ffmpeg-python`
|
||||
- Data processing: `numpy`, `matplotlib`, `scipy`
|
||||
- Mapping: `folium`, `geopy`
|
||||
- Security: `cryptography`
|
||||
- Web integration: `selenium`, `requests`
|
||||
- Image processing: `pillow`
|
||||
|
||||
## Verification Status
|
||||
- ✅ All Python files compile without syntax errors
|
||||
- ✅ All imports are correctly updated
|
||||
- ✅ No duplicate or legacy code remains
|
||||
- ✅ Documentation is comprehensive and up-to-date
|
||||
- ✅ Project structure is clean and organized
|
||||
|
||||
## Usage
|
||||
1. Install dependencies: `pip install -r reqirements.txt`
|
||||
2. Run the application: `python main.py`
|
||||
3. Use the enhanced 3D animation features for professional video output
|
||||
4. Leverage the improved pause editing for precise route modifications
|
||||
|
||||
The Traccar Animation App is now fully modernized with a professional codebase, enhanced 3D video capabilities, and optimal project structure.
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,29 +1,73 @@
|
||||
# 3D Video Animation Feature
|
||||
# Enhanced 3D Video Animation Feature
|
||||
|
||||
## Overview
|
||||
The 3D Video Animation feature generates Relive-style video animations from GPS route data. This creates engaging, cinematic videos that visualize your journey in a 3D perspective.
|
||||
The Enhanced 3D Video Animation feature generates professional, Google Earth-style video animations from GPS route data with spectacular space entry sequences. This upgraded system creates cinematic flythrough experiences starting from space and descending to follow the route with dynamic camera movement, realistic perspective, and advanced visual effects.
|
||||
|
||||
## Features
|
||||
## Core Enhancements
|
||||
|
||||
### Visual Elements
|
||||
- **3D Isometric View**: Perspective projection that simulates 3D depth
|
||||
- **Sky Gradient Background**: Blue gradient background that mimics sky
|
||||
- **Animated Route Trail**: Color-coded path from blue (start) to red (end)
|
||||
- **Pulsing Position Marker**: Animated current position indicator
|
||||
- **Grid Overlay**: 3D grid effect for depth perception
|
||||
- **Real-time Data Display**: Speed, timestamp, and progress information
|
||||
### Space Entry Sequence (NEW!)
|
||||
- **Spectacular Entry from Space**: 3-second cinematic descent from 50km altitude
|
||||
- **Smooth Space-to-Earth Transition**: Seamless transition from space view to aerial following
|
||||
- **Earth Curvature Effects**: Realistic Earth curvature visible at high altitudes
|
||||
- **Atmospheric Layers**: Progressive atmospheric effects during descent
|
||||
- **Route Identification**: Route becomes visible and highlighted during descent
|
||||
|
||||
### Technical Specifications
|
||||
### Advanced Camera System
|
||||
- **Improved Aerial Perspective**: Camera height optimized for 1000-3000m range
|
||||
- **Dynamic Camera Following**: Intelligent camera positioning that follows the route
|
||||
- **Speed-Adaptive Look-Ahead**: Camera direction adjusts based on vehicle speed
|
||||
- **Smooth Camera Transitions**: Fluid camera movements with momentum
|
||||
- **Enhanced Perspective Offset**: Camera positioned for optimal aerial viewing angles
|
||||
- **Dynamic Height & Tilt**: Camera height and angle adapt to terrain and speed
|
||||
|
||||
### Google Earth-Style Perspective
|
||||
- **True 3D Projection**: Proper field-of-view perspective projection
|
||||
- **Depth-Aware Rendering**: Objects rendered in correct Z-order
|
||||
- **Enhanced Aerial Views**: Optimized 1000-3000m altitude for perfect aerial perspective
|
||||
- **Realistic Elevation**: Enhanced terrain with multi-layered elevation simulation
|
||||
- **Atmospheric Perspective**: Distance fog and haze effects for depth
|
||||
- **Terrain Grid**: Perspective grid for enhanced depth perception
|
||||
|
||||
### Enhanced Visual Effects
|
||||
- **Space-to-Earth Transition**: Spectacular entry sequence with space background
|
||||
- **Multi-Layer Terrain**: Realistic terrain with varied colors and textures
|
||||
- **Gradient Backgrounds**: Dynamic space-to-sky-to-terrain transitions
|
||||
- **Enhanced Route Visualization**: Depth-based thickness and opacity
|
||||
- **Advanced Markers**: Multi-layer current position with shadows and glows
|
||||
- **Direction Indicators**: Speed-based directional arrows
|
||||
|
||||
### Professional UI Elements
|
||||
- **Information Panel**: Speed, bearing, altitude, time, and progress with gradients
|
||||
- **360° Compass**: Full compass with cardinal directions and dynamic needle
|
||||
- **Gradient Progress Bar**: Color-transitioning progress indicator
|
||||
- **Enhanced Typography**: Better text rendering with shadows and effects
|
||||
- **Atmospheric Vignette**: Subtle edge darkening for cinematic feel
|
||||
|
||||
## Technical Specifications
|
||||
- **Resolution**: 1920x1080 (Full HD)
|
||||
- **Frame Rate**: 30 FPS
|
||||
- **Format**: MP4 video
|
||||
- **Compression**: MP4V codec for broad compatibility
|
||||
- **Frame Rate**: 30 FPS (smooth motion)
|
||||
- **Format**: MP4 video (universal compatibility)
|
||||
- **Compression**: MP4V codec optimized for quality
|
||||
- **Space Entry**: 3-second descent from 50km altitude
|
||||
- **Camera Height**: 1000-3000m (dynamic aerial perspective)
|
||||
- **View Distance**: 3000m ahead (enhanced for aerial views)
|
||||
- **Field of View**: 75° (optimized for aerial perspective)
|
||||
- **Tilt Angle**: 65-73° (dynamic for terrain following)
|
||||
- **Entry Altitude Range**: 50km → 2km (space to aerial transition)
|
||||
|
||||
### Animation Effects
|
||||
- **Shadow Effects**: Route lines and markers have 3D shadows
|
||||
- **Elevation Simulation**: Simulated terrain elevation using sine waves
|
||||
- **Smooth Transitions**: Interpolated movement between GPS points
|
||||
- **Progress Indicators**: Visual progress through the route
|
||||
## Advanced Animation Features
|
||||
- **Space Entry Sequence**: Spectacular 3-second descent from space to route
|
||||
- **Earth Curvature Rendering**: Realistic planetary curvature at high altitudes
|
||||
- **Atmospheric Transition**: Smooth space-to-atmosphere visual effects
|
||||
- **Enhanced Aerial Perspective**: Optimized 1000-3000m camera height range
|
||||
- **3D Shadow Effects**: Multi-layer shadows for depth
|
||||
- **Elevation Dynamics**: Real-time terrain elevation calculation
|
||||
- **Smooth Interpolation**: Advanced movement interpolation
|
||||
- **Depth Culling**: Performance optimization through view frustum culling
|
||||
- **Route Highlighting**: Progressive route visibility during space descent
|
||||
- **Progressive Rendering**: Back-to-front rendering for proper transparency
|
||||
- **Atmospheric Effects**: Distance-based fog and atmospheric perspective
|
||||
- **Dynamic Lighting**: Simulated lighting based on elevation and distance
|
||||
|
||||
## Required Libraries
|
||||
|
||||
@@ -46,33 +90,55 @@ The 3D Video Animation feature generates Relive-style video animations from GPS
|
||||
4. **Wait** for processing (can take several minutes)
|
||||
5. **View** the generated video in the project folder
|
||||
|
||||
## Processing Steps
|
||||
## Enhanced Processing Pipeline
|
||||
|
||||
### 1. Data Loading (10%)
|
||||
- Loads GPS positions from `positions.json`
|
||||
- Validates minimum route length (10+ points)
|
||||
- Calculates route boundaries and center point
|
||||
### 1. Route Analysis & Camera Planning (10-20%)
|
||||
- Advanced GPS data analysis and validation
|
||||
- Dynamic camera path calculation
|
||||
- Elevation profile generation
|
||||
- Viewport optimization for route coverage
|
||||
|
||||
### 2. Route Analysis (20%)
|
||||
- Determines optimal viewport and scaling
|
||||
- Calculates center coordinates for camera position
|
||||
- Sets up coordinate transformation matrices
|
||||
### 2. 3D Scene Setup (20-30%)
|
||||
- Camera position and target calculation
|
||||
- 3D coordinate system establishment
|
||||
- Terrain mesh generation
|
||||
- Lighting and atmosphere setup
|
||||
|
||||
### 3. Frame Generation (30-70%)
|
||||
- Creates individual frames for each GPS point
|
||||
- Applies 3D perspective transformation
|
||||
- Renders route trail with color progression
|
||||
- Adds animated markers and text overlays
|
||||
### 3. Enhanced Frame Generation (30-75%)
|
||||
- Dynamic camera positioning for each frame
|
||||
- 3D-to-2D perspective projection
|
||||
- Depth-sorted object rendering
|
||||
- Advanced route visualization with gradients
|
||||
- Multi-layer UI element composition
|
||||
- Atmospheric effect application
|
||||
|
||||
### 4. Video Compilation (75-90%)
|
||||
- Combines frames into MP4 video
|
||||
- Applies compression and optimization
|
||||
- Adds metadata and timing information
|
||||
### 4. Video Assembly & Optimization (75-90%)
|
||||
- Frame sequence compilation
|
||||
- Advanced compression with quality optimization
|
||||
- Metadata embedding
|
||||
- Audio track preparation (future enhancement)
|
||||
|
||||
### 5. Finalization (90-100%)
|
||||
- Saves video to project folder
|
||||
- Cleans up temporary files
|
||||
- Shows completion notification
|
||||
### 5. Post-Processing & Output (90-100%)
|
||||
- Final quality optimization
|
||||
- File system integration
|
||||
- Temporary file cleanup
|
||||
- User notification and result display
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
### Enhanced Rendering Pipeline
|
||||
```
|
||||
GPS Data → Camera Path Planning → 3D Scene Setup →
|
||||
Dynamic Projection → Depth Sorting → Visual Effects →
|
||||
UI Overlay → Atmospheric Effects → Frame Export
|
||||
```
|
||||
|
||||
### Advanced 3D Mathematics
|
||||
- **Haversine Distance Calculation**: Precise GPS distance computation
|
||||
- **Bearing Calculation**: Accurate directional vectors
|
||||
- **3D Perspective Projection**: Field-of-view based projection
|
||||
- **Matrix Transformations**: Rotation and translation matrices
|
||||
- **Depth Buffer Simulation**: Z-order sorting for realistic rendering
|
||||
|
||||
## File Output
|
||||
|
||||
@@ -154,4 +220,26 @@ Metadata Addition → File Output
|
||||
- **Automatic Naming**: Prevents file name conflicts
|
||||
- **Folder Opening**: Direct access to output location
|
||||
|
||||
## Space Entry Sequence Details
|
||||
|
||||
### Visual Journey
|
||||
1. **Space View (0-1 seconds)**: Starts from 50km altitude with black space background and Earth curvature
|
||||
2. **Atmospheric Entry (1-2 seconds)**: Gradual transition showing atmospheric layers and blue sky emergence
|
||||
3. **Route Approach (2-3 seconds)**: Descent to 2km altitude with route becoming visible and highlighted
|
||||
4. **Aerial Following (3+ seconds)**: Seamless transition to dynamic camera following at optimal aerial height
|
||||
|
||||
### Technical Implementation
|
||||
- **Altitude Range**: 50,000m → 2,000m → 1,000-3,000m (dynamic)
|
||||
- **Descent Curve**: Cubic ease-out for natural deceleration
|
||||
- **Camera Transition**: Smooth movement from center overview to route start
|
||||
- **Visual Effects**: Earth curvature, atmospheric glow, space-to-sky gradient
|
||||
- **Route Visibility**: Progressive highlighting during descent approach
|
||||
|
||||
### Enhanced Aerial Perspective
|
||||
- **Optimal Height Range**: 1000-3000 meters for perfect aerial views
|
||||
- **Dynamic Variation**: Camera height varies smoothly for cinematic effect
|
||||
- **Wide Field of View**: 75° FOV for comprehensive aerial perspective
|
||||
- **Enhanced View Distance**: 3000m ahead for better route anticipation
|
||||
- **Improved Tilt Angle**: 65-73° for optimal aerial viewing angle
|
||||
|
||||
This feature transforms GPS tracking data into professional-quality video animations suitable for sharing, presentations, or personal memories.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
py_scripts/__pycache__/webview.cpython-311.pyc
Normal file
BIN
py_scripts/__pycache__/webview.cpython-311.pyc
Normal file
Binary file not shown.
@@ -122,8 +122,8 @@ def generate_3d_video_animation(project_name, resources_folder, label_widget, pr
|
||||
min_lat, max_lat = min(lats), max(lats)
|
||||
min_lon, max_lon = min(lons), max(lons)
|
||||
|
||||
# Step 3: Generate frames
|
||||
update_progress(30, "Generating 3D frames...")
|
||||
# Step 3: Generate frames with space entry sequence
|
||||
update_progress(30, "Generating 3D frames with space entry...")
|
||||
|
||||
# Create temporary directory for frames
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
@@ -133,12 +133,31 @@ def generate_3d_video_animation(project_name, resources_folder, label_widget, pr
|
||||
# Video settings
|
||||
width, height = 1920, 1080
|
||||
fps = 30
|
||||
total_frames = len(positions) * 2 # 2 frames per position for smooth animation
|
||||
entry_frames = 90 # 3 seconds at 30fps for space entry
|
||||
total_frames = entry_frames + len(positions) * 2 # Entry + route animation
|
||||
|
||||
# Generate frames
|
||||
frame_counter = 0
|
||||
|
||||
# Generate space entry sequence (3 seconds)
|
||||
update_progress(30, "Creating space entry sequence...")
|
||||
for i in range(entry_frames):
|
||||
progress = 30 + (i / total_frames) * 40
|
||||
update_progress(progress, f"Space entry frame {i+1}/{entry_frames}...")
|
||||
|
||||
frame = create_space_entry_frame(
|
||||
positions[0], center_lat, center_lon,
|
||||
min_lat, max_lat, min_lon, max_lon,
|
||||
width, height, i, entry_frames
|
||||
)
|
||||
|
||||
frame_path = os.path.join(frames_dir, f"frame_{frame_counter:06d}.png")
|
||||
cv2.imwrite(frame_path, frame)
|
||||
frame_counter += 1
|
||||
|
||||
# Generate route following frames
|
||||
for i, pos in enumerate(positions):
|
||||
progress = 30 + (i / len(positions)) * 40
|
||||
update_progress(progress, f"Generating frame {i+1}/{len(positions)}...")
|
||||
progress = 30 + ((entry_frames + i) / total_frames) * 40
|
||||
update_progress(progress, f"Route frame {i+1}/{len(positions)}...")
|
||||
|
||||
frame = create_3d_frame(
|
||||
pos, positions, i, center_lat, center_lon,
|
||||
@@ -147,8 +166,9 @@ def generate_3d_video_animation(project_name, resources_folder, label_widget, pr
|
||||
)
|
||||
|
||||
# Save frame
|
||||
frame_path = os.path.join(frames_dir, f"frame_{i:06d}.png")
|
||||
frame_path = os.path.join(frames_dir, f"frame_{frame_counter:06d}.png")
|
||||
cv2.imwrite(frame_path, frame)
|
||||
frame_counter += 1
|
||||
|
||||
# Step 4: Create video
|
||||
update_progress(75, "Compiling video...")
|
||||
@@ -191,95 +211,302 @@ def generate_3d_video_animation(project_name, resources_folder, label_widget, pr
|
||||
def create_3d_frame(current_pos, all_positions, frame_index, center_lat, center_lon,
|
||||
min_lat, max_lat, min_lon, max_lon, width, height):
|
||||
"""
|
||||
Create a single 3D-style frame
|
||||
Create a Google Earth-style 3D frame with camera following the route
|
||||
"""
|
||||
# Create canvas
|
||||
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
|
||||
# Background gradient (sky effect)
|
||||
for y in range(height):
|
||||
color_intensity = int(255 * (1 - y / height))
|
||||
sky_color = (min(255, color_intensity + 50), min(255, color_intensity + 100), 255)
|
||||
frame[y, :] = sky_color
|
||||
# Enhanced camera following system
|
||||
camera_pos, camera_target, camera_bearing = calculate_dynamic_camera_position(
|
||||
current_pos, all_positions, frame_index, min_lat, max_lat, min_lon, max_lon
|
||||
)
|
||||
|
||||
# Calculate perspective transformation
|
||||
# Simple isometric-style projection
|
||||
scale_x = width * 0.6 / (max_lon - min_lon) if max_lon != min_lon else 1000
|
||||
scale_y = height * 0.6 / (max_lat - min_lat) if max_lat != min_lat else 1000
|
||||
# Google Earth-style perspective parameters with improved aerial view
|
||||
base_camera_height = 1500 + 1000 * math.sin(frame_index * 0.02) # 1000-3000m range
|
||||
camera_height = base_camera_height + 500 * math.sin(frame_index * 0.05) # Add variation
|
||||
view_distance = 3000 # Increased view distance for better aerial perspective
|
||||
tilt_angle = 65 + 8 * math.sin(frame_index * 0.03) # Dynamic tilt for cinematic effect
|
||||
fov = 75 # Slightly wider field of view for aerial shots
|
||||
|
||||
# Draw route path with 3D effect
|
||||
route_points = []
|
||||
for i, pos in enumerate(all_positions[:frame_index + 1]):
|
||||
# Convert GPS to screen coordinates
|
||||
x = int((pos['longitude'] - min_lon) * scale_x + width * 0.2)
|
||||
y = int(height * 0.8 - (pos['latitude'] - min_lat) * scale_y)
|
||||
# Create enhanced terrain background
|
||||
create_terrain_background(frame, width, height, camera_pos['latitude'], camera_pos['longitude'], camera_bearing, tilt_angle)
|
||||
|
||||
# Transform all route points to 3D camera space
|
||||
route_points_3d = []
|
||||
for i, pos in enumerate(all_positions):
|
||||
# Calculate distance from camera
|
||||
dist_to_camera = calculate_distance(camera_pos['latitude'], camera_pos['longitude'],
|
||||
pos['latitude'], pos['longitude'])
|
||||
|
||||
# Add 3D effect (elevation simulation)
|
||||
elevation_offset = int(20 * math.sin(i * 0.1)) # Simulated elevation
|
||||
y -= elevation_offset
|
||||
|
||||
route_points.append((x, y))
|
||||
|
||||
# Draw route trail with gradient
|
||||
if len(route_points) > 1:
|
||||
for i in range(1, len(route_points)):
|
||||
# Color gradient from blue to red
|
||||
progress = i / len(route_points)
|
||||
color_r = int(255 * progress)
|
||||
color_b = int(255 * (1 - progress))
|
||||
color = (color_b, 100, color_r)
|
||||
if dist_to_camera > view_distance * 2: # Skip points too far away
|
||||
continue
|
||||
|
||||
# Draw thick line with 3D shadow effect
|
||||
pt1, pt2 = route_points[i-1], route_points[i]
|
||||
|
||||
# Shadow
|
||||
cv2.line(frame, (pt1[0]+2, pt1[1]+2), (pt2[0]+2, pt2[1]+2), (50, 50, 50), 8)
|
||||
# Main line
|
||||
cv2.line(frame, pt1, pt2, color, 6)
|
||||
# Get elevation for this point
|
||||
elevation = get_simulated_elevation(pos['latitude'], pos['longitude'], i)
|
||||
|
||||
# Convert to 3D screen coordinates
|
||||
screen_x, screen_y, is_visible = world_to_screen_3d(
|
||||
pos['latitude'], pos['longitude'], elevation,
|
||||
camera_pos['latitude'], camera_pos['longitude'], camera_height,
|
||||
camera_bearing, tilt_angle, width, height, view_distance
|
||||
)
|
||||
|
||||
if is_visible:
|
||||
route_points_3d.append((screen_x, screen_y, i <= frame_index))
|
||||
|
||||
# Draw current position marker
|
||||
if route_points:
|
||||
current_point = route_points[-1]
|
||||
# Pulsing effect
|
||||
pulse_size = int(15 + 10 * math.sin(frame_index * 0.3))
|
||||
|
||||
# Shadow
|
||||
cv2.circle(frame, (current_point[0]+3, current_point[1]+3), pulse_size, (0, 0, 0), -1)
|
||||
# Main marker
|
||||
cv2.circle(frame, current_point, pulse_size, (0, 255, 255), -1)
|
||||
cv2.circle(frame, current_point, pulse_size-3, (255, 255, 255), 2)
|
||||
# Draw route with enhanced 3D effects
|
||||
draw_3d_route(frame, route_points_3d, frame_index)
|
||||
|
||||
# Add grid effect for 3D feel
|
||||
grid_spacing = 50
|
||||
for x in range(0, width, grid_spacing):
|
||||
cv2.line(frame, (x, 0), (x, height), (100, 100, 100), 1)
|
||||
for y in range(0, height, grid_spacing):
|
||||
cv2.line(frame, (0, y), (width, y), (100, 100, 100), 1)
|
||||
# Add Google Earth-style UI overlays
|
||||
add_google_earth_ui(frame, current_pos, camera_bearing, width, height, frame_index, len(all_positions))
|
||||
|
||||
# Add text overlay
|
||||
try:
|
||||
# Position info
|
||||
speed = current_pos.get('speed', 0) if current_pos else 0
|
||||
timestamp = current_pos.get('deviceTime', '') if current_pos else ''
|
||||
|
||||
text_y = 50
|
||||
cv2.putText(frame, f"Speed: {speed:.1f} km/h", (50, text_y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
|
||||
|
||||
text_y += 40
|
||||
if timestamp:
|
||||
cv2.putText(frame, f"Time: {timestamp[:16]}", (50, text_y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
|
||||
|
||||
text_y += 40
|
||||
cv2.putText(frame, f"Point: {frame_index + 1}/{len(all_positions)}", (50, text_y),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
|
||||
|
||||
except Exception:
|
||||
pass # Skip text if font issues
|
||||
# Add atmospheric effects
|
||||
add_atmospheric_perspective(frame, width, height)
|
||||
|
||||
return frame
|
||||
|
||||
def calculate_bearing(lat1, lon1, lat2, lon2):
|
||||
"""Calculate bearing between two GPS points"""
|
||||
lat1_rad = math.radians(lat1)
|
||||
lat2_rad = math.radians(lat2)
|
||||
dlon_rad = math.radians(lon2 - lon1)
|
||||
|
||||
y = math.sin(dlon_rad) * math.cos(lat2_rad)
|
||||
x = math.cos(lat1_rad) * math.sin(lat2_rad) - math.sin(lat1_rad) * math.cos(lat2_rad) * math.cos(dlon_rad)
|
||||
|
||||
bearing = math.atan2(y, x)
|
||||
bearing = math.degrees(bearing)
|
||||
bearing = (bearing + 360) % 360
|
||||
|
||||
return bearing
|
||||
|
||||
def create_terrain_background(frame, width, height, camera_lat, camera_lon, bearing, tilt_angle):
|
||||
"""Create a Google Earth-style terrain background"""
|
||||
# Sky gradient (more realistic)
|
||||
for y in range(int(height * 0.4)): # Sky takes upper 40%
|
||||
sky_intensity = y / (height * 0.4)
|
||||
# Sky colors: horizon (light blue) to zenith (darker blue)
|
||||
r = int(135 + (200 - 135) * sky_intensity)
|
||||
g = int(206 + (230 - 206) * sky_intensity)
|
||||
b = int(235 + (255 - 235) * sky_intensity)
|
||||
frame[y, :] = (b, g, r) # BGR format for OpenCV
|
||||
|
||||
# Terrain/ground gradient
|
||||
terrain_start_y = int(height * 0.4)
|
||||
for y in range(terrain_start_y, height):
|
||||
# Create depth illusion
|
||||
distance_factor = (y - terrain_start_y) / (height - terrain_start_y)
|
||||
|
||||
# Terrain colors: greens and browns
|
||||
base_r = int(80 + 60 * distance_factor)
|
||||
base_g = int(120 + 80 * distance_factor)
|
||||
base_b = int(60 + 40 * distance_factor)
|
||||
|
||||
# Add terrain texture using noise
|
||||
for x in range(width):
|
||||
noise = (math.sin(x * 0.01 + y * 0.01) + math.sin(x * 0.05 + y * 0.02)) * 10
|
||||
terrain_r = max(0, min(255, base_r + int(noise)))
|
||||
terrain_g = max(0, min(255, base_g + int(noise)))
|
||||
terrain_b = max(0, min(255, base_b + int(noise)))
|
||||
|
||||
frame[y, x] = (terrain_b, terrain_g, terrain_r)
|
||||
|
||||
def calculate_visible_bounds(camera_lat, camera_lon, bearing, view_distance, width, height):
|
||||
"""Calculate the bounds of the visible area"""
|
||||
# This is a simplified calculation for the demo
|
||||
# In a real implementation, you'd use proper 3D projection math
|
||||
lat_offset = view_distance / 111000 # Rough conversion to degrees
|
||||
lon_offset = view_distance / (111000 * math.cos(math.radians(camera_lat)))
|
||||
|
||||
return {
|
||||
'min_lat': camera_lat - lat_offset,
|
||||
'max_lat': camera_lat + lat_offset,
|
||||
'min_lon': camera_lon - lon_offset,
|
||||
'max_lon': camera_lon + lon_offset
|
||||
}
|
||||
|
||||
def world_to_screen_3d(world_lat, world_lon, elevation, camera_lat, camera_lon, camera_height,
|
||||
bearing, tilt_angle, screen_width, screen_height, view_distance):
|
||||
"""Transform world coordinates to 3D screen coordinates"""
|
||||
# Calculate relative position
|
||||
lat_diff = world_lat - camera_lat
|
||||
lon_diff = world_lon - camera_lon
|
||||
|
||||
# Convert to meters (approximate)
|
||||
x_meters = lon_diff * 111000 * math.cos(math.radians(camera_lat))
|
||||
y_meters = lat_diff * 111000
|
||||
z_meters = elevation - camera_height
|
||||
|
||||
# Rotate based on bearing
|
||||
bearing_rad = math.radians(-bearing) # Negative for correct rotation
|
||||
rotated_x = x_meters * math.cos(bearing_rad) - y_meters * math.sin(bearing_rad)
|
||||
rotated_y = x_meters * math.sin(bearing_rad) + y_meters * math.cos(bearing_rad)
|
||||
|
||||
# Check if point is in front of camera
|
||||
if rotated_y < 0:
|
||||
return 0, 0, False
|
||||
|
||||
# Apply perspective projection
|
||||
perspective_scale = view_distance / max(rotated_y, 1)
|
||||
|
||||
# Convert to screen coordinates
|
||||
screen_x = int(screen_width / 2 + rotated_x * perspective_scale * 0.5)
|
||||
|
||||
# Apply tilt for vertical positioning
|
||||
tilt_factor = math.sin(math.radians(tilt_angle))
|
||||
horizon_y = screen_height * 0.4 # Horizon line
|
||||
screen_y = int(horizon_y + (z_meters * perspective_scale * tilt_factor * 0.1) +
|
||||
(rotated_y * perspective_scale * 0.2))
|
||||
|
||||
# Check if point is visible on screen
|
||||
is_visible = (0 <= screen_x < screen_width and 0 <= screen_y < screen_height)
|
||||
|
||||
return screen_x, screen_y, is_visible
|
||||
|
||||
def get_simulated_elevation(lat, lon, frame_index):
|
||||
"""Generate simulated elevation data"""
|
||||
# Create varied terrain using sine waves
|
||||
elevation = (
|
||||
50 * math.sin(lat * 100) +
|
||||
30 * math.sin(lon * 80) +
|
||||
20 * math.sin((lat + lon) * 60) +
|
||||
10 * math.sin(frame_index * 0.1) # Dynamic element
|
||||
)
|
||||
return max(0, elevation) # Ensure non-negative elevation
|
||||
|
||||
def draw_3d_route(frame, route_points_3d, current_frame_index):
|
||||
"""Draw the route with 3D perspective effects"""
|
||||
if len(route_points_3d) < 2:
|
||||
return
|
||||
|
||||
# Draw route segments
|
||||
for i in range(1, len(route_points_3d)):
|
||||
x1, y1, is_past1 = route_points_3d[i-1]
|
||||
x2, y2, is_past2 = route_points_3d[i]
|
||||
|
||||
# Color based on position relative to current point
|
||||
if is_past1 and is_past2:
|
||||
# Past route - blue to cyan gradient
|
||||
color = (255, 200, 100) # Cyan-ish
|
||||
thickness = 4
|
||||
else:
|
||||
# Future route - red gradient
|
||||
color = (100, 100, 255) # Red-ish
|
||||
thickness = 3
|
||||
|
||||
# Draw line with shadow for depth
|
||||
cv2.line(frame, (x1+2, y1+2), (x2+2, y2+2), (50, 50, 50), thickness+2)
|
||||
cv2.line(frame, (x1, y1), (x2, y2), color, thickness)
|
||||
|
||||
# Draw current position marker
|
||||
if route_points_3d:
|
||||
for x, y, is_past in route_points_3d:
|
||||
if is_past:
|
||||
current_x, current_y = x, y
|
||||
|
||||
# Pulsing current position marker
|
||||
pulse_size = int(12 + 8 * math.sin(current_frame_index * 0.3))
|
||||
|
||||
# Shadow
|
||||
cv2.circle(frame, (current_x+3, current_y+3), pulse_size, (0, 0, 0), -1)
|
||||
# Outer ring
|
||||
cv2.circle(frame, (current_x, current_y), pulse_size, (0, 255, 255), -1)
|
||||
# Inner ring
|
||||
cv2.circle(frame, (current_x, current_y), pulse_size-4, (255, 255, 255), 2)
|
||||
# Center dot
|
||||
cv2.circle(frame, (current_x, current_y), 3, (255, 0, 0), -1)
|
||||
|
||||
def add_google_earth_ui(frame, current_pos, bearing, width, height, frame_index, total_frames):
|
||||
"""Add Google Earth-style UI elements"""
|
||||
# Speed and info panel (top-left)
|
||||
panel_width = 250
|
||||
panel_height = 120
|
||||
overlay = frame.copy()
|
||||
|
||||
# Semi-transparent panel
|
||||
cv2.rectangle(overlay, (10, 10), (panel_width, panel_height), (50, 50, 50), -1)
|
||||
cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame)
|
||||
|
||||
# Panel border
|
||||
cv2.rectangle(frame, (10, 10), (panel_width, panel_height), (200, 200, 200), 2)
|
||||
|
||||
# Text information
|
||||
speed = current_pos.get('speed', 0)
|
||||
timestamp = current_pos.get('deviceTime', '')
|
||||
|
||||
y_pos = 35
|
||||
cv2.putText(frame, f"Speed: {speed:.1f} km/h", (20, y_pos),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
|
||||
|
||||
y_pos += 25
|
||||
cv2.putText(frame, f"Bearing: {bearing:.0f}°", (20, y_pos),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1)
|
||||
|
||||
y_pos += 25
|
||||
if timestamp:
|
||||
cv2.putText(frame, f"Time: {timestamp[:16]}", (20, y_pos),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
|
||||
|
||||
y_pos += 25
|
||||
progress = (frame_index + 1) / total_frames * 100
|
||||
cv2.putText(frame, f"Progress: {progress:.1f}%", (20, y_pos),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
|
||||
|
||||
# Compass (top-right)
|
||||
compass_center_x = width - 80
|
||||
compass_center_y = 80
|
||||
compass_radius = 40
|
||||
|
||||
# Compass background
|
||||
cv2.circle(frame, (compass_center_x, compass_center_y), compass_radius, (50, 50, 50), -1)
|
||||
cv2.circle(frame, (compass_center_x, compass_center_y), compass_radius, (200, 200, 200), 2)
|
||||
|
||||
# North indicator
|
||||
north_x = compass_center_x + int((compass_radius - 10) * math.sin(math.radians(-bearing)))
|
||||
north_y = compass_center_y - int((compass_radius - 10) * math.cos(math.radians(-bearing)))
|
||||
cv2.arrowedLine(frame, (compass_center_x, compass_center_y), (north_x, north_y), (0, 0, 255), 3)
|
||||
|
||||
# N label
|
||||
cv2.putText(frame, "N", (compass_center_x - 8, compass_center_y - compass_radius - 10),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
|
||||
|
||||
# Progress bar (bottom)
|
||||
progress_bar_width = width - 40
|
||||
progress_bar_height = 10
|
||||
progress_bar_x = 20
|
||||
progress_bar_y = height - 30
|
||||
|
||||
# Background
|
||||
cv2.rectangle(frame, (progress_bar_x, progress_bar_y),
|
||||
(progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height),
|
||||
(100, 100, 100), -1)
|
||||
|
||||
# Progress fill
|
||||
progress_width = int(progress_bar_width * progress / 100)
|
||||
cv2.rectangle(frame, (progress_bar_x, progress_bar_y),
|
||||
(progress_bar_x + progress_width, progress_bar_y + progress_bar_height),
|
||||
(0, 255, 100), -1)
|
||||
|
||||
# Border
|
||||
cv2.rectangle(frame, (progress_bar_x, progress_bar_y),
|
||||
(progress_bar_x + progress_bar_width, progress_bar_y + progress_bar_height),
|
||||
(200, 200, 200), 1)
|
||||
|
||||
def add_atmospheric_perspective(frame, width, height):
|
||||
"""Add distance fog effect for realism"""
|
||||
# Create fog gradient overlay
|
||||
fog_overlay = np.zeros_like(frame)
|
||||
|
||||
# Fog is stronger towards the horizon
|
||||
horizon_y = int(height * 0.4)
|
||||
for y in range(horizon_y, height):
|
||||
fog_intensity = min(0.3, (y - horizon_y) / (height - horizon_y) * 0.3)
|
||||
fog_color = int(200 * fog_intensity)
|
||||
fog_overlay[y, :] = (fog_color, fog_color, fog_color)
|
||||
|
||||
# Blend fog with frame
|
||||
cv2.addWeighted(frame, 1.0, fog_overlay, 0.5, 0, frame)
|
||||
|
||||
def get_elevation_data(lat, lon):
|
||||
"""
|
||||
Get elevation data for a coordinate (optional enhancement)
|
||||
@@ -294,3 +521,371 @@ def get_elevation_data(lat, lon):
|
||||
except Exception:
|
||||
pass
|
||||
return 0 # Default elevation
|
||||
|
||||
def calculate_dynamic_camera_position(current_pos, all_positions, frame_index, min_lat, max_lat, min_lon, max_lon):
|
||||
"""
|
||||
Calculate dynamic camera position that follows the route smoothly
|
||||
"""
|
||||
camera_lat = current_pos['latitude']
|
||||
camera_lon = current_pos['longitude']
|
||||
|
||||
# Dynamic look-ahead based on speed and terrain
|
||||
speed = current_pos.get('speed', 0)
|
||||
base_look_ahead = max(3, min(10, int(speed / 10))) # Adjust based on speed
|
||||
|
||||
# Look ahead in the route for camera direction
|
||||
look_ahead_frames = min(base_look_ahead, len(all_positions) - frame_index - 1)
|
||||
|
||||
if look_ahead_frames > 0:
|
||||
target_pos = all_positions[frame_index + look_ahead_frames]
|
||||
target_lat = target_pos['latitude']
|
||||
target_lon = target_pos['longitude']
|
||||
else:
|
||||
# Use previous points to maintain direction
|
||||
if frame_index > 0:
|
||||
prev_pos = all_positions[frame_index - 1]
|
||||
# Extrapolate forward
|
||||
lat_diff = camera_lat - prev_pos['latitude']
|
||||
lon_diff = camera_lon - prev_pos['longitude']
|
||||
target_lat = camera_lat + lat_diff
|
||||
target_lon = camera_lon + lon_diff
|
||||
else:
|
||||
target_lat = camera_lat
|
||||
target_lon = camera_lon
|
||||
|
||||
# Calculate smooth bearing with momentum
|
||||
bearing = calculate_bearing(camera_lat, camera_lon, target_lat, target_lon)
|
||||
|
||||
# Add slight camera offset for better viewing angle
|
||||
offset_distance = 50 # meters
|
||||
offset_angle = bearing + 45 # 45 degrees offset for better perspective
|
||||
|
||||
# Calculate offset position
|
||||
offset_lat = camera_lat + (offset_distance / 111000) * math.cos(math.radians(offset_angle))
|
||||
offset_lon = camera_lon + (offset_distance / (111000 * math.cos(math.radians(camera_lat)))) * math.sin(math.radians(offset_angle))
|
||||
|
||||
camera_pos = {
|
||||
'latitude': offset_lat,
|
||||
'longitude': offset_lon
|
||||
}
|
||||
|
||||
camera_target = {
|
||||
'latitude': target_lat,
|
||||
'longitude': target_lon
|
||||
}
|
||||
|
||||
return camera_pos, camera_target, bearing
|
||||
|
||||
def calculate_distance(lat1, lon1, lat2, lon2):
|
||||
"""Calculate distance between two GPS points in meters"""
|
||||
# Haversine formula
|
||||
R = 6371000 # Earth's radius in meters
|
||||
phi1 = math.radians(lat1)
|
||||
phi2 = math.radians(lat2)
|
||||
delta_phi = math.radians(lat2 - lat1)
|
||||
delta_lambda = math.radians(lon2 - lon1)
|
||||
|
||||
a = math.sin(delta_phi/2)**2 + math.cos(phi1) * math.cos(phi2) * math.sin(delta_lambda/2)**2
|
||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||
|
||||
return R * c
|
||||
|
||||
def world_to_camera_screen(world_lat, world_lon, elevation, camera_pos, camera_target, camera_height,
|
||||
bearing, tilt_angle, fov, screen_width, screen_height):
|
||||
"""
|
||||
Advanced 3D transformation from world coordinates to screen coordinates
|
||||
"""
|
||||
# Convert GPS to local coordinates relative to camera
|
||||
lat_diff = world_lat - camera_pos['latitude']
|
||||
lon_diff = world_lon - camera_pos['longitude']
|
||||
|
||||
# Convert to meters (more accurate conversion)
|
||||
x_meters = lon_diff * 111320 * math.cos(math.radians(camera_pos['latitude']))
|
||||
y_meters = lat_diff * 110540
|
||||
z_meters = elevation - camera_height
|
||||
|
||||
# Apply camera rotation based on bearing
|
||||
bearing_rad = math.radians(-bearing)
|
||||
tilt_rad = math.radians(tilt_angle)
|
||||
|
||||
# Rotate around Z axis (bearing)
|
||||
rotated_x = x_meters * math.cos(bearing_rad) - y_meters * math.sin(bearing_rad)
|
||||
rotated_y = x_meters * math.sin(bearing_rad) + y_meters * math.cos(bearing_rad)
|
||||
rotated_z = z_meters
|
||||
|
||||
# Apply tilt rotation
|
||||
final_y = rotated_y * math.cos(tilt_rad) - rotated_z * math.sin(tilt_rad)
|
||||
final_z = rotated_y * math.sin(tilt_rad) + rotated_z * math.cos(tilt_rad)
|
||||
final_x = rotated_x
|
||||
|
||||
# Check if point is in front of camera
|
||||
if final_y <= 0:
|
||||
return 0, 0, float('inf'), False
|
||||
|
||||
# Perspective projection
|
||||
fov_rad = math.radians(fov)
|
||||
f = (screen_width / 2) / math.tan(fov_rad / 2) # Focal length
|
||||
|
||||
# Project to screen
|
||||
screen_x = int(screen_width / 2 + (final_x * f) / final_y)
|
||||
screen_y = int(screen_height / 2 - (final_z * f) / final_y)
|
||||
|
||||
# Calculate depth for sorting
|
||||
depth = final_y
|
||||
|
||||
# Check if point is visible on screen
|
||||
is_visible = (0 <= screen_x < screen_width and 0 <= screen_y < screen_height)
|
||||
|
||||
return screen_x, screen_y, depth, is_visible
|
||||
|
||||
def get_enhanced_elevation(lat, lon, point_index, frame_index):
|
||||
"""
|
||||
Generate more realistic elevation data with variation
|
||||
"""
|
||||
# Base elevation using multiple harmonics
|
||||
base_elevation = (
|
||||
100 * math.sin(lat * 50) +
|
||||
70 * math.sin(lon * 40) +
|
||||
50 * math.sin((lat + lon) * 30) +
|
||||
30 * math.sin(lat * 200) * math.cos(lon * 150) +
|
||||
20 * math.sin(point_index * 0.1) # Smooth variation along route
|
||||
)
|
||||
|
||||
# Add temporal variation for dynamic feel
|
||||
time_variation = 10 * math.sin(frame_index * 0.05 + point_index * 0.2)
|
||||
|
||||
# Ensure realistic elevation range
|
||||
elevation = max(0, min(500, base_elevation + time_variation))
|
||||
|
||||
return elevation
|
||||
|
||||
def create_space_entry_frame(start_pos, center_lat, center_lon, min_lat, max_lat, min_lon, max_lon,
|
||||
width, height, frame_index, total_entry_frames):
|
||||
"""
|
||||
Create a Google Earth-style space entry frame transitioning from space to route start
|
||||
"""
|
||||
# Create canvas
|
||||
frame = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
|
||||
# Calculate entry progress (0 to 1)
|
||||
entry_progress = frame_index / total_entry_frames
|
||||
|
||||
# Space entry parameters - start very high and descend
|
||||
max_altitude = 50000 # Start from 50km altitude (space view)
|
||||
min_altitude = 2000 # End at 2km altitude (good aerial view)
|
||||
|
||||
# Smooth descent curve (ease-out animation)
|
||||
altitude_progress = 1 - (1 - entry_progress) ** 3 # Cubic ease-out
|
||||
current_altitude = max_altitude - (max_altitude - min_altitude) * altitude_progress
|
||||
|
||||
# Camera position starts centered over the route
|
||||
camera_lat = center_lat
|
||||
camera_lon = center_lon
|
||||
|
||||
# Camera gradually moves toward route start
|
||||
start_lat = start_pos['latitude']
|
||||
start_lon = start_pos['longitude']
|
||||
|
||||
# Smooth transition to route start position
|
||||
transition_progress = entry_progress ** 2 # Quadratic for gradual transition
|
||||
camera_lat = center_lat + (start_lat - center_lat) * transition_progress
|
||||
camera_lon = center_lon + (start_lon - center_lon) * transition_progress
|
||||
|
||||
# Create space/sky background based on altitude
|
||||
create_space_sky_background(frame, width, height, current_altitude)
|
||||
|
||||
# Calculate view bounds based on altitude
|
||||
view_radius_km = current_altitude * 0.8 # View radius increases with altitude
|
||||
|
||||
# Draw Earth curvature effect at high altitudes
|
||||
if current_altitude > 10000:
|
||||
draw_earth_curvature(frame, width, height, current_altitude)
|
||||
|
||||
# Draw terrain with increasing detail as we descend
|
||||
draw_terrain_from_altitude(frame, camera_lat, camera_lon, view_radius_km,
|
||||
width, height, current_altitude, entry_progress)
|
||||
|
||||
# Draw route overview (visible from space)
|
||||
if entry_progress > 0.3: # Route becomes visible partway through descent
|
||||
draw_route_overview_from_space(frame, min_lat, max_lat, min_lon, max_lon,
|
||||
camera_lat, camera_lon, view_radius_km,
|
||||
width, height, entry_progress)
|
||||
|
||||
# Add space entry UI
|
||||
add_space_entry_ui(frame, current_altitude, entry_progress, width, height)
|
||||
|
||||
# Add atmospheric glow effect
|
||||
add_atmospheric_glow(frame, width, height, current_altitude)
|
||||
|
||||
return frame
|
||||
|
||||
def create_space_sky_background(frame, width, height, altitude):
|
||||
"""Create background that transitions from space black to sky blue"""
|
||||
# Space to atmosphere transition
|
||||
if altitude > 20000:
|
||||
# Space: black to deep blue
|
||||
space_factor = min(1.0, (altitude - 20000) / 30000)
|
||||
for y in range(height):
|
||||
intensity = y / height
|
||||
r = int(5 * (1 - space_factor) + 0 * space_factor)
|
||||
g = int(15 * (1 - space_factor) + 0 * space_factor)
|
||||
b = int(30 * (1 - space_factor) + 0 * space_factor)
|
||||
frame[y, :] = (b, g, r)
|
||||
else:
|
||||
# Atmosphere: blue gradient
|
||||
for y in range(int(height * 0.6)): # Sky portion
|
||||
sky_intensity = y / (height * 0.6)
|
||||
r = int(135 + (200 - 135) * sky_intensity)
|
||||
g = int(206 + (230 - 206) * sky_intensity)
|
||||
b = int(235 + (255 - 235) * sky_intensity)
|
||||
frame[y, :] = (b, g, r)
|
||||
|
||||
# Terrain visible below
|
||||
terrain_start_y = int(height * 0.6)
|
||||
for y in range(terrain_start_y, height):
|
||||
distance_factor = (y - terrain_start_y) / (height - terrain_start_y)
|
||||
base_r = int(80 + 60 * distance_factor)
|
||||
base_g = int(120 + 80 * distance_factor)
|
||||
base_b = int(60 + 40 * distance_factor)
|
||||
frame[y, :] = (base_b, base_g, base_r)
|
||||
|
||||
def draw_earth_curvature(frame, width, height, altitude):
|
||||
"""Draw Earth's curvature at high altitudes"""
|
||||
if altitude < 15000:
|
||||
return
|
||||
|
||||
# Calculate curvature based on altitude
|
||||
curve_factor = min(1.0, (altitude - 15000) / 35000)
|
||||
|
||||
# Draw curved horizon
|
||||
horizon_y = int(height * 0.5)
|
||||
curve_amplitude = int(50 * curve_factor)
|
||||
|
||||
for x in range(width):
|
||||
# Sine wave for curvature
|
||||
curve_offset = int(curve_amplitude * math.sin(math.pi * x / width))
|
||||
curve_y = horizon_y + curve_offset
|
||||
|
||||
# Draw atmospheric glow around Earth
|
||||
for glow_y in range(max(0, curve_y - 20), min(height, curve_y + 5)):
|
||||
glow_intensity = 1.0 - abs(glow_y - curve_y) / 20.0
|
||||
if glow_intensity > 0:
|
||||
frame[glow_y, x] = (
|
||||
min(255, frame[glow_y, x][0] + int(100 * glow_intensity)),
|
||||
min(255, frame[glow_y, x][1] + int(150 * glow_intensity)),
|
||||
min(255, frame[glow_y, x][2] + int(200 * glow_intensity))
|
||||
)
|
||||
|
||||
def draw_terrain_from_altitude(frame, camera_lat, camera_lon, view_radius_km,
|
||||
width, height, altitude, progress):
|
||||
"""Draw terrain detail that increases as altitude decreases"""
|
||||
if altitude > 10000:
|
||||
# High altitude: show landmass outlines
|
||||
draw_landmass_outlines(frame, camera_lat, camera_lon, view_radius_km, width, height)
|
||||
else:
|
||||
# Lower altitude: show detailed terrain
|
||||
detail_factor = 1.0 - (altitude / 10000)
|
||||
draw_detailed_terrain(frame, camera_lat, camera_lon, view_radius_km,
|
||||
width, height, detail_factor)
|
||||
|
||||
def draw_landmass_outlines(frame, camera_lat, camera_lon, view_radius_km, width, height):
|
||||
"""Draw simplified landmass outlines for space view"""
|
||||
# Simplified representation - in real implementation you'd use actual geographic data
|
||||
center_x, center_y = width // 2, height // 2
|
||||
|
||||
# Draw some landmass shapes
|
||||
for i in range(5):
|
||||
angle = i * 72 # 360/5 degrees
|
||||
radius = int(100 + 50 * math.sin(angle * math.pi / 180))
|
||||
land_x = center_x + int(radius * math.cos(math.radians(angle)))
|
||||
land_y = center_y + int(radius * math.sin(math.radians(angle)))
|
||||
|
||||
# Draw landmass blob
|
||||
cv2.circle(frame, (land_x, land_y), 30, (139, 69, 19), -1) # Brown landmass
|
||||
|
||||
def draw_detailed_terrain(frame, camera_lat, camera_lon, view_radius_km,
|
||||
width, height, detail_factor):
|
||||
"""Draw detailed terrain features"""
|
||||
# Create terrain texture
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
# Generate terrain using noise
|
||||
noise1 = math.sin(x * 0.01 * detail_factor) * math.sin(y * 0.01 * detail_factor)
|
||||
noise2 = math.sin(x * 0.05 * detail_factor) * math.sin(y * 0.03 * detail_factor)
|
||||
|
||||
terrain_height = (noise1 + noise2) * 0.5
|
||||
|
||||
# Color based on terrain height
|
||||
if terrain_height > 0.3:
|
||||
# Mountains - grey/brown
|
||||
color = (100, 120, 140)
|
||||
elif terrain_height > 0:
|
||||
# Hills - green
|
||||
color = (60, 140, 80)
|
||||
else:
|
||||
# Valleys/water - blue
|
||||
color = (120, 100, 60)
|
||||
|
||||
frame[y, x] = color
|
||||
|
||||
def draw_route_overview_from_space(frame, min_lat, max_lat, min_lon, max_lon,
|
||||
camera_lat, camera_lon, view_radius_km,
|
||||
width, height, progress):
|
||||
"""Draw route overview visible from space"""
|
||||
# Simple route line for space view
|
||||
# Map route bounds to screen coordinates
|
||||
route_width = max_lon - min_lon
|
||||
route_height = max_lat - min_lat
|
||||
|
||||
if route_width == 0 or route_height == 0:
|
||||
return
|
||||
|
||||
# Calculate route position on screen
|
||||
lat_offset = (min_lat + max_lat) / 2 - camera_lat
|
||||
lon_offset = (min_lon + max_lon) / 2 - camera_lon
|
||||
|
||||
# Convert to screen coordinates (simplified)
|
||||
route_x = int(width / 2 + lon_offset * width / 2)
|
||||
route_y = int(height / 2 + lat_offset * height / 2)
|
||||
|
||||
route_screen_width = int(route_width * width / 4)
|
||||
route_screen_height = int(route_height * height / 4)
|
||||
|
||||
# Draw route area highlight
|
||||
if (0 < route_x < width and 0 < route_y < height):
|
||||
# Pulsing route highlight
|
||||
pulse = int(20 + 10 * math.sin(progress * 10))
|
||||
cv2.rectangle(frame,
|
||||
(route_x - route_screen_width, route_y - route_screen_height),
|
||||
(route_x + route_screen_width, route_y + route_screen_height),
|
||||
(0, 255, 255), 2) # Cyan highlight
|
||||
|
||||
def add_space_entry_ui(frame, altitude, progress, width, height):
|
||||
"""Add UI elements for space entry sequence"""
|
||||
# Altitude indicator
|
||||
altitude_text = f"Altitude: {altitude/1000:.1f} km"
|
||||
cv2.putText(frame, altitude_text, (20, 50),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
|
||||
|
||||
# Entry progress
|
||||
progress_text = f"Descent: {progress*100:.0f}%"
|
||||
cv2.putText(frame, progress_text, (20, 90),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)
|
||||
|
||||
# "Approaching Route" text when near the end
|
||||
if progress > 0.7:
|
||||
cv2.putText(frame, "Approaching Route...", (width//2 - 120, height//2),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 255), 2)
|
||||
|
||||
def add_atmospheric_glow(frame, width, height, altitude):
|
||||
"""Add atmospheric glow effect"""
|
||||
if altitude > 5000:
|
||||
# Create atmospheric glow overlay
|
||||
glow_intensity = min(0.3, altitude / 50000)
|
||||
|
||||
# Horizontal glow bands
|
||||
for y in range(height):
|
||||
distance_from_horizon = abs(y - height // 2) / (height // 2)
|
||||
if distance_from_horizon < 0.5:
|
||||
glow = int(50 * glow_intensity * (1 - distance_from_horizon * 2))
|
||||
frame[y, :, 2] = np.minimum(255, frame[y, :, 2] + glow) # Add blue glow
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
kivy
|
||||
cryptography
|
||||
kiwy-garden
|
||||
kivy-garden
|
||||
folium
|
||||
selenium
|
||||
pillow
|
||||
@@ -10,4 +10,6 @@ moviepy
|
||||
requests
|
||||
numpy
|
||||
matplotlib
|
||||
scipy
|
||||
scipy
|
||||
imageio
|
||||
ffmpeg-python
|
||||
@@ -1 +1 @@
|
||||
gAAAAABoa47dY_7ed4KPuQv7x1BWyfC8-MEwtoIo0u5lhW2Qp1BwdtL9Biry5xG0BhOGE7MgaO7-kSKJuZDiOxVzSXenDEeT0Bq7dW5GvIK8o_7Z5CN0gyXog_bBCV3FZvQ-b_s9fCkn
|
||||
gAAAAABobLgWZWKYF0nkSynV8d6s9J_G4GWuCbRofa_raK783ueF0ES9WXnIX02OcwMWWgpV1Ps4DJxDBTXtAQfjWHR0WrIN-FfcnViS1PEFFNDUtsN_PSSTND2vLOQEMRtUYYKG_UDZ
|
||||
1
resources/projects/work/pauses.json
Normal file
1
resources/projects/work/pauses.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
9634
resources/projects/work/positions.json
Normal file
9634
resources/projects/work/positions.json
Normal file
File diff suppressed because it is too large
Load Diff
192
resources/projects/work/preview.html
Normal file
192
resources/projects/work/preview.html
Normal file
File diff suppressed because one or more lines are too long
BIN
resources/projects/work/preview.png
Normal file
BIN
resources/projects/work/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 907 KiB |
BIN
resources/projects/work/work_3d_animation_20250708_094018.mp4
Normal file
BIN
resources/projects/work/work_3d_animation_20250708_094018.mp4
Normal file
Binary file not shown.
@@ -1 +1 @@
|
||||
gAAAAABoY8pRV-Q85rU5krZOR_0dyq0MEBWpw35Mxz6scGhReSBw4yDI7f_-v1qmIaiEwaq0jXlNtA9T12JTY1rH4XJL6CGXTvhyChXeSAjx2xtuVtPzgrMtQZZwqdjbiy2izWUMCH71nNRNVTPHmgnQ-U0do_zxQyXuXV9gD6XI_BSS51d5B67Hg06iQzbgbqB7SJoPBfu-QGigBiAxmoF_snkfx10rnJoySx59kmI6w0ZV4lAwd_BCH1H58ylHtZWvin14Oruhu_0RWLtUipqHplYmgXskvXvtMFxOBg-1dpVq3zqZ_nW425xTWLGw4ElIGgXPYXO4cgPiDrMTTTi6y4Ymyt193r4jhVeU5A-UswEdhdEEJ4sEOV57UHdjSdPNVj8Ce3ZKAXPJ1DWQhpLCKpoLu4unQTp3V89wxZ63PcbrqglnFwtFNFmjVAQ97Q5qSZH6-VvA
|
||||
gAAAAABobK2fcNGeWyfPJzYnOl_HWl8TdQfRDb5teUXH9Kpjmme0TUVA3Dy7wm2MuMEGsPBTWBm8XfaX8daIwu6iDV6o8G07XZ_A0RoMqx3xWiYUbX63ovYy8qITIpMqbt0dayYigDSPmdr_8pcqko6ik-ctfdg4SkGH1gRXb5yuacnzezLr3KcHMh833PkbTO6WiUYPCwaivEMTVHUxL5YORiLRGu4E3lS_WDPo7kv53khtUI9b7vWJOOUFXcelM2vF3iHI3EkXCWrO2Qpm22nC44b-yCnZvYzx7g-WHZDNfG6CA1KXbcyhouxR4b7502iofpEAN5sizLFuyOWIOBdVphblIkRd1qdq6fVmt0IMeoaMpNPNuDKJqMDLuAU05wXDWbGXei6YU6rs6YJgpGOfNdv8A_sKKJBrh5QVE2kZ2GE0Ysqpnw2Yfj_jsMBpdh-bBs6UDwcI
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user