modified index.html
This commit is contained in:
@ -2,10 +2,12 @@
|
|||||||
<html lang="pl">
|
<html lang="pl">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<title>Pogoda Web</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
|
<meta name="description" content="Interaktywna mapa pogody – kliknij lub wyszukaj miasto, by sprawdzić prognozę.">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<title>Pogoda Web</title>
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" loading="lazy" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js" loading="lazy"></script>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -92,26 +94,43 @@
|
|||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div id="controls">
|
<div id="controls">
|
||||||
<input type="text" id="cityInput" placeholder="Wpisz miasto" />
|
<input type="text" id="cityInput" placeholder="Wpisz miasto" autocomplete="off" />
|
||||||
<button onclick="searchCity()">Szukaj</button>
|
<button onclick="searchCity()">Szukaj</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="map"></div>
|
<div id="map" title="Mapa pogodowa" aria-label="Mapa pogodowa z możliwością kliknięcia"></div>
|
||||||
<div id="result">Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.</div>
|
<div id="result">Kliknij na mapie lub wyszukaj miasto, aby zobaczyć pogodę.</div>
|
||||||
<canvas id="dailyChart"></canvas>
|
<canvas id="dailyChart"></canvas>
|
||||||
<canvas id="hourlyChart"></canvas>
|
<canvas id="hourlyChart"></canvas>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet/dist/leaflet.js" loading="lazy"></script>
|
||||||
<script>
|
<script>
|
||||||
const map = L.map('map').setView([52.237, 21.017], 6);
|
const map = L.map('map').setView([52.237, 21.017], 6);
|
||||||
let marker = null;
|
let marker = null;
|
||||||
let currentPlaceName = '';
|
let currentPlaceName = null;
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18 }).addTo(map);
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18 }).addTo(map);
|
||||||
|
|
||||||
|
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);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchPlaceName(lat, lon) {
|
async function fetchPlaceName(lat, lon) {
|
||||||
const url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}`;
|
const url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lon}`;
|
||||||
const response = await fetch(url, { headers: { 'User-Agent': 'PogodaApp/1.0' } });
|
const response = await fetchWithTimeout(url, {
|
||||||
|
headers: { 'User-Agent': 'PogodaApp/1.0' }
|
||||||
|
});
|
||||||
if (!response.ok) throw new Error('Błąd podczas pobierania nazwy miejsca.');
|
if (!response.ok) throw new Error('Błąd podczas pobierania nazwy miejsca.');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data.display_name || 'Nieznana lokalizacja';
|
return data.display_name || 'Nieznana lokalizacja';
|
||||||
@ -126,6 +145,7 @@
|
|||||||
map.on('click', async (e) => {
|
map.on('click', async (e) => {
|
||||||
const { lat, lng } = e.latlng;
|
const { lat, lng } = e.latlng;
|
||||||
setMarker(lat, lng, "Ładowanie nazwy...");
|
setMarker(lat, lng, "Ładowanie nazwy...");
|
||||||
|
map.setView([lat, lng], 10);
|
||||||
updateResult("Ładowanie nazwy lokalizacji...");
|
updateResult("Ładowanie nazwy lokalizacji...");
|
||||||
try {
|
try {
|
||||||
const placeName = await fetchPlaceName(lat, lng);
|
const placeName = await fetchPlaceName(lat, lng);
|
||||||
@ -144,19 +164,15 @@
|
|||||||
|
|
||||||
async function searchCity() {
|
async function searchCity() {
|
||||||
const city = document.getElementById('cityInput').value.trim();
|
const city = document.getElementById('cityInput').value.trim();
|
||||||
if (!city) {
|
if (!city) return updateResult("Proszę wpisać nazwę miasta.");
|
||||||
updateResult("Proszę wpisać nazwę miasta.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateResult("Wyszukiwanie miasta...");
|
updateResult("Wyszukiwanie miasta...");
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(city)}`);
|
const res = await fetchWithTimeout(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(city)}`);
|
||||||
if (!res.ok) throw new Error('Błąd podczas wyszukiwania miasta.');
|
if (!res.ok) throw new Error('Błąd podczas wyszukiwania miasta.');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.length === 0) {
|
if (!data.length) return updateResult("Nie znaleziono miasta.");
|
||||||
updateResult("Nie znaleziono miasta.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { lat, lon, display_name } = data[0];
|
const { lat, lon, display_name } = data[0];
|
||||||
currentPlaceName = display_name;
|
currentPlaceName = display_name;
|
||||||
map.setView([lat, lon], 10);
|
map.setView([lat, lon], 10);
|
||||||
@ -173,39 +189,37 @@
|
|||||||
|
|
||||||
async function fetchWeather(lat, lon) {
|
async function fetchWeather(lat, lon) {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/get_weather', {
|
const res = await fetchWithTimeout('/get_weather', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ lat, lon })
|
body: JSON.stringify({ lat, lon })
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) throw new Error(`Błąd serwera: ${res.status}`);
|
if (!res.ok) throw new Error(`Błąd serwera: ${res.status}`);
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
if (data.error) return updateResult("Błąd serwera: " + data.error);
|
||||||
|
|
||||||
if (data.error) {
|
const current = data.current ?? {};
|
||||||
updateResult("Błąd serwera: " + data.error);
|
const daily = data.daily?.forecast ?? {};
|
||||||
return;
|
const hourly = data.hourly?.hourly_forecast ?? {};
|
||||||
}
|
|
||||||
|
|
||||||
const current = data.current || {};
|
let resultText = `Lokalizacja: ${currentPlaceName ?? 'Nieznana lokalizacja'}
|
||||||
const daily = data.daily?.forecast || {};
|
|
||||||
const hourly = data.hourly?.hourly_forecast || {};
|
|
||||||
|
|
||||||
let resultText = `Lokalizacja: ${currentPlaceName}
|
|
||||||
Temperatura: ${current.temperature ?? 'brak danych'} °C
|
Temperatura: ${current.temperature ?? 'brak danych'} °C
|
||||||
Wiatr: ${current.windspeed ?? 'brak danych'} km/h
|
Wiatr: ${current.windspeed ?? 'brak danych'} km/h
|
||||||
Czas pomiaru: ${current.time ? new Date(current.time).toLocaleString() : 'brak danych'}`;
|
Czas pomiaru: ${current.time ? new Date(current.time).toLocaleString() : 'brak danych'}`;
|
||||||
|
|
||||||
if (daily.time?.length) {
|
if (Array.isArray(daily.time) && daily.time.length > 0) {
|
||||||
resultText += "\n\nPrognoza 7-dniowa:\n";
|
resultText += "\n\nPrognoza 7-dniowa:\n";
|
||||||
for (let i = 0; i < daily.time.length; i++) {
|
for (let i = 0; i < daily.time.length; i++) {
|
||||||
resultText += `${daily.time[i]}: ${daily.temperature_2m_min[i]}–${daily.temperature_2m_max[i]} °C, Opady: ${daily.precipitation_sum[i]} mm\n`;
|
resultText += `${daily.time[i]}: ${daily.temperature_2m_min?.[i]}–${daily.temperature_2m_max?.[i]} °C, Opady: ${daily.precipitation_sum?.[i]} mm\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hourly.time?.length) {
|
if (Array.isArray(hourly.time) && hourly.time.length > 0) {
|
||||||
resultText += "\n\nNajbliższe godziny:\n";
|
resultText += "\n\nNajbliższe godziny:\n";
|
||||||
for (let i = 0; i < Math.min(24, hourly.time.length); i++) {
|
for (let i = 0; i < Math.min(24, hourly.time.length); i++) {
|
||||||
resultText += `${hourly.time[i]}: ${hourly.temperature_2m[i]} °C, Wiatr: ${hourly.windspeed_10m[i]} km/h, Opady: ${hourly.precipitation[i]} mm\n`;
|
resultText += `${hourly.time[i]}: ${hourly.temperature_2m?.[i]} °C, Wiatr: ${hourly.windspeed_10m?.[i]} km/h, Opady: ${hourly.precipitation?.[i]} mm\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,19 +262,13 @@ Czas pomiaru: ${current.time ? new Date(current.time).toLocaleString() : 'brak d
|
|||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
responsive: true,
|
responsive: true,
|
||||||
interaction: {
|
interaction: { mode: 'index', intersect: false },
|
||||||
mode: 'index',
|
|
||||||
intersect: false,
|
|
||||||
},
|
|
||||||
stacked: false,
|
stacked: false,
|
||||||
scales: {
|
scales: {
|
||||||
y: {
|
y: {
|
||||||
type: 'linear',
|
|
||||||
position: 'left',
|
|
||||||
title: { display: true, text: 'Temperatura (°C)' }
|
title: { display: true, text: 'Temperatura (°C)' }
|
||||||
},
|
},
|
||||||
y1: {
|
y1: {
|
||||||
type: 'linear',
|
|
||||||
position: 'right',
|
position: 'right',
|
||||||
title: { display: true, text: 'Opady (mm)' },
|
title: { display: true, text: 'Opady (mm)' },
|
||||||
grid: { drawOnChartArea: false }
|
grid: { drawOnChartArea: false }
|
||||||
|
Reference in New Issue
Block a user