File size: 10,016 Bytes
e45d2b4
 
 
 
fe90331
e45d2b4
 
 
 
fe90331
e45d2b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7c374c3
 
 
 
 
 
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
import os
import random
from typing import Optional, Tuple
import requests
import streamlit as st
from streamlit_folium import st_folium
import folium
from folium.plugins import HeatMap
from datetime import datetime

# ---------------------------
# CONFIG
# ---------------------------
DEFAULT_IMAGE_PATH = "default_farm_image.jpg"  # must exist in same folder

# ---------------------------
# HELPERS
# ---------------------------
def safe_float(val):
    try:
        if val is None or val == "" or str(val).lower() == "nan":
            return None
        return float(val)
    except Exception:
        return None

@st.cache_data(ttl=900)
def get_coordinates(city_name: str) -> Optional[Tuple[float, float]]:
    try:
        url = f"https://geocoding-api.open-meteo.com/v1/search?name={requests.utils.quote(city_name)}"
        res = requests.get(url, timeout=10).json()
        if "results" not in res or not res["results"]:
            return None
        return res["results"][0]["latitude"], res["results"][0]["longitude"]
    except Exception:
        return None

@st.cache_data(ttl=600)
def get_open_meteo(lat: float, lon: float) -> Optional[dict]:
    try:
        url = (
            f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}"
            "&hourly=temperature_2m,relativehumidity_2m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,uv_index_max"
            "&current_weather=true&timezone=auto"
        )
        return requests.get(url, timeout=10).json()
    except Exception:
        return None

@st.cache_data(ttl=600)
def get_wttr(city_name: str) -> dict:
    try:
        url = f"https://wttr.in/{requests.utils.quote(city_name)}?format=j1"
        headers = {"User-Agent": "Mozilla/5.0"}
        res = requests.get(url, timeout=10, headers=headers).json()
        current = res.get('current_condition', [{}])[0]
        return {
            "temperature": safe_float(current.get('temp_C')),
            "windspeed": safe_float(current.get('windspeedKmph')),
            "humidity": safe_float(current.get('humidity')),
            "raw": res,
        }
    except Exception:
        return {"temperature": None, "windspeed": None, "humidity": None}

# ---------------------------
# AGRONOMY LOGIC
# ---------------------------
def get_current_month() -> int:
    return datetime.now().month

def is_crop_in_season(crop: str, current_month: int):
    crop = crop.lower()
    kharif_plant = range(6, 9)
    rabi_plant = range(10, 13)
    if "paddy" in crop or "rice" in crop:
        if current_month in kharif_plant:
            return True, "Kharif season β€” ideal for rice."
        return True, "Secondary Rabi rice possible with irrigation."
    if "wheat" in crop:
        if current_month in rabi_plant:
            return True, "Perfect Rabi season β€” go for wheat now."
        return False, "Not ideal time for wheat planting."
    if "cotton" in crop:
        if current_month in (4,5,6,7):
            return True, "Cotton planting window β€” good time."
        return False, "Not ideal for cotton sowing."
    if "tomato" in crop:
        if current_month in (1,2,3,9,10,11):
            return True, "Good period for tomato cultivation."
        return False, "High temperature/humidity risk for disease."
    if "sugarcane" in crop:
        if current_month in (1,2,3,10,11):
            return True, "Right season for sugarcane."
        return False, "Not ideal for sugarcane."
    return True, "Unknown crop β€” check local extension services."

def recommend_fertilizer(crop: str) -> str:
    crop = crop.lower()
    if "paddy" in crop or "rice" in crop:
        return "NPK 75:30:30 kg/ha β€” split top dressings required."
    if "wheat" in crop:
        return "NPK 100:40:20 kg/ha β€” ensure nitrogen at tillering."
    if "cotton" in crop:
        return "NPK + micronutrients β€” avoid excess nitrogen."
    if "tomato" in crop:
        return "NPK 100:60:100 + calcium spray for BER."
    if "sugarcane" in crop:
        return "Split nitrogen + potassium β€” trench method recommended."
    return "Follow soil test recommendations."

def pest_risk_advice(crop: str, temp_c, humidity):
    if temp_c is None:
        return "Not enough data for pest risk."
    crop = crop.lower()
    msg = []
    if "tomato" in crop and temp_c > 28 and (humidity or 0) > 75:
        msg.append("Blight risk β€” avoid overhead irrigation, use fungicide.")
    if "paddy" in crop and temp_c > 30:
        msg.append("Blast risk β€” monitor neck/leaf spots.")
    if "cotton" in crop and temp_c > 34:
        msg.append("Whitefly risk β€” monitor foliage daily.")
    return " ".join(msg) if msg else "Low pest risk now."

# ---------------------------
# STREAMLIT UI
# ---------------------------
st.set_page_config(page_title="BioSyn 🌿", layout="wide")
st.title("BioSyn β€” Precision Agronomy")

