-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplane-tracker.js
More file actions
122 lines (98 loc) · 3.7 KB
/
Copy pathplane-tracker.js
File metadata and controls
122 lines (98 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// plane-tracker.js
let LOCATIONS = [];
const bearingToCardinal = deg => {
const directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
return directions[Math.round(deg / 45) % 8];
};
export async function loadLocations() {
try {
const res = await fetch('locations.json');
LOCATIONS = await res.json();
return LOCATIONS;
} catch (e) {
console.error("Failed to load locations:", e);
return [];
}
}
const REFRESH_INTERVAL = 5000;
const toRad = deg => deg * Math.PI / 180;
const haversine = (lat1, lon1, lat2, lon2) => {
const R = 3440.065;
const dLat = toRad(lat2 - lat1);
const dLon = toRad(lon2 - lon1);
const a = Math.sin(dLat / 2) ** 2 +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon / 2) ** 2;
return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
const bearing = (lat1, lon1, lat2, lon2) => {
const y = Math.sin(toRad(lon2 - lon1)) * Math.cos(toRad(lat2));
const x = Math.cos(toRad(lat1)) * Math.sin(toRad(lat2)) -
Math.sin(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.cos(toRad(lon2 - lon1));
return (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
};
const getPlaneData = async () => {
const res = await fetch('http://localhost:3000/planes');
return await res.json();
};
const updateUI = (nearest, distance, location) => {
const arrow = document.getElementById("arrow");
const distanceElem = document.getElementById("distance");
const detailsElem = document.getElementById("details");
if (!nearest) {
arrow.style.transform = `rotate(0deg)`;
arrow.style.opacity = "0.3";
distanceElem.textContent = "-- nm";
detailsElem.innerHTML = `<span class='details-empty'>No planes nearby</span>`;
return;
}
const brng = bearing(location.lat, location.lon, nearest.lat, nearest.lon);
const relativeBearing = (brng - location.heading + 90 + 360) % 360;
arrow.style.transform = `rotate(${relativeBearing}deg)`;
arrow.style.opacity = "1";
const cardinal = bearingToCardinal(relativeBearing);
distanceElem.innerHTML = `${cardinal} • <span class="unit">${distance.toFixed(1)} nm</span>`;
const flightId = nearest.flight?.trim() || "Unknown";
const altitude = nearest.alt_baro ? `${nearest.alt_baro.toLocaleString()} ft` : "? ft";
const type = nearest.t || "";
detailsElem.innerHTML = `
<span class='details-flight'>${flightId}</span>
<span class='details-altitude'>${altitude}</span>
<span class='details-type'>${type}</span>
`;
};
export const fetchPlanes = async () => {
try {
const data = await getPlaneData();
const location = LOCATIONS[0];
if (!data.ac || data.ac.length === 0) {
updateUI(null, null, location);
return;
}
let nearest = null;
let minDist = Infinity;
for (const plane of data.ac) {
if (!plane.lat || !plane.lon) continue;
const dist = haversine(location.lat, location.lon, plane.lat, plane.lon);
if (dist < minDist || (dist === minDist && plane.alt_baro < (nearest?.alt_baro || Infinity))) {
nearest = plane;
minDist = dist;
}
}
if (nearest) {
const nearestBrng = bearing(location.lat, location.lon, nearest.lat, nearest.lon);
console.log(`Nearest plane ${nearest.flight?.trim() || "Unknown"} at bearing: ${nearestBrng.toFixed(1)}°`);
}
updateUI(nearest, minDist, location);
} catch (e) {
console.error("Error fetching plane data:", e);
document.getElementById("details").innerHTML = `<span class='details-error'>Error fetching data</span>`;
}
};
export const startTracking = async () => {
await loadLocations();
fetchPlanes();
const arrow = document.getElementById('arrow');
if (arrow) arrow.classList.add('active');
setInterval(fetchPlanes, REFRESH_INTERVAL);
};