metar-analyzer/public/script.js

246 lines
8.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

document.addEventListener('DOMContentLoaded', () => {
const stationInput = document.getElementById('station-input');
const daysInput = document.getElementById('days-input');
const fetchButton = document.getElementById('fetch-button');
const loadingDiv = document.getElementById('loading');
const errorDiv = document.getElementById('error');
const resultsDiv = document.getElementById('results');
const resultsTitle = document.getElementById('results-title');
const analysisSummary = document.getElementById('analysis-summary');
const rawDataContainer = document.getElementById('raw-data-container');
// Устанавливаем значения по умолчанию
// stationInput.value = 'UIII'; // Иркутск
daysInput.value = '14';
const charts = {};
const fetchAndDrawData = async () => {
const station = stationInput.value.toUpperCase() || 'UIII';
const days = daysInput.value || '14';
loadingDiv.classList.remove('hidden');
resultsDiv.classList.add('hidden');
errorDiv.classList.add('hidden');
try {
const response = await fetch(`/api/metar?station=${station}&days=${days}`);
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
}
const apiResponse = await response.json();
if (!apiResponse.data || apiResponse.data.length === 0) {
throw new Error('Данные для указанной станции или периода не найдены.');
}
processData(apiResponse.data, station, days);
loadingDiv.classList.add('hidden');
resultsDiv.classList.remove('hidden');
} catch (e) {
loadingDiv.classList.add('hidden');
errorDiv.textContent = `Ошибка: ${e.message}`;
errorDiv.classList.remove('hidden');
}
};
const processData = (data, station, days) => {
resultsTitle.textContent = `Результаты для ${station} за последние ${days} дней`;
const labels = [];
const temperatures = [];
const dewPoints = [];
const pressures = [];
const windSpeeds = [];
const windDirections = [];
const fToC = (f) => f !== null ? ((f - 32) * 5 / 9).toFixed(1) : null;
const inHgToHpa = (inHg) => inHg !== null ? (inHg * 33.8639).toFixed(1) : null;
const knotsToMs = (knots) => knots !== null ? (knots * 0.514444).toFixed(1) : null;
data.forEach(report => {
labels.push(new Date(report.valid).toLocaleString('ru-RU', { timeZone: 'UTC' }));
temperatures.push(fToC(report.tmpf));
dewPoints.push(fToC(report.dwpf));
pressures.push(inHgToHpa(report.alti));
windSpeeds.push(knotsToMs(report.sknt));
if (report.drct) {
windDirections.push(report.drct);
}
});
// графики
drawChart('temp-chart', 'line', {
labels,
datasets: [
{
label: 'Температура (°C)',
data: temperatures,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.2)',
fill: false,
tension: 0.1
},
{
label: 'Точка росы (°C)',
data: dewPoints,
borderColor: 'rgba(54, 162, 235, 1)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
fill: false,
tension: 0.1
}
]
}, 'Температура и точка росы');
drawChart('pressure-chart', 'line', {
labels,
datasets: [{
label: 'Давление (гПа)',
data: pressures,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: false,
tension: 0.1
}]
}, 'Атмосферное давление (QNH)');
drawChart('wind-speed-chart', 'line', {
labels,
datasets: [{
label: 'Скорость ветра (м/с)',
data: windSpeeds,
borderColor: 'rgba(153, 102, 255, 1)',
backgroundColor: 'rgba(153, 102, 255, 0.2)',
fill: false,
tension: 0.1
}]
}, 'Скорость ветра');
// анализ
performAnalysis(temperatures, windSpeeds, windDirections);
// отображение данных тлько последние 50
displayRawData(data.slice(-20));
};
const drawChart = (canvasId, type, data, title) => {
const ctx = document.getElementById(canvasId).getContext('2d');
if (charts[canvasId]) {
charts[canvasId].destroy();
}
charts[canvasId] = new Chart(ctx, {
type: type,
data: data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
font: { size: 16 }
}
},
scales: {
x: {
ticks: {
maxTicksLimit: 15
}
}
}
}
});
};
const performAnalysis = (temps, speeds, directions) => {
const validTemps = temps.filter(t => t !== null).map(Number);
const validSpeeds = speeds.filter(s => s !== null).map(Number);
const maxTemp = Math.max(...validTemps);
const minTemp = Math.min(...validTemps);
const avgTemp = (validTemps.reduce((a, b) => a + b, 0) / validTemps.length).toFixed(1);
const maxWind = Math.max(...validSpeeds);
const getWindDirection = (deg) => {
if (deg > 337.5 || deg <= 22.5) return 'С';
if (deg > 22.5 && deg <= 67.5) return 'СВ';
if (deg > 67.5 && deg <= 112.5) return 'В';
if (deg > 112.5 && deg <= 157.5) return 'ЮВ';
if (deg > 157.5 && deg <= 202.5) return 'Ю';
if (deg > 202.5 && deg <= 247.5) return 'ЮЗ';
if (deg > 247.5 && deg <= 292.5) return 'З';
if (deg > 292.5 && deg <= 337.5) return 'СЗ';
return null;
};
const dirCounts = directions.map(getWindDirection)
.filter(d => d !== null)
.reduce((acc, dir) => {
acc[dir] = (acc[dir] || 0) + 1;
return acc;
}, {});
const prevailingDir = Object.keys(dirCounts).length > 0 ? Object.keys(dirCounts).reduce((a, b) => dirCounts[a] > dirCounts[b] ? a : b) : 'Нет данных';
analysisSummary.innerHTML = `
<ul>
<li><strong>Температура:</strong></li>
<ul>
<li>Максимальная: <strong>${maxTemp}°C</strong></li>
<li>Минимальная: <strong>${minTemp}°C</strong></li>
<li>Средняя: <strong>${avgTemp}°C</strong></li>
</ul>
<li><strong>Ветер:</strong></li>
<ul>
<li>Максимальная скорость: <strong>${maxWind} м/с</strong></li>
<li>Преобладающее направление: <strong>${prevailingDir}</strong></li>
</ul>
</ul>
`;
};
const displayRawData = (data) => {
let tableHTML = `
<table>
<thead>
<tr>
<th>Время (UTC)</th>
<th>Сводка METAR</th>
</tr>
</thead>
<tbody>
`;
data.forEach(report => {
const date = new Date(report.valid);
const formattedDate = date.toLocaleString('ru-RU', {
timeZone: 'UTC',
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
tableHTML += `
<tr>
<td>${formattedDate}</td>
<td><code>${report.metar}</code></td>
</tr>
`;
});
tableHTML += `</tbody></table>`;
rawDataContainer.innerHTML = tableHTML;
};
fetchButton.addEventListener('click', fetchAndDrawData);
fetchAndDrawData();
});