Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| 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" | |
| "¤t_weather=true&timezone=auto" | |
| ) | |
| return requests.get(url, timeout=10).json() | |
| except Exception: | |
| return None | |
| 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() | |