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
-
-
-
-
-
-
-
-
-
-
-
-
-
- Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.
-
-
-
-
-
-
+
- const rainLayer = L.tileLayer('', {
- opacity: 0.6,
- zIndex: 100,
- attribution: 'Dane radarowe: RainViewer.com'
- });
+
+
+
+
+
+
+
+
+
+
+
Interaktywna Mapa Pogody
+
Kliknij na mapie lub wyszukaj miasto, aby zobaczyć aktualną prognozę i szczegółowe dane.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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