ChatGS / templates /climatemap.html
sharmamohit8624's picture
Upload 2395 files
829f2ca verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>India Climate Map</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/>
<link rel="stylesheet" type="text/css" href="/static/styles.css">
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'Inter', sans-serif; background: #000; color: #e5e7eb; }
#map { height: 90vh; border-radius: 0.75rem; }
.loader {
border: 4px solid #222;
border-top: 4px solid #10b981;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
}
@keyframes spin { 0% { transform: rotate(0deg);} 100% { transform: rotate(360deg);} }
</style>
</head>
<body class="bg-black text-gray-200 antialiased">
<nav class="nav">
<img class="nav-img" src="{{ url_for('static', filename='assets/NRSC.png') }}" alt="NRSC-ISRO logo">
<ul>
<li class="active"><a href="/" style="padding: 12px;">Home</a></li><br>
<li class="about"><a href="/about">About</a></li><br>
<li class="info"><a href="/info">Reservoirs Info</a></li><br>
<li class="qr"><a href="/qr">QR Scanner</a></li><br>
<li class="command"><a href="/command">Face Recognition</a></li><br>
<li class="tutorial"><a href="/voiceassistant">Voice Assistant</a></li><br>
<li class="climatemap"><a href="/climatemap">Climate Map</a></li>
</ul>
</nav>
<div class="center-container">
<header class="text-center mb-8">
<h1 class="text-3xl md:text-4xl font-bold text-emerald-400">India Climate Map</h1>
<p class="text-md text-gray-400 mt-2">Enter a city or click on the map to get live weather updates.</p>
</header>
<main>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
<div class="lg:col-span-1 bg-gray-900 p-6 rounded-xl shadow-lg">
<div class="mb-6">
<label for="city-input" class="block text-sm font-medium text-gray-300 mb-2">Search for a City</label>
<div class="flex items-center space-x-2">
<input type="text" id="city-input" placeholder="e.g., Mumbai"
class="w-full px-4 py-2 border border-gray-700 bg-black text-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-emerald-400"/>
<button id="search-button"
class="px-4 py-2 bg-emerald-600 text-white font-semibold rounded-lg hover:bg-emerald-700 focus:ring-2 focus:ring-emerald-400">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 16 16">
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"/>
</svg>
</button>
</div>
</div>
<div class="mb-4">
<button id="radar-btn" class="w-full bg-emerald-600 text-white px-4 py-2 rounded-lg hover:bg-emerald-700">
Show Radar
</button>
</div>
<div id="weather-info" class="space-y-4"></div>
</div>
<div class="lg:col-span-2">
<div id="map"></div>
</div>
</div>
<div id="forecast-container" class="bg-gray-900 p-6 rounded-xl shadow-lg">
</div>
</main>
</div>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const apiKey = "683bae9848bec16b6e7db75f8bf0bc0e"; // your OpenWeatherMap key
const initialCity = "Hyderabad";
const cityInput = document.getElementById('city-input');
const searchButton = document.getElementById('search-button');
const weatherInfoDiv = document.getElementById('weather-info');
const forecastContainer = document.getElementById('forecast-container');
const radarBtn = document.getElementById('radar-btn');
const map = L.map('map').setView([22.5937, 78.9629], 5);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
let mapMarker;
// Radar Layer
const radarLayer = L.tileLayer(`https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid=${apiKey}`, {opacity: 0.5});
let radarVisible = false;
// ✅ Add tile recoloring logic here
radarLayer.on('tileload', function (event) {
let img = event.tile;
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
let data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
let alpha = data[i + 3] / 255;
let percent = alpha * 100;
let grey;
if (percent >= 90) {
grey = 0x4d; // Dark Gray
} else if (percent >= 75) {
grey = 0x66; // Medium Dark Gray
} else if (percent >= 40) {
grey = 0x99; // Medium Gray
} else if (percent >= 10) {
grey = 0xcc; // Light Gray
} else {
grey = 0xf2; // Very Light Gray
}
data[i] = grey; // Red
data[i + 1] = grey; // Green
data[i + 2] = grey; // Blue
data[i + 3] = 255; // Opaque
}
ctx.putImageData(imageData, 0, 0);
img.src = canvas.toDataURL();
});
radarBtn.addEventListener('click', () => {
if(!radarVisible) {
radarLayer.addTo(map);
radarBtn.textContent = "Hide Radar";
} else {
map.removeLayer(radarLayer);
radarBtn.textContent = "Show Radar";
}
radarVisible = !radarVisible;
});
// Fetch weather by city
const getWeatherByCity = (city) => {
showLoading();
fetch(`https://api.openweathermap.org/geo/1.0/direct?q=${city}&limit=1&appid=${apiKey}`)
.then(res => res.json())
.then(geo => {
if(!geo || geo.length===0) throw new Error("City not found");
const {lat, lon, name, state, country} = geo[0];
fetchWeatherByCoords(lat, lon, name, state, country);
}).catch(err => showError(err.message));
};
// Fetch weather by coords
const getWeatherByCoords = (lat, lon) => {
showLoading();
fetch(`https://api.openweathermap.org/geo/1.0/reverse?lat=${lat}&lon=${lon}&limit=1&appid=${apiKey}`)
.then(res => res.json())
.then(geo => {
let name="", state="", country="";
if(geo && geo.length>0){
name = geo[0].name;
state = geo[0].state;
country = geo[0].country;
}
fetchWeatherByCoords(lat, lon, name, state, country);
}).catch(err => showError(err.message));
};
const fetchWeatherByCoords = (lat, lon, name=null, state=null, country=null) => {
fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric`)
.then(res => { if(!res.ok) throw new Error("Weather data not found"); return res.json(); })
.then(async data => {
const cityName = name || data.name;
const cityState = state || "";
let cityCountry = country || data.sys.country;
try {
const countryRes = await fetch(`https://restcountries.com/v3.1/alpha/${cityCountry}`);
const countryJson = await countryRes.json();
cityCountry = countryJson[0]?.name?.common || cityCountry;
} catch(e) { console.error("Country fetch failed:", e); }
updateWeatherUI(data, cityName, cityState, cityCountry);
updateMap(lat, lon, cityName, cityState, cityCountry);
applyRainShadow(data);
fetchForecast(lat, lon);
}).catch(err => showError(err.message));
};
// Update UI
const updateWeatherUI = (data, cityName, cityState, cityCountry) => {
const rainChance = data.clouds ? data.clouds.all : 0;
const rainVolume = data.rain ? (data.rain["1h"] || 0) : 0;
weatherInfoDiv.innerHTML = `
<h2 class="text-2xl font-semibold text-emerald-400 mb-0">${cityName}</h2>
<p class="text-xs text-gray-400 mb-2">${cityState ? cityState + ", " : ""}${cityCountry}</p>
<p class="capitalize text-gray-400 mb-2">${data.weather[0].description}</p>
<img src="https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png" class="mx-auto">
<p class="text-3xl font-bold text-white mb-4">${Math.round(data.main.temp)}°C</p>
<div class="grid grid-cols-2 gap-4 text-sm">
<div class="flex items-center bg-gray-800 p-3 rounded-lg">
<i data-lucide="droplet" class="w-5 h-5 text-emerald-400 mr-2"></i>
<span>Humidity: <span class="text-emerald-400">${data.main.humidity}%</span></span>
</div>
<div class="flex items-center bg-gray-800 p-3 rounded-lg">
<i data-lucide="wind" class="w-5 h-5 text-emerald-400 mr-2"></i>
<span>Wind: <span class="text-emerald-400">${data.wind.speed} m/s</span></span>
</div>
<div class="flex items-center bg-gray-800 p-3 rounded-lg">
<i data-lucide="cloud-rain" class="w-5 h-5 text-emerald-400 mr-2"></i>
<span>Rain Chance: <span class="text-emerald-400">${rainChance}%</span></span>
</div>
<div class="flex items-center bg-gray-800 p-3 rounded-lg">
<i data-lucide="umbrella" class="w-5 h-5 text-emerald-400 mr-2"></i>
<span>Rain Volume: <span class="text-emerald-400">${rainVolume} mm</span></span>
</div>
</div>
`;
lucide.createIcons();
};
// Update map
const updateMap = (lat, lon, cityName, state, country) => {
map.setView([lat, lon], 10);
if(mapMarker) map.removeLayer(mapMarker);
mapMarker = L.marker([lat, lon]).addTo(map)
.bindPopup(`<b>${cityName}</b><br>${state ? state + ", " : ""}${country}`).openPopup();
};
const showLoading = () => {
weatherInfoDiv.innerHTML = `
<div class="flex flex-col items-center justify-center h-full space-y-3">
<div class="loader"></div>
<p class="text-gray-400">Fetching weather data...</p>
</div>`;
forecastContainer.innerHTML = `
<div class="flex items-center justify-center space-x-3">
<div class="loader"></div>
<p class="text-gray-400">Fetching forecast data...</p>
</div>`;
};
const showError = (msg) => {
weatherInfoDiv.innerHTML = `
<div class="bg-red-900 text-red-200 p-4 rounded-lg text-center">
<p class="font-semibold">Error</p>
<p class="text-sm">${msg}</p>
</div>`;
forecastContainer.innerHTML = `
<div class="text-center text-red-400">
<p>Could not load forecast.</p>
</div>`;
};
const applyRainShadow = (data) => {
let rainChance = data.clouds ? data.clouds.all : 0;
let opacity = 0;
if(rainChance >= 80) opacity = 1.0;
else if(rainChance >= 60) opacity = 0.8;
else if(rainChance >= 40) opacity = 0.5;
else if(rainChance >= 20) opacity = 0.2;
else opacity = 0.0;
radarLayer.setOpacity(opacity);
};
const fetchForecast = (lat, lon) => {
fetch(`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${apiKey}&units=metric`)
.then(res => res.json())
.then(data => {
if (!data.list) return;
const daily = [];
data.list.forEach(item => {
if (item.dt_txt.includes("12:00:00")) {
daily.push(item);
}
});
updateForecastUI(daily.slice(0, 5));
})
.catch(err => {
console.error("Forecast error:", err);
forecastContainer.innerHTML = `<div class="text-center text-red-400"><p>Could not load forecast data.</p></div>`;
});
};
const updateForecastUI = (daily) => {
forecastContainer.innerHTML = `
<h3 class="text-xl font-semibold text-emerald-400 mb-4 text-center lg:text-left">5-Day Forecast</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-4">
${daily.map(day => {
const date = new Date(day.dt * 1000);
const options = { weekday: 'short' };
const weekday = date.toLocaleDateString(undefined, options);
return `
<div class="bg-gray-800 p-3 rounded-lg flex flex-col items-center text-center">
<p class="text-sm font-semibold text-gray-300">${weekday}</p>
<img src="https://openweathermap.org/img/wn/${day.weather[0].icon}.png" alt="${day.weather[0].main}" class="w-12 h-12"/>
<p class="font-bold text-white">${Math.round(day.main.temp)}°C</p>
<p class="text-xs text-gray-400 capitalize">${day.weather[0].description}</p>
</div>
`;
}).join('')}
</div>
`;
};
searchButton.addEventListener('click', () => {
const city = cityInput.value.trim();
if(city) getWeatherByCity(city);
});
cityInput.addEventListener('keydown', e => { if(e.key==="Enter") searchButton.click(); });
map.on('click', e => getWeatherByCoords(e.latlng.lat, e.latlng.lng));
getWeatherByCity(initialCity);
});
</script>
</body>
</html>