AlphaHacker1729 commited on
Commit
e45d2b4
Β·
verified Β·
1 Parent(s): 1fd84c6

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +258 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,260 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
 
4
  import streamlit as st
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import random
3
+ from typing import Optional, Tuple
4
+ import requests
5
  import streamlit as st
6
+ from streamlit_folium import st_folium
7
+ import folium
8
+ from folium.plugins import HeatMap
9
+ from datetime import datetime
10
 
11
+ # ---------------------------
12
+ # CONFIG
13
+ # ---------------------------
14
+ DEFAULT_IMAGE_PATH = "default_farm_image.jpg" # must exist in same folder
15
+
16
+ # ---------------------------
17
+ # HELPERS
18
+ # ---------------------------
19
+ def safe_float(val):
20
+ try:
21
+ if val is None or val == "" or str(val).lower() == "nan":
22
+ return None
23
+ return float(val)
24
+ except Exception:
25
+ return None
26
+
27
+ @st.cache_data(ttl=900)
28
+ def get_coordinates(city_name: str) -> Optional[Tuple[float, float]]:
29
+ try:
30
+ url = f"https://geocoding-api.open-meteo.com/v1/search?name={requests.utils.quote(city_name)}"
31
+ res = requests.get(url, timeout=10).json()
32
+ if "results" not in res or not res["results"]:
33
+ return None
34
+ return res["results"][0]["latitude"], res["results"][0]["longitude"]
35
+ except Exception:
36
+ return None
37
+
38
+ @st.cache_data(ttl=600)
39
+ def get_open_meteo(lat: float, lon: float) -> Optional[dict]:
40
+ try:
41
+ url = (
42
+ f"https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lon}"
43
+ "&hourly=temperature_2m,relativehumidity_2m&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,uv_index_max"
44
+ "&current_weather=true&timezone=auto"
45
+ )
46
+ return requests.get(url, timeout=10).json()
47
+ except Exception:
48
+ return None
49
+
50
+ @st.cache_data(ttl=600)
51
+ def get_wttr(city_name: str) -> dict:
52
+ try:
53
+ url = f"https://wttr.in/{requests.utils.quote(city_name)}?format=j1"
54
+ headers = {"User-Agent": "Mozilla/5.0"}
55
+ res = requests.get(url, timeout=10, headers=headers).json()
56
+ current = res.get('current_condition', [{}])[0]
57
+ return {
58
+ "temperature": safe_float(current.get('temp_C')),
59
+ "windspeed": safe_float(current.get('windspeedKmph')),
60
+ "humidity": safe_float(current.get('humidity')),
61
+ "raw": res,
62
+ }
63
+ except Exception:
64
+ return {"temperature": None, "windspeed": None, "humidity": None}
65
+
66
+ # ---------------------------
67
+ # AGRONOMY LOGIC
68
+ # ---------------------------
69
+ def get_current_month() -> int:
70
+ return datetime.now().month
71
+
72
+ def is_crop_in_season(crop: str, current_month: int):
73
+ crop = crop.lower()
74
+ kharif_plant = range(6, 9)
75
+ rabi_plant = range(10, 13)
76
+ if "paddy" in crop or "rice" in crop:
77
+ if current_month in kharif_plant:
78
+ return True, "Kharif season β€” ideal for rice."
79
+ return True, "Secondary Rabi rice possible with irrigation."
80
+ if "wheat" in crop:
81
+ if current_month in rabi_plant:
82
+ return True, "Perfect Rabi season β€” go for wheat now."
83
+ return False, "Not ideal time for wheat planting."
84
+ if "cotton" in crop:
85
+ if current_month in (4,5,6,7):
86
+ return True, "Cotton planting window β€” good time."
87
+ return False, "Not ideal for cotton sowing."
88
+ if "tomato" in crop:
89
+ if current_month in (1,2,3,9,10,11):
90
+ return True, "Good period for tomato cultivation."
91
+ return False, "High temperature/humidity risk for disease."
92
+ if "sugarcane" in crop:
93
+ if current_month in (1,2,3,10,11):
94
+ return True, "Right season for sugarcane."
95
+ return False, "Not ideal for sugarcane."
96
+ return True, "Unknown crop β€” check local extension services."
97
+
98
+ def recommend_fertilizer(crop: str) -> str:
99
+ crop = crop.lower()
100
+ if "paddy" in crop or "rice" in crop:
101
+ return "NPK 75:30:30 kg/ha β€” split top dressings required."
102
+ if "wheat" in crop:
103
+ return "NPK 100:40:20 kg/ha β€” ensure nitrogen at tillering."
104
+ if "cotton" in crop:
105
+ return "NPK + micronutrients β€” avoid excess nitrogen."
106
+ if "tomato" in crop:
107
+ return "NPK 100:60:100 + calcium spray for BER."
108
+ if "sugarcane" in crop:
109
+ return "Split nitrogen + potassium β€” trench method recommended."
110
+ return "Follow soil test recommendations."
111
+
112
+ def pest_risk_advice(crop: str, temp_c, humidity):
113
+ if temp_c is None:
114
+ return "Not enough data for pest risk."
115
+ crop = crop.lower()
116
+ msg = []
117
+ if "tomato" in crop and temp_c > 28 and (humidity or 0) > 75:
118
+ msg.append("Blight risk β€” avoid overhead irrigation, use fungicide.")
119
+ if "paddy" in crop and temp_c > 30:
120
+ msg.append("Blast risk β€” monitor neck/leaf spots.")
121
+ if "cotton" in crop and temp_c > 34:
122
+ msg.append("Whitefly risk β€” monitor foliage daily.")
123
+ return " ".join(msg) if msg else "Low pest risk now."
124
+
125
+ # ---------------------------
126
+ # STREAMLIT UI
127
+ # ---------------------------
128
+ st.set_page_config(page_title="BioSyn 🌿", layout="wide")
129
+ st.title("BioSyn β€” Precision Agronomy")
130
+
131
+ # Fullscreen background
132
+ if os.path.exists(DEFAULT_IMAGE_PATH):
133
+ st.markdown(
134
+ f"""
135
+ <style>
136
+ .stApp {{
137
+ background: url("{DEFAULT_IMAGE_PATH}");
138
+ background-size: cover;
139
+ background-position: center;
140
+ background-repeat: no-repeat;
141
+ }}
142
+ .css-18e3th9 {{
143
+ backdrop-filter: blur(6px);
144
+ background: rgba(255,255,255,0.25);
145
+ }}
146
+ h1 {{ color:#fff; text-shadow:0 0 12px black; }}
147
+ </style>
148
+ """, unsafe_allow_html=True
149
+ )
150
+
151
+ # Sidebar
152
+ st.sidebar.header("Controls")
153
+ city = st.sidebar.text_input("Enter your city:", value="Madurai")
154
+ crops = ["Paddy (Rice)", "Wheat", "Cotton", "Tomato", "Sugarcane", "Other"]
155
+ selected_crop = st.sidebar.selectbox("Select your crop", crops)
156
+ show_forecast = st.sidebar.checkbox("Show 7-day forecast", value=True)
157
+
158
+ # Layout
159
+ col1, col2 = st.columns([2,1])
160
+ open_data = {}
161
+ weather_card = {}
162
+ temperatures = []
163
+ humidity_series = []
164
+
165
+ coords = get_coordinates(city)
166
+ if coords:
167
+ lat, lon = coords
168
+ open_data = get_open_meteo(lat, lon)
169
+ if open_data and "current_weather" in open_data:
170
+ cw = open_data["current_weather"]
171
+ weather_card["temperature"] = safe_float(cw.get("temperature"))
172
+ weather_card["windspeed"] = safe_float(cw.get("windspeed"))
173
+ temperatures = (open_data.get("hourly", {}).get("temperature_2m", []) or [])[-24:]
174
+ humidity_series = (open_data.get("hourly", {}).get("relativehumidity_2m", []) or [])[-24:]
175
+ else:
176
+ wt = get_wttr(city)
177
+ weather_card["temperature"] = wt.get("temperature")
178
+ weather_card["windspeed"] = wt.get("windspeed")
179
+ humidity_series = [wt.get("humidity")]
180
+
181
+ # ---------------- COLUMN 1 (Map + Heatmap) ----------------
182
+ with col1:
183
+ if coords:
184
+ st.markdown("## πŸ—Ί Location")
185
+ st.write(f"πŸ“ **{city}** β€” {lat:.4f}, {lon:.4f}")
186
+ st.markdown("### πŸ”₯ Temperature Heatmap (approx last 24h)")
187
+
188
+ # ---------------- Heatmap (persistent points) ----------------
189
+ if "heat_points" not in st.session_state or st.session_state.get("last_city") != city:
190
+ st.session_state.heat_points = []
191
+ st.session_state.last_city = city
192
+ temps = temperatures if temperatures else [weather_card.get("temperature",28)+random.uniform(-2,2) for _ in range(12)]
193
+ for _ in range(50):
194
+ t = random.choice(temps)
195
+ if t is not None:
196
+ st.session_state.heat_points.append([
197
+ lat + random.uniform(-0.05,0.05),
198
+ lon + random.uniform(-0.05,0.05),
199
+ float(t)
200
+ ])
201
+
202
+ if st.session_state.heat_points:
203
+ m = folium.Map(location=[lat, lon], zoom_start=10)
204
+ HeatMap(st.session_state.heat_points, radius=25, blur=15, min_opacity=0.6).add_to(m)
205
+ st_folium(m, width=700, height=500, key="heatmap")
206
+ else:
207
+ st.info("Heatmap data unavailable.")
208
+ else:
209
+ st.error("Invalid city β€” try correcting spelling.")
210
+
211
+ # ---------------- COLUMN 2 (Data + Advice) ----------------
212
+ with col2:
213
+ st.markdown("## πŸ“Š Current Conditions")
214
+ col_t, col_w = st.columns(2)
215
+ with col_t:
216
+ st.metric("Temperature (Β°C)", weather_card.get("temperature","N/A"))
217
+ with col_w:
218
+ st.metric("Wind Speed (km/h)", weather_card.get("windspeed","N/A"))
219
+
220
+ st.markdown("---")
221
+ if show_forecast:
222
+ st.markdown("### πŸ“… 7-Day Forecast")
223
+ if open_data and "daily" in open_data:
224
+ daily = open_data["daily"]
225
+ df = {
226
+ "Date": daily.get("time", []),
227
+ "Max Temp (Β°C)": daily.get("temperature_2m_max", []),
228
+ "Min Temp (Β°C)": daily.get("temperature_2m_min", []),
229
+ "Precipitation (mm)": daily.get("precipitation_sum", []),
230
+ "UV Index": daily.get("uv_index_max", []),
231
+ }
232
+ st.dataframe(df, width='stretch')
233
+ else:
234
+ st.info("Forecast not available.")
235
+
236
+ st.markdown("---")
237
+ st.markdown("## 🌾 Agronomy")
238
+ current_month = get_current_month()
239
+ ok, msg = is_crop_in_season(selected_crop, current_month)
240
+ if ok:
241
+ st.success(f"🌱 {selected_crop} can be planted now.")
242
+ else:
243
+ st.warning(f"❌ Not ideal time for {selected_crop}.")
244
+ st.caption(msg)
245
+
246
+ temp = weather_card.get("temperature")
247
+ wind = weather_card.get("windspeed")
248
+ hum = humidity_series[0] if humidity_series else None
249
+
250
+ if temp and wind and wind < 8 and 18 <= temp <= 35:
251
+ st.success("🧴 Safe to spray pesticides now.")
252
+ elif wind and wind >= 8:
253
+ st.warning("⚠️ Wind too high β€” avoid spraying.")
254
+ elif temp and (temp < 18 or temp > 35):
255
+ st.warning("⚠️ Temperature not ideal for spraying.")
256
+ else:
257
+ st.info("Spray advice unavailable.")
258
+
259
+ st.write("**Fertilizer:**", recommend_fertilizer(selected_crop))
260
+ st.write("**Pest Risk:**", pest_risk_advice(selected_crop, temp, hum))