Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,183 +1,231 @@
|
|
1 |
import streamlit as st
|
2 |
import pandas as pd
|
3 |
import numpy as np
|
4 |
-
import
|
5 |
-
|
6 |
-
|
7 |
import requests
|
8 |
import json
|
9 |
-
import
|
10 |
-
import
|
11 |
-
|
12 |
-
|
|
|
|
|
13 |
from reportlab.pdfgen import canvas
|
14 |
from reportlab.lib.pagesizes import letter
|
15 |
-
import
|
16 |
|
|
|
17 |
st.set_page_config(
|
18 |
-
page_title="
|
|
|
19 |
layout="wide",
|
20 |
initial_sidebar_state="expanded"
|
21 |
)
|
22 |
|
23 |
-
# Apply dark theme
|
24 |
-
|
25 |
-
st.markdown("""
|
26 |
<style>
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
</style>
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
ee.
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
|
|
|
48 |
@st.cache_data
|
49 |
def load_farm_data():
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
def
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
'
|
59 |
-
'humidity': response['main']['humidity'],
|
60 |
-
'wind_speed': response['wind']['speed'],
|
61 |
-
'wind_deg': response['wind']['deg'],
|
62 |
-
'weather': response['weather'][0]['main']
|
63 |
-
}
|
64 |
-
|
65 |
-
def fetch_ndvi_data(field_geom, start_date, end_date):
|
66 |
-
collection = ee.ImageCollection('COPERNICUS/S2') \
|
67 |
-
.filterBounds(field_geom) \
|
68 |
-
.filterDate(start_date, end_date)
|
69 |
|
70 |
-
def
|
71 |
ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
|
72 |
return image.addBands(ndvi)
|
73 |
|
74 |
-
ndvi_collection = collection.map(
|
75 |
-
return ndvi_collection
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
-
#
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
st.metric("Age", f"{selected_data['age']} years")
|
89 |
-
st.metric("Variety", selected_data['variety'])
|
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 |
-
template="plotly_dark"
|
119 |
-
)
|
120 |
-
st.plotly_chart(fig, use_container_width=True)
|
121 |
-
|
122 |
-
# NDVI Map
|
123 |
-
st.subheader("Field Health Analysis")
|
124 |
-
field_geom = ee.Geometry.Point([selected_data['longitude'], selected_data['latitude']]).buffer(500)
|
125 |
-
ndvi_data = fetch_ndvi_data(field_geom, str(date_range[0]), str(date_range[1]))
|
126 |
-
mean_ndvi = ndvi_data.mean().getInfo()['bands'][0]['stats']['mean']
|
127 |
-
|
128 |
-
st.pydeck_chart(pdk.Deck(
|
129 |
-
map_style='mapbox://styles/mapbox/dark-v10',
|
130 |
-
initial_view_state=pdk.ViewState(
|
131 |
-
latitude=selected_data['latitude'],
|
132 |
-
longitude=selected_data['longitude'],
|
133 |
-
zoom=12,
|
134 |
-
pitch=50,
|
135 |
-
),
|
136 |
-
layers=[
|
137 |
-
pdk.Layer(
|
138 |
-
'ScatterplotLayer',
|
139 |
-
data=pd.DataFrame({
|
140 |
-
'lat': [selected_data['latitude']],
|
141 |
-
'lon': [selected_data['longitude']],
|
142 |
-
'color': [[0, 255, 0] if mean_ndvi > 0.6 else [255, 255, 0] if mean_ndvi > 0.3 else [255, 0, 0]]
|
143 |
-
}),
|
144 |
-
get_position='[lon, lat]',
|
145 |
-
get_color='color',
|
146 |
-
get_radius=200,
|
147 |
)
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
buffer = io.BytesIO()
|
156 |
c = canvas.Canvas(buffer, pagesize=letter)
|
157 |
-
|
158 |
-
|
159 |
-
c.drawString(100,
|
160 |
-
c.drawString(100,
|
|
|
|
|
161 |
c.save()
|
162 |
-
|
163 |
-
|
164 |
-
if st.button("Generate PDF Report"):
|
165 |
-
pdf = create_pdf_report()
|
166 |
-
st.download_button(
|
167 |
-
label="Download Report",
|
168 |
-
data=pdf,
|
169 |
-
file_name="field_report.pdf",
|
170 |
-
mime="application/pdf"
|
171 |
-
)
|
172 |
-
|
173 |
-
# --------------------------
|
174 |
-
# Hidden API Integration
|
175 |
-
# --------------------------
|
176 |
-
# Windy Map Integration (Hidden branding)
|
177 |
-
st.components.v1.html(f"""
|
178 |
-
<iframe src="https://embed.windy.com/embed2.html?lat={selected_data['latitude']}&lon={selected_data['longitude']}&detailLat=31.534&detailLon=48.724&width=650&height=450&zoom=12&level=surface&overlay=wind&product=ecmwf&menu=&message=&marker=true&calendar=now&pressure=&type=map&location=coordinates&detail=&metricWind=default&metricTemp=default&radarRange=-1"
|
179 |
-
width="100%" height="450" frameborder="0" style="border:0; margin-top:-30px"></iframe>
|
180 |
-
""", height=450)
|
181 |
|
182 |
-
|
183 |
-
|
|
|
1 |
import streamlit as st
|
2 |
import pandas as pd
|
3 |
import numpy as np
|
4 |
+
import folium
|
5 |
+
from streamlit_folium import folium_static
|
6 |
+
import ee
|
7 |
import requests
|
8 |
import json
|
9 |
+
from datetime import datetime, timedelta
|
10 |
+
import plotly.express as px
|
11 |
+
import plotly.graph_objects as go
|
12 |
+
from PIL import Image
|
13 |
+
import io
|
14 |
+
import base64
|
15 |
from reportlab.pdfgen import canvas
|
16 |
from reportlab.lib.pagesizes import letter
|
17 |
+
import os
|
18 |
|
19 |
+
# Page config
|
20 |
st.set_page_config(
|
21 |
+
page_title="مانیتورینگ مزارع نیشکر دهخدا",
|
22 |
+
page_icon="🌾",
|
23 |
layout="wide",
|
24 |
initial_sidebar_state="expanded"
|
25 |
)
|
26 |
|
27 |
+
# Apply custom CSS for dark theme
|
28 |
+
st.markdown("""
|
|
|
29 |
<style>
|
30 |
+
.reportview-container {
|
31 |
+
background: #0e1117;
|
32 |
+
color: #fafafa;
|
33 |
+
}
|
34 |
+
.sidebar .sidebar-content {
|
35 |
+
background: #262730;
|
36 |
+
}
|
37 |
+
.Widget>label {
|
38 |
+
color: #fafafa;
|
39 |
+
}
|
40 |
+
.stButton>button {
|
41 |
+
background-color: #4CAF50;
|
42 |
+
color: white;
|
43 |
+
}
|
44 |
+
.stProgress .st-bo {
|
45 |
+
background-color: #4CAF50;
|
46 |
+
}
|
47 |
</style>
|
48 |
+
""", unsafe_allow_html=True)
|
49 |
+
|
50 |
+
# Initialize Earth Engine
|
51 |
+
def initialize_ee():
|
52 |
+
try:
|
53 |
+
ee.Initialize()
|
54 |
+
except:
|
55 |
+
credentials = ee.ServiceAccountCredentials(
|
56 |
+
'esmaeil-kiani1387-gmail-com@ee-esmaeilkiani13877.iam.gserviceaccount.com',
|
57 |
+
'ee-esmaeilkiani13877-9a054809a4bb.json'
|
58 |
+
)
|
59 |
+
ee.Initialize(credentials)
|
60 |
|
61 |
+
# Load farm data
|
62 |
@st.cache_data
|
63 |
def load_farm_data():
|
64 |
+
df = pd.read_csv('tableConvert.com_wftamx (1).csv')
|
65 |
+
return df
|
66 |
+
|
67 |
+
# NDVI calculation function
|
68 |
+
def calculate_ndvi(farm_geometry, start_date, end_date):
|
69 |
+
collection = ee.ImageCollection('COPERNICUS/S2_SR') \
|
70 |
+
.filterBounds(farm_geometry) \
|
71 |
+
.filterDate(start_date, end_date) \
|
72 |
+
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
+
def addNDVI(image):
|
75 |
ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
|
76 |
return image.addBands(ndvi)
|
77 |
|
78 |
+
ndvi_collection = collection.map(addNDVI)
|
79 |
+
return ndvi_collection
|
80 |
+
|
81 |
+
# Weather data fetching
|
82 |
+
def get_weather_data(lat, lon, api_key):
|
83 |
+
base_url = "http://api.openweathermap.org/data/2.5/weather"
|
84 |
+
params = {
|
85 |
+
"lat": lat,
|
86 |
+
"lon": lon,
|
87 |
+
"appid": api_key,
|
88 |
+
"units": "metric"
|
89 |
+
}
|
90 |
+
response = requests.get(base_url, params=params)
|
91 |
+
return response.json()
|
92 |
+
|
93 |
+
# Windy.com API integration
|
94 |
+
def get_windy_forecast(lat, lon, api_key):
|
95 |
+
base_url = "https://api.windy.com/api/point-forecast/v2"
|
96 |
+
headers = {
|
97 |
+
"Authorization": f"Bearer {api_key}"
|
98 |
+
}
|
99 |
+
params = {
|
100 |
+
"lat": lat,
|
101 |
+
"lon": lon,
|
102 |
+
"model": "gfs",
|
103 |
+
"parameters": ["wind", "temp", "humidity"]
|
104 |
+
}
|
105 |
+
response = requests.get(base_url, headers=headers, params=params)
|
106 |
+
return response.json()
|
107 |
|
108 |
+
# Main function
|
109 |
+
def main():
|
110 |
+
# Initialize Earth Engine
|
111 |
+
initialize_ee()
|
|
|
|
|
112 |
|
113 |
+
# Load farm data
|
114 |
+
farms_df = load_farm_data()
|
115 |
+
|
116 |
+
# Sidebar
|
117 |
+
st.sidebar.title("مدیریت مزارع")
|
118 |
+
|
119 |
+
# Farm search
|
120 |
+
search_query = st.sidebar.text_input("جستجوی مزرعه", "")
|
121 |
+
filtered_farms = farms_df[farms_df['name'].str.contains(search_query, case=False, na=False)]
|
122 |
+
|
123 |
+
# Farm selection
|
124 |
+
selected_farm = st.sidebar.selectbox(
|
125 |
+
"انتخاب مزرعه",
|
126 |
+
filtered_farms['name'].tolist()
|
127 |
+
)
|
128 |
+
|
129 |
+
# Main content area
|
130 |
+
col1, col2 = st.columns([2, 1])
|
131 |
+
|
132 |
+
with col1:
|
133 |
+
st.title(f"داشبورد مزرعه {selected_farm}")
|
134 |
+
|
135 |
+
# Map
|
136 |
+
farm_data = farms_df[farms_df['name'] == selected_farm].iloc[0]
|
137 |
+
m = folium.Map(
|
138 |
+
location=[farm_data['latitude'], farm_data['longitude']],
|
139 |
+
zoom_start=15
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
)
|
141 |
+
|
142 |
+
# Add farm marker
|
143 |
+
folium.Marker(
|
144 |
+
[farm_data['latitude'], farm_data['longitude']],
|
145 |
+
popup=f"مزرعه {selected_farm}\nسن: {farm_data['age']}\nواریته: {farm_data['variety']}"
|
146 |
+
).add_to(m)
|
147 |
+
|
148 |
+
folium_static(m)
|
149 |
+
|
150 |
+
# NDVI Analysis
|
151 |
+
st.subheader("تحلیل NDVI")
|
152 |
+
date_range = st.date_input(
|
153 |
+
"بازه زمانی",
|
154 |
+
[datetime.now() - timedelta(days=30), datetime.now()]
|
155 |
+
)
|
156 |
+
|
157 |
+
if len(date_range) == 2:
|
158 |
+
farm_geometry = ee.Geometry.Point([farm_data['longitude'], farm_data['latitude']])
|
159 |
+
ndvi_collection = calculate_ndvi(farm_geometry, date_range[0], date_range[1])
|
160 |
+
|
161 |
+
# Plot NDVI time series
|
162 |
+
ndvi_values = ndvi_collection.aggregate_array('NDVI').getInfo()
|
163 |
+
dates = ndvi_collection.aggregate_array('system:time_start').getInfo()
|
164 |
+
|
165 |
+
fig = px.line(
|
166 |
+
x=[datetime.fromtimestamp(d/1000) for d in dates],
|
167 |
+
y=ndvi_values,
|
168 |
+
title="NDVI Time Series",
|
169 |
+
labels={"x": "Date", "y": "NDVI"}
|
170 |
+
)
|
171 |
+
st.plotly_chart(fig)
|
172 |
+
|
173 |
+
with col2:
|
174 |
+
st.subheader("اطلاعات آب و هوا")
|
175 |
+
|
176 |
+
# Weather data
|
177 |
+
weather_data = get_weather_data(
|
178 |
+
farm_data['latitude'],
|
179 |
+
farm_data['longitude'],
|
180 |
+
"ed47316a45379e2221a75f813229fb46"
|
181 |
+
)
|
182 |
+
|
183 |
+
# Display current weather
|
184 |
+
st.metric(
|
185 |
+
"دمای فعلی",
|
186 |
+
f"{weather_data['main']['temp']}°C",
|
187 |
+
f"{weather_data['main']['temp_max'] - weather_data['main']['temp_min']}°C"
|
188 |
+
)
|
189 |
+
|
190 |
+
st.metric(
|
191 |
+
"رطوبت",
|
192 |
+
f"{weather_data['main']['humidity']}%"
|
193 |
+
)
|
194 |
+
|
195 |
+
# Wind data from Windy.com
|
196 |
+
windy_data = get_windy_forecast(
|
197 |
+
farm_data['latitude'],
|
198 |
+
farm_data['longitude'],
|
199 |
+
"ohZom14Dr9JPKOaeUoY8nBxKdiK2yURW"
|
200 |
+
)
|
201 |
+
|
202 |
+
# AI Recommendations
|
203 |
+
st.subheader("توصیههای هوشمند")
|
204 |
+
if farm_data['age'] > 8:
|
205 |
+
st.warning("زمان برداشت نزدیک است")
|
206 |
+
|
207 |
+
# Generate PDF Report
|
208 |
+
if st.button("دانلود گزارش"):
|
209 |
+
pdf_buffer = generate_report(selected_farm, farm_data, weather_data, ndvi_values)
|
210 |
+
st.download_button(
|
211 |
+
"دانلود PDF",
|
212 |
+
pdf_buffer,
|
213 |
+
file_name=f"farm_report_{selected_farm}.pdf",
|
214 |
+
mime="application/pdf"
|
215 |
+
)
|
216 |
+
|
217 |
+
def generate_report(farm_name, farm_data, weather_data, ndvi_values):
|
218 |
buffer = io.BytesIO()
|
219 |
c = canvas.Canvas(buffer, pagesize=letter)
|
220 |
+
|
221 |
+
# Add report content
|
222 |
+
c.drawString(100, 750, f"گزارش مزرعه {farm_name}")
|
223 |
+
c.drawString(100, 700, f"سن مزرعه: {farm_data['age']}")
|
224 |
+
c.drawString(100, 650, f"واریته: {farm_data['variety']}")
|
225 |
+
|
226 |
c.save()
|
227 |
+
buffer.seek(0)
|
228 |
+
return buffer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
229 |
|
230 |
+
if __name__ == "__main__":
|
231 |
+
main()
|