File size: 5,110 Bytes
1d20c3b
 
 
 
be94c44
1d20c3b
be94c44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce62384
be94c44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce62384
1d20c3b
be94c44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ce62384
be94c44
 
 
 
 
 
 
 
ce62384
be94c44
 
 
1d20c3b
be94c44
1d20c3b
53edcd0
 
 
 
8e2d880
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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()