Spaces:
Sleeping
Sleeping
| import os | |
| import requests | |
| import pandas as pd | |
| import gradio as gr | |
| from datetime import datetime, timedelta, timezone | |
| from zoneinfo import ZoneInfo | |
| # --- CONFIG --- | |
| TOMORROW_API_KEY = os.getenv("TOMORROW_API_KEY", "teKj9Rkys1UzWxKBEs36pAR8paCXnPW6") | |
| METEOMATICS_USERNAME = os.getenv("METEO_USERNAME", "ptbukitteknologidigital_tudjuka_ricky") | |
| METEOMATICS_PASSWORD = os.getenv("METEO_PASSWORD", "u5n25MwaviEG3O3v8q94") | |
| DEFAULT_LAT = 0.46876 | |
| DEFAULT_LON = 116.16879 | |
| LOCAL_TZ = ZoneInfo("Asia/Kuala_Lumpur") # ✅ changed from Asia/Jakarta to Asia/Kuala_Lumpur | |
| MM_VAR_RAIN = "precip_1h:mm" | |
| MM_VAR_PROB = "prob_precip_1h:p" | |
| DEFAULT_MODEL = "ecmwf-ifs" | |
| # --- Tomorrow.io fetch --- | |
| def fetch_tomorrow(lat, lon, hours=24): | |
| if not TOMORROW_API_KEY: | |
| raise RuntimeError("Missing TOMORROW_API_KEY") | |
| url = "https://api.tomorrow.io/v4/weather/forecast" | |
| params = { | |
| "location": f"{lat},{lon}", | |
| "units": "metric", | |
| "fields": "rainIntensity,precipitationProbability", | |
| "apikey": TOMORROW_API_KEY, | |
| } | |
| r = requests.get(url, params=params, timeout=25) | |
| if r.status_code != 200: | |
| raise RuntimeError(f"Tomorrow.io API error: {r.status_code} {r.text}") | |
| data = r.json().get("timelines", {}).get("hourly", []) | |
| if not data: | |
| raise RuntimeError("Tomorrow.io: no hourly data returned") | |
| rows = [] | |
| for p in data[:hours]: | |
| ts = pd.to_datetime(p["time"], utc=True).tz_convert(LOCAL_TZ) | |
| vals = p.get("values", {}) | |
| rain = float(vals.get("rainIntensity", 0.0) or 0.0) | |
| prob = float(vals.get("precipitationProbability", 0.0) or 0.0) | |
| rows.append({"time": ts, "tio_rain": rain, "tio_prob": prob}) | |
| return pd.DataFrame(rows) | |
| # --- Meteomatics fetch --- | |
| def fetch_meteomatics(lat, lon, hours=24): | |
| if not METEOMATICS_USERNAME or not METEOMATICS_PASSWORD: | |
| raise RuntimeError("Missing METEOMATICS credentials") | |
| start = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0) | |
| end = start + timedelta(hours=hours - 1) | |
| timepath = f"{start.isoformat().replace('+00:00','Z')}--{end.isoformat().replace('+00:00','Z')}:PT1H" | |
| url = f"https://api.meteomatics.com/{timepath}/{MM_VAR_RAIN},{MM_VAR_PROB}/{lat},{lon}/json" | |
| params = {"model": DEFAULT_MODEL} | |
| r = requests.get(url, auth=(METEOMATICS_USERNAME, METEOMATICS_PASSWORD), params=params, timeout=25) | |
| if r.status_code != 200: | |
| raise RuntimeError(f"Meteomatics API error: {r.status_code} {r.text}") | |
| js = r.json().get("data", []) | |
| if not js: | |
| raise RuntimeError("Meteomatics: no data returned") | |
| time_map = {} | |
| for entry in js: | |
| var = entry.get("parameter") | |
| for d in entry["coordinates"][0]["dates"]: | |
| ts = pd.to_datetime(d["date"], utc=True).tz_convert(LOCAL_TZ) | |
| val = float(d.get("value", 0.0) or 0.0) | |
| rec = time_map.setdefault(ts, {}) | |
| if var == MM_VAR_RAIN: | |
| rec["mm_rain"] = val | |
| elif var == MM_VAR_PROB: | |
| rec["mm_prob"] = val | |
| df = pd.DataFrame([ | |
| {"time": t, "mm_rain": rec.get("mm_rain", 0.0), "mm_prob": rec.get("mm_prob", 0.0)} | |
| for t, rec in sorted(time_map.items()) | |
| ]) | |
| return df | |
| # --- Combine & table --- | |
| def build_table(lat, lon): | |
| df_tio = fetch_tomorrow(lat, lon, 24) | |
| df_mm = fetch_meteomatics(lat, lon, 24) | |
| df = pd.merge(df_tio, df_mm, on="time", how="outer").sort_values("time") | |
| table = pd.DataFrame({ | |
| "Time (Asia/Kuala_Lumpur)": df["time"].dt.strftime("%Y-%m-%d %H:%M"), | |
| "Tomorrow.io Rain (mm/hr)": df["tio_rain"].round(3), | |
| "Tomorrow.io Prob (%)": df["tio_prob"].round(1), | |
| "Meteomatics Rain (mm/hr)": df["mm_rain"].round(3), | |
| "Meteomatics Prob (%)": df["mm_prob"].round(1), | |
| }) | |
| return table | |
| def run_table(lat, lon): | |
| try: | |
| table = build_table(lat, lon) | |
| info = f"<small>Location: {lat:.5f}, {lon:.5f} | Model: <b>{DEFAULT_MODEL}</b> | Timezone: <b>Asia/Kuala_Lumpur</b> | Units: <b>mm/hr</b> | Horizon: 24 hours</small>" | |
| return table, info | |
| except Exception as e: | |
| return None, f"<p><b>Error:</b> {e}</p>" | |
| # --- Gradio UI --- | |
| with gr.Blocks(title="Next 24 Hours — Tomorrow.io + Meteomatics Table (mm/hr)") as demo: | |
| gr.Markdown("## 🌧️ Next 24 Hours — **Tomorrow.io** & **Meteomatics** (All in mm/hr)\n" | |
| "Side-by-side hourly rain and probability forecasts for the next 24 hours.") | |
| with gr.Row(): | |
| lat = gr.Number(label="Latitude", value=DEFAULT_LAT) | |
| lon = gr.Number(label="Longitude", value=DEFAULT_LON) | |
| btn = gr.Button("Fetch 24h Table") | |
| table_out = gr.Dataframe(interactive=False, wrap=True) | |
| info_out = gr.HTML() | |
| btn.click(run_table, inputs=[lat, lon], outputs=[table_out, info_out]) | |
| # if __name__ == "__main__": | |
| # os.environ["GRADIO_ANALYTICS_ENABLED"]="false" | |
| # os.environ["HF_HUB_DISABLE_TELEMETRY"]="1" | |
| # demo.launch(server_name="127.0.0.1", server_port=7861, show_error=True, inbrowser=False, debug=True) | |
| if __name__ == "__main__": | |
| demo.launch() |