Sugarcane / app.py
Esmaeilkianii's picture
Update app.py
1102865 verified
raw
history blame
22.3 kB
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
import folium
from folium.plugins import HeatMap
from streamlit_folium import folium_static
import json
import os
import ee
import geemap.foliumap as geemap
from datetime import datetime, timedelta
import io
import base64
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle
from reportlab.lib import colors
from sklearn.ensemble import RandomForestRegressor
from streamlit_lottie import st_lottie
import requests
import seaborn as sns
from PIL import Image
import streamlit_authenticator as stauth
import pickle
from pathlib import Path
import time
import statsmodels.api as sm
from statsmodels.tsa.arima.model import ARIMA
# تنظیمات صفحه
st.set_page_config(
page_title="سامانه هوشمند پایش مزارع نیشکر",
page_icon="🌱",
layout="wide",
initial_sidebar_state="expanded"
)
# تنظیمات CSS سفارشی
with open("style.css") as f:
st.markdown(f'<style>{f.read()}</style>', unsafe_allow_html=True)
# تنظیمات فونت فارسی
st.markdown(
"""
<style>
@font-face {
font-family: 'Vazirmatn';
src: url('https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/fonts/webfonts/Vazirmatn-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
}
* {
font-family: 'Vazirmatn', sans-serif;
direction: rtl;
text-align: right;
}
</style>
""",
unsafe_allow_html=True
)
# توابع برای لود انیمیشن‌های Lottie
def load_lottieurl(url):
r = requests.get(url)
if r.status_code != 200:
return None
return r.json()
# بارگذاری انیمیشن‌های Lottie
lottie_farm = load_lottieurl("https://assets4.lottiefiles.com/packages/lf20_qbkoyp6j.json")
lottie_analytics = load_lottieurl("https://assets2.lottiefiles.com/private_files/lf30_ajzyv37m.json")
lottie_report = load_lottieurl("https://assets9.lottiefiles.com/packages/lf20_vfcu9yyj.json")
# اتصال به Google Earth Engine
@st.cache_resource
def initialize_gee():
try:
# مسیر فایل اعتبارسنجی سرویس اکانت
service_account = '[email protected]'
credentials_path = r"ee-esmaeilkiani13877-cfdea6eaf411.json"
credentials = ee.ServiceAccountCredentials(service_account, credentials_path)
ee.Initialize(credentials)
return True
except Exception as e:
st.error(f"خطا در اتصال به Google Earth Engine: {e}")
return False
# بارگذاری و پردازش داده‌ها
@st.cache_data
def load_farm_data():
try:
# بارگذاری داده‌های مزارع
farm_data = pd.read_csv('پایگاه داده (1).csv')
farm_coordinates = pd.read_csv('farm_coordinates.csv')
# پیش‌پردازش و یکپارچه‌سازی داده‌ها
# تبدیل ستون‌های تاریخ به فرمت مناسب و غیره
return farm_data, farm_coordinates
except Exception as e:
st.error(f"خطا در بارگذاری داده‌ها: {e}")
return None, None
# توابع محاسبه شاخص‌های گیاهی
def calculate_ndvi(geometry, date):
"""محاسبه شاخص NDVI برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه NDVI
ndvi = sentinel.normalizedDifference(['B8', 'B4']).rename('NDVI')
# گرفتن آمار NDVI برای منطقه
ndvi_stats = ndvi.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return ndvi, ndvi_stats
def calculate_ndwi(geometry, date):
"""محاسبه شاخص NDWI برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه NDWI (استفاده از باند‌های NIR و SWIR)
ndwi = sentinel.normalizedDifference(['B8', 'B11']).rename('NDWI')
# گرفتن آمار NDWI برای منطقه
ndwi_stats = ndwi.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return ndwi, ndwi_stats
def calculate_evi(geometry, date):
"""محاسبه شاخص EVI برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه EVI
# EVI = 2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))
nir = sentinel.select('B8')
red = sentinel.select('B4')
blue = sentinel.select('B2')
evi = nir.subtract(red).multiply(2.5).divide(
nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)
).rename('EVI')
# گرفتن آمار EVI برای منطقه
evi_stats = evi.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return evi, evi_stats
def calculate_ndmi(geometry, date):
"""محاسبه شاخص NDMI برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه NDMI (استفاده از باند‌های NIR و SWIR)
ndmi = sentinel.normalizedDifference(['B8', 'B11']).rename('NDMI')
# گرفتن آمار NDMI برای منطقه
ndmi_stats = ndmi.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return ndmi, ndmi_stats
def calculate_lai(geometry, date):
"""محاسبه شاخص LAI (سطح برگ) برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه LAI (تقریبی بر اساس NDVI)
ndvi = sentinel.normalizedDifference(['B8', 'B4'])
lai = ndvi.multiply(3.618).exp().subtract(0.118).rename('LAI')
# گرفتن آمار LAI برای منطقه
lai_stats = lai.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return lai, lai_stats
def calculate_chl(geometry, date):
"""محاسبه شاخص کلروفیل (CHL) برای یک منطقه و تاریخ مشخص"""
start_date = (datetime.strptime(date, '%Y-%m-%d') - timedelta(days=5)).strftime('%Y-%m-%d')
end_date = (datetime.strptime(date, '%Y-%m-%d') + timedelta(days=5)).strftime('%Y-%m-%d')
# استفاده از تصاویر Sentinel-2
sentinel = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date, end_date) \
.filterBounds(geometry) \
.sort('CLOUDY_PIXEL_PERCENTAGE') \
.first()
# محاسبه شاخص کلروفیل با استفاده از نسبت باندها
# استفاده از باندهای قرمز لبه (Red Edge) و نزدیک مادون قرمز (NIR)
re1 = sentinel.select('B5') # Red Edge 1
re2 = sentinel.select('B6') # Red Edge 2
chl = re2.divide(re1).rename('CHL')
# گرفتن آمار CHL برای منطقه
chl_stats = chl.reduceRegion(
reducer=ee.Reducer.mean().combine(ee.Reducer.min(), None, True).combine(ee.Reducer.max(), None, True),
geometry=geometry,
scale=10,
maxPixels=1e9
).getInfo()
return chl, chl_stats
# توابع ایجاد نمودارها و نقشه‌ها
def create_variety_pie_chart(farm_data):
"""ایجاد نمودار دایره‌ای برای نمایش توزیع واریته‌های نیشکر"""
variety_counts = farm_data['واریته'].value_counts()
fig = px.pie(
values=variety_counts.values,
names=variety_counts.index,
title="توزیع واریته‌های نیشکر",
color_discrete_sequence=px.colors.sequential.Greens_r
)
fig.update_layout(
legend_title="واریته",
font=dict(family="Vazirmatn"),
hoverlabel=dict(font_family="Vazirmatn")
)
return fig
def create_age_pie_chart(farm_data):
"""ایجاد نمودار دایره‌ای برای نمایش توزیع سن مزارع نیشکر"""
age_counts = farm_data['سن'].value_counts()
fig = px.pie(
values=age_counts.values,
names=age_counts.index,
title="توزیع سن مزارع نیشکر",
color_discrete_sequence=px.colors.sequential.Greens_r
)
fig.update_layout(
legend_title="سن",
font=dict(family="Vazirmatn"),
hoverlabel=dict(font_family="Vazirmatn")
)
return fig
def create_farm_map(farm_coordinates, selected_index=None, index_data=None):
"""ایجاد نقشه تعاملی مزارع با Folium"""
# ایجاد نقشه با مرکزیت میانگین مختصات مزارع
center_lat = farm_coordinates['lat'].mean()
center_lon = farm_coordinates['lon'].mean()
m = folium.Map(location=[center_lat, center_lon], zoom_start=10, control_scale=True)
# اضافه کردن لایه‌های مختلف به نقشه
folium.TileLayer('openstreetmap').add_to(m)
folium.TileLayer('Stamen Terrain').add_to(m)
folium.TileLayer('Stamen Toner').add_to(m)
folium.TileLayer('Stamen Watercolor').add_to(m)
folium.TileLayer('CartoDB positron').add_to(m)
# اگر داده‌های شاخص برای نمایش ارائه شده باشد
if selected_index and index_data:
# اضافه کردن لایه حرارتی بر اساس شاخص انتخاب شده
heat_data = []
for _, row in farm_coordinates.iterrows():
farm_id = row['farm_id']
if farm_id in index_data:
heat_data.append([row['lat'], row['lon'], index_data[farm_id]])
HeatMap(heat_data, radius=15, max_zoom=13, blur=10).add_to(m)
# اضافه کردن نشانگرها برای هر مزرعه
for _, row in farm_coordinates.iterrows():
popup_text = f"""
<div dir="rtl" style="font-family: 'Vazirmatn', sans-serif;">
<b>شناسه مزرعه:</b> {row['farm_id']}<br>
<b>واریته:</b> {row['variety']}<br>
<b>سن:</b> {row['age']} سال<br>
<b>مساحت:</b> {row['area']} هکتار
</div>
"""
folium.Marker(
location=[row['lat'], row['lon']],
popup=folium.Popup(popup_text, max_width=300),
tooltip=f"مزرعه {row['farm_id']}",
icon=folium.Icon(color='green', icon='leaf', prefix='fa')
).add_to(m)
# اضافه کردن کنترل لایه‌ها
folium.LayerControl().add_to(m)
return m
def create_growth_chart(farm_data, farm_id, index_type='NDVI'):
"""ایجاد نمودار خطی برای نمایش روند رشد مزرعه بر اساس شاخص انتخابی"""
# فیلتر داده‌ها برای مزرعه مورد نظر
farm_history = farm_data[farm_data['farm_id'] == farm_id].sort_values('date')
# ایجاد نمودار خطی
fig = px.line(
farm_history,
x='date',
y=index_type,
title=f"روند شاخص {index_type} برای مزرعه {farm_id}",
markers=True
)
fig.update_layout(
xaxis_title="تاریخ",
yaxis_title=f"شاخص {index_type}",
font=dict(family="Vazirmatn"),
hoverlabel=dict(font_family="Vazirmatn")
)
return fig
def create_index_distribution(farm_data, index_type='NDVI'):
"""ایجاد نمودار توزیع شاخص انتخابی برای تمام مزارع"""
fig = px.histogram(
farm_data,
x=index_type,
title=f"توزیع شاخص {index_type} در مزارع",
color_discrete_sequence=['green']
)
fig.update_layout(
xaxis_title=f"شاخص {index_type}",
yaxis_title="تعداد مزارع",
font=dict(family="Vazirmatn"),
hoverlabel=dict(font_family="Vazirmatn")
)
return fig
def create_correlation_heatmap(farm_data):
"""ایجاد نقشه حرارتی همبستگی بین شاخص‌های مختلف"""
# انتخاب ستون‌های شاخص
index_columns = ['NDVI', 'NDWI', 'EVI', 'NDMI', 'LAI', 'CHL']
correlation_data = farm_data[index_columns].corr()
fig = px.imshow(
correlation_data,
text_auto=True,
color_continuous_scale='Viridis',
title="همبستگی بین شاخص‌های گیاهی"
)
fig.update_layout(
font=dict(family="Vazirmatn"),
hoverlabel=dict(font_family="Vazirmatn")
)
return fig
# توابع مربوط به یادگیری ماشین و تحلیل داده
def train_growth_prediction_model(farm_data, target_index='NDVI'):
"""آموزش مدل پیش‌بینی رشد با استفاده از Random Forest"""
# آماده‌سازی داده‌ها
X = farm_data[['سن', 'رطوبت', 'دما', 'بارش']] # ویژگی‌های ورودی
y = farm_data[target_index] # هدف پیش‌بینی
# آموزش مدل
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X, y)
return model
def predict_growth(model, new_data):
"""پیش‌بینی رشد با استفاده از مدل آموزش دیده"""
predictions = model.predict(new_data)
return predictions
def detect_stress(farm_data, ndvi_threshold=0.4, ndwi_threshold=0.2):
"""شناسایی مزارع با تنش آبی یا بیماری بر اساس آستانه‌های شاخص"""
# شناسایی مزارع با NDVI پایین (تنش یا بیماری)
low_ndvi_farms = farm_data[farm_data['NDVI'] < ndvi_threshold]
# شناسایی مزارع با NDWI پایین (تنش آبی)
low_water_farms = farm_data[farm_data['NDWI'] < ndwi_threshold]
return low_ndvi_farms, low_water_farms
# توابع مربوط به گزارش‌گیری
def generate_pdf_report(farm_data, report_type, start_date, end_date):
"""تولید گزارش PDF بر اساس نوع گزارش و بازه زمانی"""
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
elements = []
# افزودن عنوان گزارش
title = f"گزارش {report_type} مزارع نیشکر از {start_date} تا {end_date}"
elements.append(Paragraph(title, getSampleStyleSheet()['Heading1']))
# افزودن جدول داده‌ها
table_data = [list(farm_data.columns)]
for _, row in farm_data.iterrows():
table_data.append(list(row.values))
t = Table(table_data)
t.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.green),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('BOTTOMPADDING', (0, 0), (-1, 0), 12),
('BACKGROUND', (0, 1), (-1, -1), colors.beige),
('GRID', (0, 0), (-1, -1), 1, colors.black)
]))
elements.append(t)
# ساخت PDF
doc.build(elements)
buffer.seek(0)
return buffer
def download_button(object_to_download, download_filename, button_text):
"""ایجاد دکمه دانلود برای فایل"""
b64 = base64.b64encode(object_to_download.getvalue()).decode()
button_uuid = str(uuid.uuid4()).replace('-', '')
button_id = re.sub('\d+', '', button_uuid)
custom_css = f"""
<style>
#{button_id} {{
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-family: 'Vazirmatn', sans-serif;
cursor: pointer;
}}
#{button_id}:hover {{
background-color: #45a049;
}}
</style>
"""
dl_link = custom_css + f'<a download="{download_filename}" id="{button_id}" href="data:application/octet-stream;base64,{b64}">{button_text}</a><br><br>'
return st.markdown(dl_link, unsafe_allow_html=True)
# بخش اصلی برنامه
def main():
# شروع مقدارسازی Google Earth Engine
gee_initialized = initialize_gee()
# بارگذاری داده‌های مزارع
farm_data, farm_coordinates = load_farm_data()
# منوی افقی
st.markdown(
"""
<div class="horizontal-menu">
<a href="#" class="menu-item active" id="dashboard-btn">داشبورد</a>
<a href="#" class="menu-item" id="map-btn">نقشه مزارع</a>
<a href="#" class="menu-item" id="input-btn">ورود اطلاعات</a>
<a href="#" class="menu-item" id="analysis-btn">تحلیل داده‌ها</a>
<a href="#" class="menu-item" id="report-btn">گزارش‌گیری</a>
<a href="#" class="menu-item" id="settings-btn">تنظیمات</a>
</div>
""",
unsafe_allow_html=True
)
# باکس سمت راست برای انتخاب گزینه‌ها
with st.sidebar:
st.markdown("<h2 style='text-align: right;'>سامانه هوشمند پایش مزارع نیشکر</h2>", unsafe_allow_html=True)
st_lottie(lottie_farm, height=200)
st.markdown("<h3 style='text-align: right;'>تنظیمات</h3>", unsafe_allow_html=True)
# انتخاب تاریخ
selected_date = st.date_input(
"تاریخ مورد نظر",
datetime.now().date(),
format="YYYY/MM/DD"
)
# انتخاب مزرعه
if farm_coordinates is not None:
farm_options = farm_coordinates['farm_id'].unique()
selected_farm = st.selectbox("انتخاب مزرعه", farm_options, index=0)
# انتخاب شاخص
index_options = ['NDVI', 'NDWI', 'EVI', 'NDMI', 'LAI', 'CHL']
selected_index = st.selectbox("انتخاب شاخص", index_options, index=0)
# دکمه اعمال تغییرات
apply_btn = st.button("اعمال تغییرات", type="primary")
# نمایش داشبورد (صفحه اصلی)
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"])
with tab1:
st.markdown("<h1 style='text-align: right;'>داشبورد مدیریت مزارع نیشکر</h1>", unsafe_allow_html=True)
# کارت‌های اطلاعات کلیدی
col1, col2, col3, col4 = st.columns(4)
with col1:
st.markdown(
"""
<div class="metric-card green-gradient">
<div class="metric-icon">🌱</div>
<div class="metric-content">
<div class="metric-title">تعداد کل مزارع</div>
<div class="metric-value">125</div>
</div>
</div>
""",
unsafe_allow_html=True
)
with col2:
st.markdown(
"""
<div class="metric-card green-gradient">
<div class="metric-icon">🚜</div>
<div class="metric-content">"""
)