# Fullscreen background
if os.path.exists(DEFAULT_IMAGE_PATH):
    st.markdown(
        f"""
        <style>
        .stApp {{
            background: url("{DEFAULT_IMAGE_PATH}");
            background-size: cover;
            background-position: center;
            background-repeat: no-repeat;
        }}
        .css-18e3th9 {{
            backdrop-filter: blur(6px);
            background: rgba(255,255,255,0.25);
        }}
        h1 {{ color:#fff; text-shadow:0 0 12px black; }}
        </style>
        """, unsafe_allow_html=True
    )

# Sidebar
st.sidebar.header("Controls")
city = st.sidebar.text_input("Enter your city:", value="Madurai")
crops = ["Paddy (Rice)", "Wheat", "Cotton", "Tomato", "Sugarcane", "Other"]
selected_crop = st.sidebar.selectbox("Select your crop", crops)
show_forecast = st.sidebar.checkbox("Show 7-day forecast", value=True)

# Layout
col1, col2 = st.columns([2,1])
open_data = {}
weather_card = {}
temperatures = []
humidity_series = []

coords = get_coordinates(city)
if coords:
    lat, lon = coords
    open_data = get_open_meteo(lat, lon)
    if open_data and "current_weather" in open_data:
        cw = open_data["current_weather"]
        weather_card["temperature"] = safe_float(cw.get("temperature"))
        weather_card["windspeed"] = safe_float(cw.get("windspeed"))
        temperatures = (open_data.get("hourly", {}).get("temperature_2m", []) or [])[-24:]
        humidity_series = (open_data.get("hourly", {}).get("relativehumidity_2m", []) or [])[-24:]
    else:
        wt = get_wttr(city)
        weather_card["temperature"] = wt.get("temperature")
        weather_card["windspeed"] = wt.get("windspeed")
        humidity_series = [wt.get("humidity")]

# ---------------- COLUMN 1 (Map + Heatmap) ----------------
with col1:
    if coords:
        st.markdown("## πŸ—Ί Location")
        st.write(f"πŸ“ **{city}** β€” {lat:.4f}, {lon:.4f}")
        st.markdown("### πŸ”₯ Temperature Heatmap (approx last 24h)")

        # ---------------- Heatmap (persistent points) ----------------
        if "heat_points" not in st.session_state or st.session_state.get("last_city") != city:
            st.session_state.heat_points = []
            st.session_state.last_city = city
            temps = temperatures if temperatures else [weather_card.get("temperature",28)+random.uniform(-2,2) for _ in range(12)]
            for _ in range(50):
                t = random.choice(temps)
                if t is not None:
                    st.session_state.heat_points.append([
                        lat + random.uniform(-0.05,0.05),
                        lon + random.uniform(-0.05,0.05),
                        float(t)
                    ])

        if st.session_state.heat_points:
            m = folium.Map(location=[lat, lon], zoom_start=10)
            HeatMap(st.session_state.heat_points, radius=25, blur=15, min_opacity=0.6).add_to(m)
            st_folium(m, width=700, height=500, key="heatmap")
        else:
            st.info("Heatmap data unavailable.")
    else:
        st.error("Invalid city β€” try correcting spelling.")

# ---------------- COLUMN 2 (Data + Advice) ----------------
with col2:
    st.markdown("## πŸ“Š Current Conditions")
    col_t, col_w = st.columns(2)
    with col_t:
        st.metric("Temperature (Β°C)", weather_card.get("temperature","N/A"))
    with col_w:
        st.metric("Wind Speed (km/h)", weather_card.get("windspeed","N/A"))

    st.markdown("---")
    if show_forecast:
        st.markdown("### πŸ“… 7-Day Forecast")
        if open_data and "daily" in open_data:
            daily = open_data["daily"]
            df = {
                "Date": daily.get("time", []),
                "Max Temp (Β°C)": daily.get("temperature_2m_max", []),
                "Min Temp (Β°C)": daily.get("temperature_2m_min", []),
                "Precipitation (mm)": daily.get("precipitation_sum", []),
                "UV Index": daily.get("uv_index_max", []),
            }
            st.dataframe(df, width='stretch')
        else:
            st.info("Forecast not available.")

    st.markdown("---")
    st.markdown("## 🌾 Agronomy")
    current_month = get_current_month()
    ok, msg = is_crop_in_season(selected_crop, current_month)
    if ok:
        st.success(f"🌱 {selected_crop} can be planted now.")
    else:
        st.warning(f"❌ Not ideal time for {selected_crop}.")
    st.caption(msg)

    temp = weather_card.get("temperature")
    wind = weather_card.get("windspeed")
    hum = humidity_series[0] if humidity_series else None

    if temp and wind and wind < 8 and 18 <= temp <= 35:
        st.success("🧴 Safe to spray pesticides now.")
    elif wind and wind >= 8:
        st.warning("⚠️ Wind too high β€” avoid spraying.")
    elif temp and (temp < 18 or temp > 35):
        st.warning("⚠️ Temperature not ideal for spraying.")
    else:
        st.info("Spray advice unavailable.")

    st.write("**Fertilizer:**", recommend_fertilizer(selected_crop))
    st.write("**Pest Risk:**", pest_risk_advice(selected_crop, temp, hum))

    import streamlit as st

if __name__ == "__main__":
    st.run()