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 = `
| Время (UTC) | Сводка METAR |
|---|---|
| ${formattedDate} | ${report.metar} |