diff --git a/weather-webapp/templates/index.html b/weather-webapp/templates/index.html index b1e3da7..f9fef24 100644 --- a/weather-webapp/templates/index.html +++ b/weather-webapp/templates/index.html @@ -1,408 +1,805 @@ - + - - - - Pogoda Web - - - - - - -
-

Pogoda Web

-

Kliknij na mapie lub wyszukaj miasto, aby zobaczyć prognozę pogody

-
-
-
-
- - -
-
-
Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.
- - -
- - - + - const rainLayer = L.tileLayer('', { - opacity: 0.6, - zIndex: 100, - attribution: 'Dane radarowe: RainViewer.com' - }); + + + + + + +
+
+
Pogoda Web Menu
+ +
+
+ +
+

Wersja 1.0.0

+
+
+
+ +
+
+

Interaktywna Mapa Pogody

+

Kliknij na mapie lub wyszukaj miasto, aby zobaczyć aktualną prognozę i szczegółowe dane.

+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ Ładowanie... +
+
+ +
+
+
+ +
+

Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.

+
+ +

Prognoza dzienna

+ + +

Prognoza godzinowa

+ +
+ +
+ +
+ + + + + const map = L.map('map').setView([52.237, 21.017], 6); + let marker = null; + let currentPlaceName = null; + + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: '© OpenStreetMap' }).addTo(map); + + const rainLayer = L.tileLayer('', { + opacity: 0.6, + zIndex: 100, + attribution: 'Dane radarowe: RainViewer.com' + }); + + async function updateRainLayer() { + try { + const res = await fetch('https://api.rainviewer.com/public/weather-maps.json'); + const data = await res.json(); + const timestamps = data.radar.past; + if (!timestamps.length) return; + + const latestTime = timestamps[timestamps.length - 1].path; + const tileUrl = `https://tilecache.rainviewer.com/v2/radar/${latestTime}/256/{z}/{x}/{y}/2/1_1.png`; + + rainLayer.setUrl(tileUrl); + map.addLayer(rainLayer); + } catch (err) { + console.error("Błąd pobierania warstwy deszczu:", err); + } + } + + async function fetchWithTimeout(resource, options = {}) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 8000); + options.signal = controller.signal; + + try { + const response = await fetch(resource, options); + clearTimeout(timeout); + return response; + } catch (error) { + clearTimeout(timeout); + if (error.name === 'AbortError') { + throw new Error('Żądanie zostało przerwane: przekroczono limit czasu.'); + } + throw error; + } + } + + async function fetchPlaceName(lat, lon) { + const url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}&zoom=18&addressdetails=1`; + const response = await fetchWithTimeout(url, { + headers: { 'User-Agent': 'PogodaApp/1.0 (your-email@example.com)' } + }); + if (!response.ok) throw new Error('Błąd podczas pobierania nazwy miejsca.'); + const data = await response.json(); + return data.display_name || 'Nieznana lokalizacja'; + } + + function setMarker(lat, lon, name) { + if (marker) map.removeLayer(marker); + marker = L.marker([lat, lon]).addTo(map); + if (name) marker.bindPopup(name).openPopup(); + } + + map.on('click', async (e) => { + const { lat, lng } = e.latlng; + setMarker(lat, lng, "Ładowanie nazwy..."); + map.setView([lat, lng], 10); + updateResult("Ładowanie nazwy lokalizacji..."); + showSpinner(true); + try { + const placeName = await fetchPlaceName(lat, lng); + currentPlaceName = placeName; + setMarker(lat, lng, placeName); + updateResult("Ładowanie pogody..."); + await fetchWeather(lat, lng); + } catch (err) { + updateResult("Błąd: " + err.message, true); + } + showSpinner(false); + }); + + async function searchCity() { + const city = document.getElementById('cityInput').value.trim(); + if (!city) { + updateResult("Proszę wpisać nazwę miasta.", true); + return; + } + + updateResult("Wyszukiwanie miasta..."); + showSpinner(true); + try { + const res = await fetchWithTimeout(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(city)}&limit=1`, { + headers: { 'User-Agent': 'PogodaApp/1.0 (your-email@example.com)' } + }); + if (!res.ok) throw new Error('Błąd podczas wyszukiwania miasta.'); + const data = await res.json(); + if (!data.length) { + updateResult("Nie znaleziono miasta.", true); + return; + } + + const { lat, lon, display_name } = data[0]; + currentPlaceName = display_name; + map.setView([lat, lon], 10); + setMarker(lat, lon, display_name); + updateResult("Ładowanie pogody..."); + await fetchWeather(lat, lon); + } catch (err) { + updateResult("Błąd: " + err.message, true); + } + showSpinner(false); + } + + let dailyChart = null; + let hourlyChart = null; + + async function fetchWeather(lat, lon) { + try { + const res = await fetchWithTimeout('/app/get_weather', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ lat, lon }) + }); + + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Błąd serwera: ${res.status} - ${errorText}`); + } + const data = await res.json(); + if (data.error) return updateResult("Błąd serwera: " + data.error, true); + + const current = data.current ?? {}; + const daily = data.daily?.forecast ?? {}; + const hourly = data.hourly?.hourly_forecast ?? {}; + + let resultText = ` +

${currentPlaceName ?? 'Nieznana lokalizacja'}

+

Temperatura: ${current.temperature ?? 'brak danych'} °C

+

Wiatr: ${current.windspeed ?? 'brak danych'} km/h

+

Czas pomiaru: ${current.time ? new Date(current.time).toLocaleString() : 'brak danych'}

+ `; + + updateResult(resultText.trim()); + drawDailyChart(daily); + drawHourlyChart(hourly); + } catch (err) { + updateResult("Błąd podczas pobierania pogody: " + err.message, true); + } + } + + function drawDailyChart(data) { + const ctx = document.getElementById('dailyChart').getContext('2d'); + if (dailyChart) dailyChart.destroy(); + + dailyChart = new Chart(ctx, { + type: 'bar', + data: { + labels: data.time.map(t => new Date(t).toLocaleDateString('pl-PL', { weekday: 'short', day: 'numeric', month: 'short' })), + datasets: [ + { + label: 'Temp. max (°C)', + data: data.temperature_2m_max, + backgroundColor: 'rgba(255, 99, 132, 0.8)', + borderColor: 'rgba(255, 99, 132, 1)', + borderWidth: 1 + }, + { + label: 'Temp. min (°C)', + data: data.temperature_2m_min, + backgroundColor: 'rgba(54, 162, 235, 0.8)', + borderColor: 'rgba(54, 162, 235, 1)', + borderWidth: 1 + }, + { + label: 'Opady (mm)', + data: data.precipitation_sum, + type: 'line', + borderColor: 'rgba(0, 200, 0, 0.8)', + backgroundColor: 'rgba(0, 200, 0, 0.2)', + fill: true, + yAxisID: 'y1', + tension: 0.3 + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: true, + interaction: { mode: 'index', intersect: false }, + stacked: false, + plugins: { + title: { + display: true, + text: 'Prognoza dzienna', + color: '#e2e6ea', + font: { + size: 18 + } + }, + legend: { + labels: { + color: '#e2e6ea', + font: { + size: 12 + } + } + }, + tooltip: { + backgroundColor: 'rgba(0,0,0,0.7)', + titleColor: '#fff', + bodyColor: '#fff', + borderColor: '#0d6efd', + } + } + } + }); + } + - - - + \ No newline at end of file