fix(weather): align weather interpretation with BMKG rainfall threshold standards
Browse files- frontend/app.js +58 -37
frontend/app.js
CHANGED
|
@@ -414,38 +414,41 @@ async function fetchLiveWeather(loc) {
|
|
| 414 |
const precipList = dailyData.precipitation_sum || [];
|
| 415 |
const precipToday = precipList[2] || 0;
|
| 416 |
|
| 417 |
-
let cond = "
|
| 418 |
-
if (code === 0) cond = "
|
| 419 |
-
else if (code
|
| 420 |
-
else if (code
|
| 421 |
-
else if (
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
if (weatherForecastText) weatherForecastText.textContent = `${temp}°C - ${cond}`;
|
| 424 |
if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
|
| 425 |
if (weatherPrecip) weatherPrecip.textContent = `${precipToday.toFixed(1)} mm`;
|
| 426 |
|
| 427 |
if (weatherAlert) {
|
| 428 |
-
if (precipToday >
|
| 429 |
-
weatherAlert.textContent = "HEAVY RAIN 🟡";
|
| 430 |
-
weatherAlert.className = "highlight text-warning";
|
| 431 |
-
} else if (precipToday > 50) {
|
| 432 |
weatherAlert.textContent = "FLOOD DANGER 🔴";
|
| 433 |
weatherAlert.className = "highlight text-red";
|
|
|
|
|
|
|
|
|
|
| 434 |
} else {
|
| 435 |
-
weatherAlert.textContent = "Normal
|
| 436 |
weatherAlert.className = "highlight";
|
| 437 |
}
|
| 438 |
}
|
| 439 |
} catch (err) {
|
| 440 |
console.warn("Weather fetch timed out/failed. Using fallback forecast.", err);
|
| 441 |
// Instant Fallback Weather Data
|
| 442 |
-
const fallbackTemp =
|
| 443 |
const fallbackPrecip = 0.0;
|
| 444 |
-
if (weatherForecastText) weatherForecastText.textContent = `${fallbackTemp.toFixed(1)}°C -
|
| 445 |
if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
|
| 446 |
if (weatherPrecip) weatherPrecip.textContent = `${fallbackPrecip.toFixed(1)} mm`;
|
| 447 |
if (weatherAlert) {
|
| 448 |
-
weatherAlert.textContent = "Normal
|
| 449 |
weatherAlert.className = "highlight";
|
| 450 |
}
|
| 451 |
}
|
|
@@ -779,29 +782,47 @@ async function openWeatherModal(loc) {
|
|
| 779 |
const tMin = Math.round(tempMin[idx] || 25);
|
| 780 |
|
| 781 |
let icon = "☀️";
|
| 782 |
-
let condition = "
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
} else if (
|
| 789 |
-
icon = "🌧️";
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
} else if (
|
| 795 |
-
icon = "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
}
|
| 797 |
|
|
|
|
|
|
|
| 798 |
const row = document.createElement("div");
|
| 799 |
row.className = "weather-forecast-row";
|
| 800 |
row.innerHTML = `
|
| 801 |
<span class="w-row-day">${dayName}</span>
|
| 802 |
<span class="w-row-icon">${icon}</span>
|
| 803 |
<span class="w-row-cond">${condition}</span>
|
| 804 |
-
<span class="w-row-precip">${
|
| 805 |
<div class="w-row-temp">
|
| 806 |
<span class="w-temp-max">${tMax}°</span>
|
| 807 |
<span class="w-temp-min">${tMin}°</span>
|
|
@@ -815,17 +836,17 @@ async function openWeatherModal(loc) {
|
|
| 815 |
console.warn("Open-Meteo weather modal fallback:", err);
|
| 816 |
// Fallback matching real Jakarta climate baseline
|
| 817 |
const fallbackData = [
|
| 818 |
-
{ day: "Kam", icon: "
|
| 819 |
-
{ day: "Jum", icon: "
|
| 820 |
-
{ day: "Sab", icon: "
|
| 821 |
-
{ day: "Min", icon: "
|
| 822 |
-
{ day: "Sen", icon: "
|
| 823 |
-
{ day: "Sel", icon: "
|
| 824 |
-
{ day: "Rab", icon: "🌧️", cond: "
|
| 825 |
];
|
| 826 |
|
| 827 |
-
if (modalSumTotal) modalSumTotal.textContent = "
|
| 828 |
-
if (modalSumAvg) modalSumAvg.textContent = "
|
| 829 |
if (modalSumBadge) {
|
| 830 |
modalSumBadge.textContent = "KERING";
|
| 831 |
modalSumBadge.className = "weather-dryness-badge dry";
|
|
|
|
| 414 |
const precipList = dailyData.precipitation_sum || [];
|
| 415 |
const precipToday = precipList[2] || 0;
|
| 416 |
|
| 417 |
+
let cond = "Cerah Berawan";
|
| 418 |
+
if (code === 0 && precipToday < 0.5) cond = "Cerah";
|
| 419 |
+
else if (code <= 2 && precipToday < 0.5) cond = "Cerah Berawan";
|
| 420 |
+
else if (code <= 3 && precipToday < 0.5) cond = "Berawan";
|
| 421 |
+
else if (precipToday >= 20.0) cond = "Hujan Lebat";
|
| 422 |
+
else if (precipToday >= 5.0) cond = "Hujan Sedang";
|
| 423 |
+
else if (precipToday >= 0.5) cond = "Hujan Ringan";
|
| 424 |
+
else cond = "Berawan";
|
| 425 |
|
| 426 |
if (weatherForecastText) weatherForecastText.textContent = `${temp}°C - ${cond}`;
|
| 427 |
if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
|
| 428 |
if (weatherPrecip) weatherPrecip.textContent = `${precipToday.toFixed(1)} mm`;
|
| 429 |
|
| 430 |
if (weatherAlert) {
|
| 431 |
+
if (precipToday >= 50) {
|
|
|
|
|
|
|
|
|
|
| 432 |
weatherAlert.textContent = "FLOOD DANGER 🔴";
|
| 433 |
weatherAlert.className = "highlight text-red";
|
| 434 |
+
} else if (precipToday >= 20) {
|
| 435 |
+
weatherAlert.textContent = "HEAVY RAIN 🟡";
|
| 436 |
+
weatherAlert.className = "highlight text-warning";
|
| 437 |
} else {
|
| 438 |
+
weatherAlert.textContent = "Normal / Aman";
|
| 439 |
weatherAlert.className = "highlight";
|
| 440 |
}
|
| 441 |
}
|
| 442 |
} catch (err) {
|
| 443 |
console.warn("Weather fetch timed out/failed. Using fallback forecast.", err);
|
| 444 |
// Instant Fallback Weather Data
|
| 445 |
+
const fallbackTemp = 32.5 + Math.random() * 2.0;
|
| 446 |
const fallbackPrecip = 0.0;
|
| 447 |
+
if (weatherForecastText) weatherForecastText.textContent = `${fallbackTemp.toFixed(1)}°C - Cerah Berawan`;
|
| 448 |
if (weatherLocationText) weatherLocationText.textContent = `${loc} (${coord.city})`;
|
| 449 |
if (weatherPrecip) weatherPrecip.textContent = `${fallbackPrecip.toFixed(1)} mm`;
|
| 450 |
if (weatherAlert) {
|
| 451 |
+
weatherAlert.textContent = "Normal / Aman";
|
| 452 |
weatherAlert.className = "highlight";
|
| 453 |
}
|
| 454 |
}
|
|
|
|
| 782 |
const tMin = Math.round(tempMin[idx] || 25);
|
| 783 |
|
| 784 |
let icon = "☀️";
|
| 785 |
+
let condition = "Cerah";
|
| 786 |
+
|
| 787 |
+
// BMKG Meteorological Standard Interpretation
|
| 788 |
+
if (wCode >= 95 || (wCode >= 80 && pVal >= 20.0)) {
|
| 789 |
+
icon = "⛈️";
|
| 790 |
+
condition = "Badai Petir";
|
| 791 |
+
} else if (pVal >= 20.0) {
|
| 792 |
+
icon = "🌧️";
|
| 793 |
+
condition = "Hujan Lebat";
|
| 794 |
+
} else if (pVal >= 5.0) {
|
| 795 |
+
icon = "🌧️";
|
| 796 |
+
condition = "Hujan Sedang";
|
| 797 |
+
} else if (pVal >= 1.5) {
|
| 798 |
+
icon = "🌦️";
|
| 799 |
+
condition = "Hujan Ringan";
|
| 800 |
+
} else if (pVal >= 0.5) {
|
| 801 |
+
icon = "⛅";
|
| 802 |
+
condition = "Berawan";
|
| 803 |
+
} else {
|
| 804 |
+
// Under 0.5mm is non-rainy (Cerah / Cerah Berawan)
|
| 805 |
+
if (wCode === 0) {
|
| 806 |
+
icon = "☀️";
|
| 807 |
+
condition = "Cerah";
|
| 808 |
+
} else if (wCode <= 2) {
|
| 809 |
+
icon = "🌤️";
|
| 810 |
+
condition = "Cerah Berawan";
|
| 811 |
+
} else {
|
| 812 |
+
icon = "⛅";
|
| 813 |
+
condition = "Berawan";
|
| 814 |
+
}
|
| 815 |
}
|
| 816 |
|
| 817 |
+
const precipDisplay = pVal >= 0.5 ? `${pVal.toFixed(1)}mm` : '';
|
| 818 |
+
|
| 819 |
const row = document.createElement("div");
|
| 820 |
row.className = "weather-forecast-row";
|
| 821 |
row.innerHTML = `
|
| 822 |
<span class="w-row-day">${dayName}</span>
|
| 823 |
<span class="w-row-icon">${icon}</span>
|
| 824 |
<span class="w-row-cond">${condition}</span>
|
| 825 |
+
<span class="w-row-precip">${precipDisplay}</span>
|
| 826 |
<div class="w-row-temp">
|
| 827 |
<span class="w-temp-max">${tMax}°</span>
|
| 828 |
<span class="w-temp-min">${tMin}°</span>
|
|
|
|
| 836 |
console.warn("Open-Meteo weather modal fallback:", err);
|
| 837 |
// Fallback matching real Jakarta climate baseline
|
| 838 |
const fallbackData = [
|
| 839 |
+
{ day: "Kam", icon: "🌦️", cond: "Hujan Ringan", precip: "1.3mm", max: 33, min: 25 },
|
| 840 |
+
{ day: "Jum", icon: "⛅", cond: "Berawan", precip: "", max: 35, min: 25 },
|
| 841 |
+
{ day: "Sab", icon: "🌤️", cond: "Cerah Berawan", precip: "", max: 36, min: 25 },
|
| 842 |
+
{ day: "Min", icon: "☀️", cond: "Cerah", precip: "", max: 34, min: 26 },
|
| 843 |
+
{ day: "Sen", icon: "🌦️", cond: "Hujan Ringan", precip: "1.5mm", max: 33, min: 26 },
|
| 844 |
+
{ day: "Sel", icon: "🌤️", cond: "Cerah Berawan", precip: "", max: 35, min: 26 },
|
| 845 |
+
{ day: "Rab", icon: "🌧️", cond: "Hujan Sedang", precip: "3.6mm", max: 35, min: 27 }
|
| 846 |
];
|
| 847 |
|
| 848 |
+
if (modalSumTotal) modalSumTotal.textContent = "6.4mm";
|
| 849 |
+
if (modalSumAvg) modalSumAvg.textContent = "0.9mm/hr";
|
| 850 |
if (modalSumBadge) {
|
| 851 |
modalSumBadge.textContent = "KERING";
|
| 852 |
modalSumBadge.className = "weather-dryness-badge dry";
|