Compare commits
No commits in common. "7d54aa4fae14c6573c292443b253ba297808a687" and "4c9215b53527a2e37778519a289dba2ec44eb6c9" have entirely different histories.
7d54aa4fae
...
4c9215b535
3 changed files with 44 additions and 352 deletions
|
|
@ -858,59 +858,3 @@ body {
|
||||||
.squawk-standard:hover {
|
.squawk-standard:hover {
|
||||||
background: #218838;
|
background: #218838;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3D Radar Styles */
|
|
||||||
#radar3d-view {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#radar3d-container {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 500px;
|
|
||||||
width: 100%;
|
|
||||||
background: #0a0a0a;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background: #2d2d2d;
|
|
||||||
border-bottom: 1px solid #404040;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls button {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
background: #404040;
|
|
||||||
color: white;
|
|
||||||
border: 1px solid #666;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls button:hover:not(:disabled) {
|
|
||||||
background: #505050;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls button:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls button.active {
|
|
||||||
background: #00a8ff;
|
|
||||||
border-color: #0088cc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls label {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
align-items: center;
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radar3d-controls input[type="range"] {
|
|
||||||
width: 150px;
|
|
||||||
}
|
|
||||||
|
|
@ -411,6 +411,7 @@
|
||||||
<!-- 3D Radar View -->
|
<!-- 3D Radar View -->
|
||||||
<div id="radar3d-view" class="view">
|
<div id="radar3d-view" class="view">
|
||||||
<div class="radar3d-controls">
|
<div class="radar3d-controls">
|
||||||
|
<div class="construction-notice">🚧 3D Controls Under Construction</div>
|
||||||
<button id="radar3d-reset" disabled>Reset View</button>
|
<button id="radar3d-reset" disabled>Reset View</button>
|
||||||
<button id="radar3d-auto-rotate" disabled>Auto Rotate</button>
|
<button id="radar3d-auto-rotate" disabled>Auto Rotate</button>
|
||||||
<label>
|
<label>
|
||||||
|
|
|
||||||
|
|
@ -65,12 +65,7 @@ class SkyView {
|
||||||
// Initialize other components
|
// Initialize other components
|
||||||
this.initializeCharts();
|
this.initializeCharts();
|
||||||
this.uiManager.updateClocks();
|
this.uiManager.updateClocks();
|
||||||
|
this.initialize3DRadar();
|
||||||
// Flag to track 3D radar initialization
|
|
||||||
this.radar3dInitialized = false;
|
|
||||||
|
|
||||||
// Add view change listener for 3D radar initialization
|
|
||||||
this.setupViewChangeListener();
|
|
||||||
|
|
||||||
// Set up map controls
|
// Set up map controls
|
||||||
this.setupMapControls();
|
this.setupMapControls();
|
||||||
|
|
@ -273,7 +268,7 @@ class SkyView {
|
||||||
// Update coverage controls
|
// Update coverage controls
|
||||||
this.mapManager.updateCoverageControls();
|
this.mapManager.updateCoverageControls();
|
||||||
|
|
||||||
if (this.uiManager.currentView === 'radar3d-view' && this.radar3dInitialized) {
|
if (this.uiManager.currentView === 'radar3d-view') {
|
||||||
this.update3DRadar();
|
this.update3DRadar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -356,85 +351,13 @@ class SkyView {
|
||||||
chart.update('none');
|
chart.update('none');
|
||||||
}
|
}
|
||||||
|
|
||||||
setupViewChangeListener() {
|
|
||||||
// Override the ui manager's switchView to handle 3D radar initialization
|
|
||||||
const originalSwitchView = this.uiManager.switchView.bind(this.uiManager);
|
|
||||||
|
|
||||||
this.uiManager.switchView = (viewId) => {
|
|
||||||
const result = originalSwitchView(viewId);
|
|
||||||
|
|
||||||
// Initialize 3D radar when switching to 3D radar view
|
|
||||||
if (viewId === 'radar3d-view' && !this.radar3dInitialized) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.initialize3DRadar();
|
|
||||||
this.radar3dInitialized = true;
|
|
||||||
}, 100); // Small delay to ensure the view is visible
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3D Radar (basic implementation)
|
// 3D Radar (basic implementation)
|
||||||
initialize3DRadar() {
|
initialize3DRadar() {
|
||||||
console.log('🚀 Starting 3D radar initialization');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const container = document.getElementById('radar3d-container');
|
const container = document.getElementById('radar3d-container');
|
||||||
if (!container) {
|
if (!container) return;
|
||||||
console.error('❌ Container radar3d-container not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Container found:', container, 'Size:', container.clientWidth, 'x', container.clientHeight);
|
// Create scene
|
||||||
|
|
||||||
// Check if container is visible
|
|
||||||
const containerStyles = window.getComputedStyle(container);
|
|
||||||
console.log('📋 Container styles:', {
|
|
||||||
display: containerStyles.display,
|
|
||||||
visibility: containerStyles.visibility,
|
|
||||||
width: containerStyles.width,
|
|
||||||
height: containerStyles.height
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if Three.js is available
|
|
||||||
if (typeof THREE === 'undefined') {
|
|
||||||
console.error('❌ Three.js is not available');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('✅ Three.js available, version:', THREE.REVISION);
|
|
||||||
|
|
||||||
// Quick WebGL test
|
|
||||||
const testCanvas = document.createElement('canvas');
|
|
||||||
const gl = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl');
|
|
||||||
if (!gl) {
|
|
||||||
console.error('❌ WebGL is not supported in this browser');
|
|
||||||
container.innerHTML = '<div style="color: red; padding: 20px; text-align: center;">WebGL not supported. Please use a modern browser that supports WebGL.</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('✅ WebGL is supported');
|
|
||||||
|
|
||||||
// Check container dimensions first
|
|
||||||
if (container.clientWidth === 0 || container.clientHeight === 0) {
|
|
||||||
console.error('❌ Container has zero dimensions:', container.clientWidth, 'x', container.clientHeight);
|
|
||||||
// Force minimum size for initialization
|
|
||||||
const width = Math.max(container.clientWidth, 800);
|
|
||||||
const height = Math.max(container.clientHeight, 600);
|
|
||||||
console.log('📐 Using forced dimensions:', width, 'x', height);
|
|
||||||
|
|
||||||
// Create scene with forced dimensions
|
|
||||||
this.radar3d = {
|
|
||||||
scene: new THREE.Scene(),
|
|
||||||
camera: new THREE.PerspectiveCamera(75, width / height, 0.1, 1000),
|
|
||||||
renderer: new THREE.WebGLRenderer({ alpha: true, antialias: true }),
|
|
||||||
controls: null,
|
|
||||||
aircraftMeshes: new Map()
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set up renderer with forced dimensions
|
|
||||||
this.radar3d.renderer.setSize(width, height);
|
|
||||||
} else {
|
|
||||||
// Create scene with actual container dimensions
|
|
||||||
this.radar3d = {
|
this.radar3d = {
|
||||||
scene: new THREE.Scene(),
|
scene: new THREE.Scene(),
|
||||||
camera: new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000),
|
camera: new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000),
|
||||||
|
|
@ -445,97 +368,41 @@ class SkyView {
|
||||||
|
|
||||||
// Set up renderer
|
// Set up renderer
|
||||||
this.radar3d.renderer.setSize(container.clientWidth, container.clientHeight);
|
this.radar3d.renderer.setSize(container.clientWidth, container.clientHeight);
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Three.js objects created:', {
|
|
||||||
scene: this.radar3d.scene,
|
|
||||||
camera: this.radar3d.camera,
|
|
||||||
renderer: this.radar3d.renderer
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check WebGL context
|
|
||||||
const rendererGL = this.radar3d.renderer.getContext();
|
|
||||||
if (!rendererGL) {
|
|
||||||
console.error('❌ Failed to get WebGL context');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log('✅ WebGL context created');
|
|
||||||
|
|
||||||
this.radar3d.renderer.setClearColor(0x0a0a0a, 0.9);
|
this.radar3d.renderer.setClearColor(0x0a0a0a, 0.9);
|
||||||
container.appendChild(this.radar3d.renderer.domElement);
|
container.appendChild(this.radar3d.renderer.domElement);
|
||||||
console.log('✅ Renderer added to container');
|
|
||||||
|
|
||||||
// Add lighting - ensure the scene is visible
|
// Add lighting
|
||||||
const ambientLight = new THREE.AmbientLight(0x404040, 1.0); // Increased intensity
|
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
|
||||||
this.radar3d.scene.add(ambientLight);
|
this.radar3d.scene.add(ambientLight);
|
||||||
|
|
||||||
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0); // Increased intensity
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
||||||
directionalLight.position.set(10, 50, 25);
|
directionalLight.position.set(10, 10, 5);
|
||||||
this.radar3d.scene.add(directionalLight);
|
this.radar3d.scene.add(directionalLight);
|
||||||
|
|
||||||
// Add hemisphere light for better illumination
|
|
||||||
const hemiLight = new THREE.HemisphereLight(0x0000ff, 0x00ff00, 0.6);
|
|
||||||
hemiLight.position.set(0, 100, 0);
|
|
||||||
this.radar3d.scene.add(hemiLight);
|
|
||||||
|
|
||||||
// Set up camera
|
// Set up camera
|
||||||
this.radar3d.camera.position.set(0, 50, 50);
|
this.radar3d.camera.position.set(0, 50, 50);
|
||||||
this.radar3d.camera.lookAt(0, 0, 0);
|
this.radar3d.camera.lookAt(0, 0, 0);
|
||||||
|
|
||||||
// Add controls
|
// Add controls
|
||||||
console.log('🎮 Adding OrbitControls...');
|
|
||||||
if (typeof OrbitControls === 'undefined') {
|
|
||||||
console.error('❌ OrbitControls not available');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.radar3d.controls = new OrbitControls(this.radar3d.camera, this.radar3d.renderer.domElement);
|
this.radar3d.controls = new OrbitControls(this.radar3d.camera, this.radar3d.renderer.domElement);
|
||||||
this.radar3d.controls.enableDamping = true;
|
this.radar3d.controls.enableDamping = true;
|
||||||
this.radar3d.controls.dampingFactor = 0.05;
|
this.radar3d.controls.dampingFactor = 0.05;
|
||||||
console.log('✅ OrbitControls added');
|
|
||||||
|
|
||||||
// Store default camera position for reset functionality
|
// Add ground plane
|
||||||
this.radar3d.defaultPosition = { x: 0, y: 50, z: 50 };
|
const groundGeometry = new THREE.PlaneGeometry(200, 200);
|
||||||
this.radar3d.defaultTarget = { x: 0, y: 0, z: 0 };
|
|
||||||
|
|
||||||
// Auto-rotate state
|
|
||||||
this.radar3d.autoRotate = false;
|
|
||||||
|
|
||||||
// Origin coordinates (will be fetched from server)
|
|
||||||
this.radar3d.origin = { latitude: 59.9081, longitude: 10.8015 }; // fallback
|
|
||||||
|
|
||||||
// Initialize 3D controls and fetch origin
|
|
||||||
this.initialize3DControls();
|
|
||||||
this.fetch3DOrigin();
|
|
||||||
|
|
||||||
// Add ground plane with better visibility
|
|
||||||
const groundGeometry = new THREE.PlaneGeometry(400, 400);
|
|
||||||
const groundMaterial = new THREE.MeshLambertMaterial({
|
const groundMaterial = new THREE.MeshLambertMaterial({
|
||||||
color: 0x1a3d2a,
|
color: 0x2a4d3a,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.8
|
opacity: 0.5
|
||||||
});
|
});
|
||||||
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
|
||||||
ground.rotation.x = -Math.PI / 2;
|
ground.rotation.x = -Math.PI / 2;
|
||||||
this.radar3d.scene.add(ground);
|
this.radar3d.scene.add(ground);
|
||||||
|
|
||||||
// Add grid with better visibility
|
// Add grid
|
||||||
const gridHelper = new THREE.GridHelper(400, 40, 0x44aa44, 0x44aa44);
|
const gridHelper = new THREE.GridHelper(200, 20, 0x44aa44, 0x44aa44);
|
||||||
this.radar3d.scene.add(gridHelper);
|
this.radar3d.scene.add(gridHelper);
|
||||||
|
|
||||||
// Add some reference objects to help with debugging
|
|
||||||
const cubeGeometry = new THREE.BoxGeometry(2, 2, 2);
|
|
||||||
const cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xff0000 });
|
|
||||||
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
|
|
||||||
cube.position.set(10, 1, 10);
|
|
||||||
this.radar3d.scene.add(cube);
|
|
||||||
|
|
||||||
console.log('3D Radar initialized successfully', {
|
|
||||||
scene: this.radar3d.scene,
|
|
||||||
camera: this.radar3d.camera,
|
|
||||||
renderer: this.radar3d.renderer
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start render loop
|
// Start render loop
|
||||||
this.render3DRadar();
|
this.render3DRadar();
|
||||||
|
|
||||||
|
|
@ -548,47 +415,25 @@ class SkyView {
|
||||||
if (!this.radar3d || !this.radar3d.scene || !this.aircraftManager) return;
|
if (!this.radar3d || !this.radar3d.scene || !this.aircraftManager) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Origin coordinates for distance calculation
|
|
||||||
const originLat = this.radar3d.origin.latitude;
|
|
||||||
const originLon = this.radar3d.origin.longitude;
|
|
||||||
const currentRange = this.radar3d.range || 100; // Default to 100km if not set
|
|
||||||
|
|
||||||
// Update aircraft positions in 3D space
|
// Update aircraft positions in 3D space
|
||||||
this.aircraftManager.aircraftData.forEach((aircraft, icao) => {
|
this.aircraftManager.aircraftData.forEach((aircraft, icao) => {
|
||||||
if (aircraft.Latitude && aircraft.Longitude) {
|
if (aircraft.Latitude && aircraft.Longitude) {
|
||||||
const key = icao.toString();
|
const key = icao.toString();
|
||||||
|
|
||||||
// Calculate distance from origin to filter by range
|
|
||||||
const distance = this.calculateDistance(
|
|
||||||
originLat, originLon,
|
|
||||||
aircraft.Latitude, aircraft.Longitude
|
|
||||||
);
|
|
||||||
|
|
||||||
// Check if aircraft is within range
|
|
||||||
const withinRange = distance <= currentRange;
|
|
||||||
|
|
||||||
if (!this.radar3d.aircraftMeshes.has(key)) {
|
if (!this.radar3d.aircraftMeshes.has(key)) {
|
||||||
// Create new aircraft mesh only if within range
|
// Create new aircraft mesh
|
||||||
if (withinRange) {
|
|
||||||
const geometry = new THREE.ConeGeometry(0.5, 2, 6);
|
const geometry = new THREE.ConeGeometry(0.5, 2, 6);
|
||||||
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
|
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });
|
||||||
const mesh = new THREE.Mesh(geometry, material);
|
const mesh = new THREE.Mesh(geometry, material);
|
||||||
this.radar3d.aircraftMeshes.set(key, mesh);
|
this.radar3d.aircraftMeshes.set(key, mesh);
|
||||||
this.radar3d.scene.add(mesh);
|
this.radar3d.scene.add(mesh);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Aircraft mesh exists, check if it should be visible
|
|
||||||
const mesh = this.radar3d.aircraftMeshes.get(key);
|
|
||||||
mesh.visible = withinRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update position if aircraft is within range
|
|
||||||
if (withinRange) {
|
|
||||||
const mesh = this.radar3d.aircraftMeshes.get(key);
|
const mesh = this.radar3d.aircraftMeshes.get(key);
|
||||||
|
|
||||||
// Convert lat/lon to local coordinates (simplified)
|
// Convert lat/lon to local coordinates (simplified)
|
||||||
const x = (aircraft.Longitude - originLon) * 111320 * Math.cos(aircraft.Latitude * Math.PI / 180) / 1000;
|
const x = (aircraft.Longitude - (-0.4600)) * 111320 * Math.cos(aircraft.Latitude * Math.PI / 180) / 1000;
|
||||||
const z = -(aircraft.Latitude - originLat) * 111320 / 1000;
|
const z = -(aircraft.Latitude - 51.4700) * 111320 / 1000;
|
||||||
const y = (aircraft.Altitude || 0) / 1000; // Convert feet to km for display
|
const y = (aircraft.Altitude || 0) / 1000; // Convert feet to km for display
|
||||||
|
|
||||||
mesh.position.set(x, y, z);
|
mesh.position.set(x, y, z);
|
||||||
|
|
@ -598,7 +443,6 @@ class SkyView {
|
||||||
mesh.rotation.y = -aircraft.Track * Math.PI / 180;
|
mesh.rotation.y = -aircraft.Track * Math.PI / 180;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove old aircraft
|
// Remove old aircraft
|
||||||
|
|
@ -625,103 +469,6 @@ class SkyView {
|
||||||
this.radar3d.renderer.render(this.radar3d.scene, this.radar3d.camera);
|
this.radar3d.renderer.render(this.radar3d.scene, this.radar3d.camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize3DControls() {
|
|
||||||
// Enable and initialize the Reset View button
|
|
||||||
const resetButton = document.getElementById('radar3d-reset');
|
|
||||||
if (resetButton) {
|
|
||||||
resetButton.disabled = false;
|
|
||||||
resetButton.addEventListener('click', () => this.reset3DView());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable and initialize the Auto Rotate toggle button
|
|
||||||
const autoRotateButton = document.getElementById('radar3d-auto-rotate');
|
|
||||||
if (autoRotateButton) {
|
|
||||||
autoRotateButton.disabled = false;
|
|
||||||
autoRotateButton.addEventListener('click', () => this.toggle3DAutoRotate());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable and initialize the Range slider
|
|
||||||
const rangeSlider = document.getElementById('radar3d-range');
|
|
||||||
const rangeValue = document.getElementById('radar3d-range-value');
|
|
||||||
if (rangeSlider && rangeValue) {
|
|
||||||
rangeSlider.disabled = false;
|
|
||||||
this.radar3d.range = parseInt(rangeSlider.value); // Store current range
|
|
||||||
|
|
||||||
rangeSlider.addEventListener('input', (e) => {
|
|
||||||
this.radar3d.range = parseInt(e.target.value);
|
|
||||||
rangeValue.textContent = this.radar3d.range;
|
|
||||||
this.update3DRadarRange();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reset3DView() {
|
|
||||||
if (!this.radar3d || !this.radar3d.controls) return;
|
|
||||||
|
|
||||||
// Reset camera to default position
|
|
||||||
this.radar3d.camera.position.set(
|
|
||||||
this.radar3d.defaultPosition.x,
|
|
||||||
this.radar3d.defaultPosition.y,
|
|
||||||
this.radar3d.defaultPosition.z
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset camera target
|
|
||||||
this.radar3d.controls.target.set(
|
|
||||||
this.radar3d.defaultTarget.x,
|
|
||||||
this.radar3d.defaultTarget.y,
|
|
||||||
this.radar3d.defaultTarget.z
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update controls to apply changes smoothly
|
|
||||||
this.radar3d.controls.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
toggle3DAutoRotate() {
|
|
||||||
if (!this.radar3d || !this.radar3d.controls) return;
|
|
||||||
|
|
||||||
// Toggle auto-rotate state
|
|
||||||
this.radar3d.autoRotate = !this.radar3d.autoRotate;
|
|
||||||
this.radar3d.controls.autoRotate = this.radar3d.autoRotate;
|
|
||||||
|
|
||||||
// Update button text to reflect current state
|
|
||||||
const autoRotateButton = document.getElementById('radar3d-auto-rotate');
|
|
||||||
if (autoRotateButton) {
|
|
||||||
autoRotateButton.textContent = this.radar3d.autoRotate ? 'Stop Auto Rotate' : 'Auto Rotate';
|
|
||||||
autoRotateButton.classList.toggle('active', this.radar3d.autoRotate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fetch3DOrigin() {
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/origin');
|
|
||||||
if (response.ok) {
|
|
||||||
const origin = await response.json();
|
|
||||||
this.radar3d.origin = origin;
|
|
||||||
console.log('3D Radar origin set to:', origin);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to fetch origin, using fallback:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
update3DRadarRange() {
|
|
||||||
// Simply trigger a full update of the 3D radar with new range filter
|
|
||||||
this.update3DRadar();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate distance between two lat/lon points in kilometers
|
|
||||||
calculateDistance(lat1, lon1, lat2, lon2) {
|
|
||||||
const R = 6371; // Earth's radius in kilometers
|
|
||||||
const dLat = (lat2 - lat1) * Math.PI / 180;
|
|
||||||
const dLon = (lon2 - lon1) * Math.PI / 180;
|
|
||||||
const a =
|
|
||||||
Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
||||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
|
||||||
Math.sin(dLon/2) * Math.sin(dLon/2);
|
|
||||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
||||||
return R * c;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateOpenPopupAges() {
|
updateOpenPopupAges() {
|
||||||
// Find any open aircraft popups and update their age displays
|
// Find any open aircraft popups and update their age displays
|
||||||
if (!this.aircraftManager) return;
|
if (!this.aircraftManager) return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue