diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,633 +1,39 @@
import streamlit as st
+import ee
+import folium
import pandas as pd
import numpy as np
-import folium
-from streamlit_folium import folium_static
-import ee
-import os
+import datetime
+import requests
import json
-import time
-from datetime import datetime, timedelta
+import os
+from streamlit_folium import folium_static
+import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
-from PIL import Image
-import base64
-from io import BytesIO
-import matplotlib.pyplot as plt
-import seaborn as sns
-import altair as alt
-from streamlit_option_menu import option_menu
-from streamlit_lottie import st_lottie
-import requests
-import hydralit_components as hc
-from streamlit_extras.colored_header import colored_header
-from streamlit_extras.metric_cards import style_metric_cards
-from streamlit_extras.chart_container import chart_container
-from streamlit_extras.add_vertical_space import add_vertical_space
-from streamlit_card import card
-import pydeck as pdk
-import math
-from sklearn.linear_model import LinearRegression
+from datetime import datetime, timedelta
-# Page configuration with custom theme
+# Set page configuration
st.set_page_config(
- page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
- page_icon="🌱",
+ page_title="Sugarcane Monitoring System",
+ page_icon="🌿",
layout="wide",
initial_sidebar_state="expanded"
)
-# Custom CSS with modern, stunning design
-st.markdown("""
-
-""", unsafe_allow_html=True)
-
-# Load real farm data from CSV
-@st.cache_data
-def load_farm_data():
- try:
- df = pd.read_csv("کراپ لاگ کلی (1).csv")
- # Print columns for verification
- st.write("ستونهای موجود در فایل:", df.columns.tolist())
- # Rename columns for consistency with the program
- df.rename(columns={
- 'سال': 'Year',
- 'هفته': 'Week',
- 'مزرعه': 'Farm_ID',
- 'کانال': 'Channel',
- 'اداره': 'Administration',
- 'مساحت': 'Area',
- 'مساحت زیر مجموعه': 'SubArea',
- 'رقم': 'Variety',
- 'سن': 'Age',
- 'ایستگاه 1': 'Station1',
- 'ایستگاه 2': 'Station2',
- 'ایستگاه 3': 'Station3',
- 'ایستگاه 4': 'Station4',
- 'ایستگاه 5': 'Station5',
- 'ارتفاع هفته جاری مزرعه': 'CurrentHeight',
- 'ارتفاع هفته گذشته مزرعه': 'PreviousHeight',
- 'رشد هفته جاری': 'CurrentGrowth',
- 'رشد هفته گذشته': 'PreviousGrowth',
- 'نیتروژن فعلی': 'CurrentNitrogen',
- 'نیتروژن استاندارد فعلی': 'StandardNitrogen',
- 'نیتروژن قبلی': 'PreviousNitrogen',
- 'نیتروژن استاندارد قبلی': 'PreviousStandardNitrogen',
- 'رطوبت': 'CurrentMoisture', # بهجای رطوبت غلاف فعلی
- 'رطوبت استاندارد فعلی': 'StandardMoisture',
- 'رطوبت': 'PreviousMoisture', # بهجای رطوبت غلاف قبلی (فرض میکنم در یک ستون تکراری باشد)
- 'رطوبت استاندارد قبلی': 'PreviousStandardMoisture',
- 'چاهک 1': 'Well1',
- 'تاریخ قرائت': 'Well1Date',
- 'چاهک 2': 'Well2',
- 'تاریخ قرائت.1': 'Well2Date'
- }, inplace=True, errors='ignore')
- return df
- except Exception as e:
- st.error(f"خطا در بارگذاری دادههای مزارع: {e}")
- return pd.DataFrame()
-
-@st.cache_data
-def load_coordinates_data():
- try:
- coords_df = pd.read_csv("farm_coordinates.csv")
- coords_df.rename(columns={
- 'مزرعه': 'Farm_ID',
- 'عرض جغرافیایی': 'Latitude',
- 'طول جغرافیایی': 'Longitude'
- }, inplace=True, errors='ignore')
- return coords_df
- except Exception as e:
- st.error(f"خطا در بارگذاری دادههای مختصات: {e}")
- return pd.DataFrame()
+# App title and description
+st.title("Sugarcane Monitoring System")
+st.markdown("### Monitoring sugarcane farms in Khuzestan, Iran")
-@st.cache_data
-def load_day_data():
- try:
- day_df = pd.read_csv("پایگاه داده (1).csv")
- day_df.rename(columns={
- 'سال': 'Year', # فرض شده برای سازگاری
- 'هفته': 'Week', # فرض شده برای سازگاری
- 'مزرعه': 'Farm_ID',
- 'روز': 'Day'
- }, inplace=True, errors='ignore')
- return day_df
- except Exception as e:
- st.error(f"خطا در بارگذاری دادههای روزهای هفته: {e}")
- return pd.DataFrame()
-
-# Load animation JSON
-@st.cache_data
-def load_lottie_url(url: str):
- r = requests.get(url)
- if r.status_code != 200:
- return None
- return r.json()
-
-# Initialize Earth Engine
+# Load service account credentials for Earth Engine
@st.cache_resource
-def initialize_earth_engine():
- try:
- service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com'
- credentials_dict = {
+def initialize_ee():
+ service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com'
+
+ # Create a temporary credentials file
+ credentials_path = 'credentials.json'
+ with open(credentials_path, 'w') as f:
+ f.write(json.dumps({
"type": "service_account",
"project_id": "ee-esmaeilkiani13877",
"private_key_id": "cfdea6eaf4115cb6462626743e4b15df85fd0c7f",
@@ -639,1249 +45,736 @@ def initialize_earth_engine():
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
- }
- credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json'
- with open(credentials_file, 'w') as f:
- json.dump(credentials_dict, f)
- credentials = ee.ServiceAccountCredentials(service_account, credentials_file)
- ee.Initialize(credentials)
- os.remove(credentials_file)
- return True
- except Exception as e:
- st.error(f"خطا در اتصال به Earth Engine: {e}")
- return False
-
-# Create Earth Engine map with indices
-def create_ee_map(farm_id, date_str, layer_type="NDVI"):
- try:
- farm_row = coordinates_df[coordinates_df['Farm_ID'] == farm_id].iloc[0]
- lat, lon = farm_row['Latitude'], farm_row['Longitude']
- m = folium.Map(location=[lat, lon], zoom_start=14, tiles='CartoDB positron')
- date_obj = datetime.strptime(date_str, '%Y-%m-%d')
- start_date = (date_obj - timedelta(days=5)).strftime('%Y-%m-%d')
- end_date = (date_obj + timedelta(days=5)).strftime('%Y-%m-%d')
- region = ee.Geometry.Point([lon, lat]).buffer(1500)
- s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
- .filterDate(start_date, end_date) \
- .filterBounds(region) \
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
- .first()
- if layer_type == "NDVI":
- index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
- viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
- legend_title = 'شاخص پوشش گیاهی (NDVI)'
- elif layer_type == "NDMI":
- index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
- viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
- legend_title = 'شاخص رطوبت (NDMI)'
- elif layer_type == "EVI":
- nir = s2.select('B8')
- red = s2.select('B4')
- blue = s2.select('B2')
- index = nir.subtract(red).multiply(2.5).divide(nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)).rename('EVI')
- viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
- legend_title = 'شاخص پیشرفته گیاهی (EVI)'
- elif layer_type == "NDWI":
- index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
- viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
- legend_title = 'شاخص آب (NDWI)'
- map_id_dict = ee.Image(index).getMapId(viz_params)
- folium.TileLayer(
- tiles=map_id_dict['tile_fetcher'].url_format,
- attr='Google Earth Engine',
- name=layer_type,
- overlay=True,
- control=True
- ).add_to(m)
- folium.Marker(
- [lat, lon],
- popup=f'مزرعه {farm_id}',
- tooltip=f'مزرعه {farm_id}',
- icon=folium.Icon(color='green', icon='leaf', prefix='fa')
- ).add_to(m)
- folium.Circle(
- [lat, lon],
- radius=1500,
- color='green',
- fill=True,
- fill_color='green',
- fill_opacity=0.1,
- weight=2
- ).add_to(m)
- folium.LayerControl().add_to(m)
- legend_html = '''
-
-
''' + legend_title + '''
-
-
-
-
- '''
- m.get_root().html.add_child(folium.Element(legend_html))
- return m
- except Exception as e:
- st.error(f"خطا در ایجاد نقشه: {e}")
- return None
-
-# Calculate real farm stats
-def calculate_farm_stats(farm_id, layer_type="NDVI"):
- farm_data = farm_df[farm_df['Farm_ID'] == farm_id]
- if layer_type == "NDVI":
- stats = {
- 'mean': farm_data['ارتفاع هفته جاری مزرعه'].mean() if not farm_data.empty else 0,
- 'min': farm_data['ارتفاع هفته جاری مزرعه'].min() if not farm_data.empty else 0,
- 'max': farm_data['ارتفاع هفته جاری مزرعه'].max() if not farm_data.empty else 0,
- 'std_dev': farm_data['ارتفاع هفته جاری مزرعه'].std() if not farm_data.empty else 0,
- 'histogram_data': farm_data['ارتفاع هفته جاری مزرعه'].values if not farm_data.empty else np.array([])
- }
- elif layer_type == "NDMI":
- stats = {
- 'mean': farm_data['CurrentMoisture'].mean() if not farm_data.empty else 0,
- 'min': farm_data['CurrentMoisture'].min() if not farm_data.empty else 0,
- 'max': farm_data['CurrentMoisture'].max() if not farm_data.empty else 0,
- 'std_dev': farm_data['CurrentMoisture'].std() if not farm_data.empty else 0,
- 'histogram_data': farm_data['CurrentMoisture'].values if not farm_data.empty else np.array([])
- }
- return stats
+ }))
+ )
+
+ # Authenticate and initialize
+ credentials = ee.ServiceAccountCredentials(service_account, credentials_path)
+ ee.Initialize(credentials)
+
+ # Remove the temporary file after initialization
+ os.remove(credentials_path)
+
+ return True
-# Generate real growth data
-def generate_real_growth_data(selected_variety="all", selected_age="all"):
- filtered_farms = farm_df
- if selected_variety != "all":
- filtered_farms = filtered_farms[filtered_farms['Variety'] == selected_variety]
- if selected_age != "all":
- filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age]
+# Download CSV data from URLs
+@st.cache_data
+def load_farm_data():
+ farm_coords_url = "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/farm_coordinates-TTksVyH860XeyfKUCGMMkq9pYMChZj.csv"
+ farm_db_url = "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87%20%D8%AF%D8%A7%D8%AF%D9%87%20%281%29-5Aq8TzJrbK3y5AtUVjrU0bwZD1SUHL.csv"
- farm_growth_data = []
- weeks = filtered_farms['Week'].unique()
- for farm_id in filtered_farms['Farm_ID'].unique():
- farm_data = filtered_farms[filtered_farms['Farm_ID'] == farm_id]
- growth_data = {
- 'farm_id': farm_id,
- 'variety': farm_data['Variety'].iloc[0] if not farm_data.empty else 'Unknown',
- 'age': farm_data['Age'].iloc[0] if not farm_data.empty else 'Unknown',
- 'weeks': weeks,
- 'heights': [farm_data[farm_data['Week'] == week]['ارتفاع هفته جاری مزرعه'].mean() if not farm_data[farm_data['Week'] == week].empty else 0 for week in weeks]
- }
- farm_growth_data.append(growth_data)
+ # Load farm coordinates
+ farm_coords = pd.read_csv(farm_coords_url)
+
+ # Load farm database
+ farm_db = pd.read_csv(farm_db_url)
+
+ return farm_coords, farm_db
+
+# Process satellite imagery using GEE
+@st.cache_data(ttl=3600)
+def get_satellite_indices(lat, lon, date_range):
+ # Define point of interest
+ poi = ee.Geometry.Point([lon, lat])
+
+ # Define region around the point
+ region = poi.buffer(500) # 500m buffer around the point
+
+ # Load Sentinel-2 collection
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
+ .filterBounds(region) \
+ .filterDate(date_range[0], date_range[1]) \
+ .sort('CLOUDY_PIXEL_PERCENTAGE') \
+ .first()
+
+ if s2 is None:
+ return None, None, None, None
+
+ # Calculate indices
+ ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
+ ndwi = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
+
+ # LAI calculation (Leaf Area Index)
+ # Using simplified model: LAI = 3.618 * NDVI - 0.118
+ lai = ndvi.multiply(3.618).subtract(0.118).rename('LAI')
+
+ # Chlorophyll content (CHL)
+ # Using ratio of bands B8/B5
+ chl = s2.select('B8').divide(s2.select('B5')).rename('CHL')
+
+ # Create visualization parameters
+ ndvi_vis = {
+ 'min': 0,
+ 'max': 1,
+ 'palette': ['red', 'yellow', 'green']
+ }
+
+ ndwi_vis = {
+ 'min': -1,
+ 'max': 1,
+ 'palette': ['red', 'white', 'blue']
+ }
+
+ lai_vis = {
+ 'min': 0,
+ 'max': 5,
+ 'palette': ['white', 'lightgreen', 'darkgreen']
+ }
+
+ chl_vis = {
+ 'min': 1,
+ 'max': 3,
+ 'palette': ['white', 'yellow', 'green']
+ }
+
+ # Get NDVI map tile URL
+ ndvi_mapid = ndvi.getMapId(ndvi_vis)
+ ndvi_url = ndvi_mapid['tile_fetcher'].url_format
+
+ # Get NDWI map tile URL
+ ndwi_mapid = ndwi.getMapId(ndwi_vis)
+ ndwi_url = ndwi_mapid['tile_fetcher'].url_format
+
+ # Get LAI map tile URL
+ lai_mapid = lai.getMapId(lai_vis)
+ lai_url = lai_mapid['tile_fetcher'].url_format
+
+ # Get CHL map tile URL
+ chl_mapid = chl.getMapId(chl_vis)
+ chl_url = chl_mapid['tile_fetcher'].url_format
+
+ # Get values at point
+ ndvi_val = ndvi.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).getInfo()['NDVI']
+
+ ndwi_val = ndwi.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).getInfo()['NDWI']
+
+ lai_val = lai.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).getInfo()['LAI']
+
+ chl_val = chl.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).getInfo()['CHL']
- if farm_growth_data:
- avg_heights = []
- for week in weeks:
- week_heights = [farm['heights'][list(weeks).index(week)] for farm in farm_growth_data if farm['heights'][list(weeks).index(week)] > 0]
- avg_heights.append(round(sum(week_heights) / len(week_heights)) if week_heights else 0)
-
- avg_growth_data = {
- 'farm_id': 'میانگین',
- 'variety': 'همه',
- 'age': 'همه',
- 'weeks': weeks,
- 'heights': avg_heights
- }
- return {'individual': farm_growth_data, 'average': avg_growth_data}
return {
- 'individual': [],
- 'average': {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': [0] * len(weeks)}
+ 'urls': {
+ 'ndvi': ndvi_url,
+ 'ndwi': ndwi_url,
+ 'lai': lai_url,
+ 'chl': chl_url
+ },
+ 'values': {
+ 'ndvi': ndvi_val,
+ 'ndwi': ndwi_val,
+ 'lai': lai_val,
+ 'chl': chl_val
+ }
}
-# Initialize Earth Engine and load data
-ee_initialized = initialize_earth_engine()
-farm_df = load_farm_data()
-coordinates_df = load_coordinates_data()
-day_df = load_day_data()
-
-# Load animations
-lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json')
-lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json')
-lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json')
+# Get time series data for indices
+@st.cache_data(ttl=3600)
+def get_time_series(lat, lon, start_date, end_date):
+ # Define point of interest
+ poi = ee.Geometry.Point([lon, lat])
+
+ # Define region around the point
+ region = poi.buffer(500) # 500m buffer around the point
+
+ # Load Sentinel-2 collection for the time period
+ s2_collection = ee.ImageCollection('COPERNICUS/S2_SR') \
+ .filterBounds(region) \
+ .filterDate(start_date, end_date) \
+ .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 20))
+
+ # Create a function to calculate indices for each image
+ def add_indices(image):
+ ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
+ ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
+ lai = ndvi.multiply(3.618).subtract(0.118).rename('LAI')
+ chl = image.select('B8').divide(image.select('B5')).rename('CHL')
+
+ # Get the timestamp
+ date = ee.Date(image.get('system:time_start'))
+
+ # Get values at the point
+ ndvi_val = ndvi.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).get('NDVI')
+
+ ndwi_val = ndwi.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).get('NDWI')
+
+ lai_val = lai.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).get('LAI')
+
+ chl_val = chl.reduceRegion(
+ reducer=ee.Reducer.mean(),
+ geometry=poi,
+ scale=10
+ ).get('CHL')
+
+ # Return a feature with these properties
+ return ee.Feature(None, {
+ 'date': date.format('YYYY-MM-dd'),
+ 'ndvi': ndvi_val,
+ 'ndwi': ndwi_val,
+ 'lai': lai_val,
+ 'chl': chl_val
+ })
+
+ # Map the function over the collection
+ indices_fc = s2_collection.map(add_indices)
+
+ # Get the data as a list of dictionaries
+ indices_data = indices_fc.getInfo()['features']
+
+ # Convert to pandas DataFrame
+ if indices_data:
+ data_list = [{'date': feature['properties']['date'],
+ 'ndvi': feature['properties']['ndvi'],
+ 'ndwi': feature['properties']['ndwi'],
+ 'lai': feature['properties']['lai'],
+ 'chl': feature['properties']['chl']}
+ for feature in indices_data]
+
+ df = pd.DataFrame(data_list)
+ df['date'] = pd.to_datetime(df['date'])
+ return df.sort_values('date')
+ else:
+ return pd.DataFrame(columns=['date', 'ndvi', 'ndwi', 'lai', 'chl'])
-# Create session state for storing data
-if 'heights_df' not in st.session_state:
- st.session_state.heights_df = farm_df.copy()
+# Get weather data from OpenWeather API
+@st.cache_data(ttl=3600)
+def get_weather_data(lat, lon):
+ api_key = "ed47316a45379e2221a75f813229fb46"
+ url = f"https://api.openweathermap.org/data/2.5/onecall?lat={lat}&lon={lon}&exclude=minutely,hourly,alerts&appid={api_key}&units=metric"
+
+ response = requests.get(url)
+
+ if response.status_code == 200:
+ data = response.json()
+
+ # Current weather
+ current = data.get('current', {})
+ current_weather = {
+ 'temp': current.get('temp'),
+ 'humidity': current.get('humidity'),
+ 'wind_speed': current.get('wind_speed'),
+ 'description': current.get('weather', [{}])[0].get('description', '')
+ }
+
+ # Daily forecast for the next 7 days
+ daily = data.get('daily', [])
+ daily_forecast = []
+
+ for day in daily:
+ date = datetime.fromtimestamp(day.get('dt')).strftime('%Y-%m-%d')
+ daily_forecast.append({
+ 'date': date,
+ 'temp_min': day.get('temp', {}).get('min'),
+ 'temp_max': day.get('temp', {}).get('max'),
+ 'humidity': day.get('humidity'),
+ 'wind_speed': day.get('wind_speed')
+ })
+
+ return {
+ 'current': current_weather,
+ 'forecast': daily_forecast
+ }
+ else:
+ st.error(f"Failed to fetch weather data: {response.status_code}")
+ return None
-# Main header with animation
-st.markdown('', unsafe_allow_html=True)
-st.markdown('
سامانه هوشمند پایش مزارع نیشکر دهخدا ', unsafe_allow_html=True)
-st.markdown('
پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع نیشکر با هوش مصنوعی پیشرفته
', unsafe_allow_html=True)
-st.markdown('
', unsafe_allow_html=True)
+# Display folium map with satellite data
+def display_satellite_map(lat, lon, tile_url, layer_name):
+ # Create map centered on the farm
+ m = folium.Map(location=[lat, lon], zoom_start=15)
+
+ # Add base tiles
+ folium.TileLayer('OpenStreetMap').add_to(m)
+ folium.TileLayer('Stamen Terrain').add_to(m)
+
+ # Add satellite data tile layer
+ folium.TileLayer(
+ tiles=tile_url,
+ attr='Google Earth Engine',
+ name=layer_name,
+ overlay=True,
+ opacity=0.7
+ ).add_to(m)
+
+ # Add marker for the farm
+ folium.Marker(
+ [lat, lon],
+ popup=f"Farm Location\nLat: {lat}\nLon: {lon}"
+ ).add_to(m)
+
+ # Add layer control
+ folium.LayerControl().add_to(m)
+
+ return m
-# Create a stunning navigation menu
-selected = option_menu(
- menu_title=None,
- options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل دادهها", "گزارشگیری", "تنظیمات"],
- icons=["speedometer2", "map", "pencil-square", "bar-chart", "file-text", "gear"],
- menu_icon="leaf",
- default_index=0,
- orientation="horizontal",
- styles={
- "container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "25px"},
- "icon": {"color": "#2ecc71", "font-size": "22px"},
- "nav-link": {"font-size": "18px", "text-align": "center", "margin": "5px", "--hover-color": "#e9f7ef", "border-radius": "12px"},
- "nav-link-selected": {"background": "linear-gradient(135deg, #2ecc71 0%, #27ae60 100%)", "color": "white", "font-weight": "700", "box-shadow": "0 10px 30px rgba(46, 204, 113, 0.4)"},
- }
-)
+# Initialize Earth Engine
+ee_initialized = initialize_ee()
-# Dashboard
-if selected == "داشبورد":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("### نمای کلی مزارع", unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+if ee_initialized:
+ # Load farm data
+ farm_coords, farm_db = load_farm_data()
- # Dashboard metrics with animation
- col1, col2, col3, col4 = st.columns(4)
+ # Create sidebar for selection
+ st.sidebar.title("Farm Selection")
- with col1:
- st.markdown('', unsafe_allow_html=True)
- st.markdown(f'
{len(farm_df["Farm_ID"].unique())}
', unsafe_allow_html=True)
- st.markdown('
تعداد مزارع
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+ # Select farm from dropdown
+ farm_list = farm_coords['name'].unique().tolist()
+ selected_farm = st.sidebar.selectbox("Select Farm", farm_list)
- with col2:
- active_farms = int(len(farm_df["Farm_ID"].unique()) * 0.85)
- st.markdown('', unsafe_allow_html=True)
- st.markdown(f'
{active_farms}
', unsafe_allow_html=True)
- st.markdown('
مزارع فعال
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+ # Get the selected farm data
+ farm_row = farm_coords[farm_coords['name'] == selected_farm].iloc[0]
+ farm_lat = farm_row['latitude']
+ farm_lon = farm_row['longitude']
+ farm_age = farm_row['age']
+ farm_variety = farm_row['variety']
- with col3:
- avg_height = farm_df['ارتفاع هفته جاری مزرعه'].mean()
- st.markdown('', unsafe_allow_html=True)
- st.markdown(f'
{avg_height:.1f} cm
', unsafe_allow_html=True)
- st.markdown('
میانگین ارتفاع
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+ # Get farm information from database
+ farm_info = farm_db[farm_db['مزرعه'] == selected_farm]
- with col4:
- avg_moisture = farm_df['CurrentMoisture'].mean()
- st.markdown('', unsafe_allow_html=True)
- st.markdown(f'
{avg_moisture:.1f}%
', unsafe_allow_html=True)
- st.markdown('
میانگین رطوبت
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+ # Select date range
+ st.sidebar.subheader("Date Selection")
- # Dashboard tabs with animation
- tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "دادهها"])
+ # Default to last 30 days
+ end_date = datetime.now()
+ start_date = end_date - timedelta(days=30)
- with tab1:
- st.markdown('', unsafe_allow_html=True)
- st.markdown("### توزیع واریتهها و سن محصول", unsafe_allow_html=True)
-
- col1, col2 = st.columns(2)
-
- with col1:
- variety_counts = farm_df['Variety'].value_counts().reset_index()
- variety_counts.columns = ['Variety', 'Count']
- fig = px.pie(
- variety_counts,
- values='Count',
- names='Variety',
- title='توزیع واریتهها',
- color_discrete_sequence=px.colors.sequential.Greens_r
- )
- fig.update_traces(textposition='inside', textinfo='percent+label')
- fig.update_layout(
- font=dict(family="Vazirmatn"),
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5),
- template="plotly_white",
- height=400
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- with col2:
- age_counts = farm_df['Age'].value_counts().reset_index()
- age_counts.columns = ['Age', 'Count']
- fig = px.pie(
- age_counts,
- values='Count',
- names='Age',
- title='توزیع سن محصول',
- color_discrete_sequence=px.colors.sequential.Blues_r
- )
- fig.update_traces(textposition='inside', textinfo='percent+label')
- fig.update_layout(
- font=dict(family="Vazirmatn"),
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5),
- template="plotly_white",
- height=400
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- st.markdown("### اطلاعات کلی مزارع")
-
- total_area = farm_df['Area'].sum()
-
- col1, col2, col3 = st.columns(3)
- with col1:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{len(farm_df["Farm_ID"].unique())}
', unsafe_allow_html=True)
- st.markdown('
تعداد کل مزارع
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
- with col2:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{total_area:.2f}
', unsafe_allow_html=True)
- st.markdown('
مساحت کل (هکتار)
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
- with col3:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{farm_df["Channel"].nunique()}
', unsafe_allow_html=True)
- st.markdown('
تعداد کانالها
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- st.markdown('
', unsafe_allow_html=True)
-
- st_lottie(lottie_farm, height=350, key="farm_animation", speed=1.2)
- st.markdown('
', unsafe_allow_html=True)
+ start_date_input = st.sidebar.date_input("Start Date", start_date)
+ end_date_input = st.sidebar.date_input("End Date", end_date)
- with tab2:
- st.markdown('', unsafe_allow_html=True)
- st.markdown("### نقشه مزارع", unsafe_allow_html=True)
-
- if coordinates_df is not None and not coordinates_df.empty:
- m = folium.Map(location=[31.45, 48.72], zoom_start=12, tiles='CartoDB positron')
- for _, farm in coordinates_df.iterrows():
- lat = farm['Latitude']
- lon = farm['Longitude']
- name = farm['Farm_ID']
- farm_info = farm_df[farm_df['Farm_ID'] == name]
- if not farm_info.empty:
- variety = farm_info['Variety'].iloc[0]
- age = farm_info['Age'].iloc[0]
- area = farm_info['Area'].iloc[0]
- popup_text = f"""
-
-
مزرعه {name}
-
واریته: {variety}
-
سن: {age}
-
مساحت: {area} هکتار
-
- """
- else:
- popup_text = f"
مزرعه {name}
"
- folium.Marker(
- [lat, lon],
- popup=folium.Popup(popup_text, max_width=300),
- tooltip=f"مزرعه {name}",
- icon=folium.Icon(color='#2ecc71', icon='leaf', prefix='fa')
- ).add_to(m)
- st.markdown('
', unsafe_allow_html=True)
- folium_static(m, width=1000, height=600)
- st.markdown('
', unsafe_allow_html=True)
- else:
- st.warning("دادههای مختصات در دسترس نیست.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
+ # Convert to string format for Earth Engine
+ start_date_str = start_date_input.strftime('%Y-%m-%d')
+ end_date_str = end_date_input.strftime('%Y-%m-%d')
- with tab3:
- st.markdown('', unsafe_allow_html=True)
- st.markdown("### نمودار رشد هفتگی", unsafe_allow_html=True)
-
- col1, col2 = st.columns(2)
- with col1:
- selected_variety = st.selectbox(
- "انتخاب واریته",
- ["all"] + list(farm_df['Variety'].unique()),
- format_func=lambda x: "همه واریتهها" if x == "all" else x,
- key="growth_variety",
- help="واریته موردنظر را برای تحلیل انتخاب کنید"
- )
-
- with col2:
- selected_age = st.selectbox(
- "انتخاب سن",
- ["all"] + list(farm_df['Age'].unique()),
- format_func=lambda x: "همه سنین" if x == "all" else x,
- key="growth_age",
- help="سن موردنظر را برای تحلیل انتخاب کنید"
- )
-
- growth_data = generate_real_growth_data(selected_variety, selected_age)
+ # Get days in database for the selected farm
+ if not farm_info.empty:
+ days = farm_info['روز'].unique().tolist()
+ selected_day = st.sidebar.selectbox("Select Day", days)
- chart_tab1, chart_tab2 = st.tabs(["میانگین رشد", "رشد مزارع فردی"])
+ # Filter farm info by selected day
+ day_info = farm_info[farm_info['روز'] == selected_day]
- with chart_tab1:
- avg_data = growth_data['average']
- fig = go.Figure()
- fig.add_trace(go.Scatter(
- x=avg_data['weeks'],
- y=avg_data['heights'],
- mode='lines+markers',
- name='میانگین رشد',
- line=dict(color='#2ecc71', width=4),
- marker=dict(size=10, color='#2ecc71')
- ))
- fig.update_layout(
- title='میانگین رشد هفتگی',
- xaxis_title='هفته',
- yaxis_title='ارتفاع (سانتیمتر)',
- font=dict(family='Vazirmatn', size=16),
- hovermode='x unified',
- template='plotly_white',
- height=500,
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff'
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- with chart_tab2:
- if growth_data['individual']:
- fig = go.Figure()
- colors = ['#2ecc71', '#3498db', '#e67e22', '#9b59b6', '#e74c3c']
- for i, farm_data in enumerate(growth_data['individual'][:5]):
- fig.add_trace(go.Scatter(
- x=farm_data['weeks'],
- y=farm_data['heights'],
- mode='lines+markers',
- name=f"مزرعه {farm_data['farm_id']}",
- line=dict(color=colors[i % len(colors)], width=3),
- marker=dict(size=8, color=colors[i % len(colors)])
- ))
- fig.update_layout(
- title='رشد هفتگی مزارع فردی',
- xaxis_title='هفته',
- yaxis_title='ارتفاع (سانتیمتر)',
- font=dict(family='Vazirmatn', size=16),
- hovermode='x unified',
- template='plotly_white',
- height=500,
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff'
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
- else:
- st.warning("دادهای برای نمایش وجود ندارد.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
-
- with tab4:
- st.markdown('', unsafe_allow_html=True)
- st.markdown("### دادههای مزارع", unsafe_allow_html=True)
-
- search_term = st.text_input(
- "جستجو در دادهها",
- placeholder="نام مزرعه، واریته، سن و...",
- help="عبارت موردنظر را برای جستجو وارد کنید"
+ if not day_info.empty:
+ # Display farm details
+ with st.expander("Farm Details", expanded=True):
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ st.metric("Farm", selected_farm)
+
+ with col2:
+ st.metric("Age", farm_age)
+
+ with col3:
+ st.metric("Variety", farm_variety)
+
+ with col4:
+ st.metric("Area", f"{day_info.iloc[0]['مساحت زیرمجموعه']} ha")
+
+ # Fetch satellite data
+ with st.spinner("Fetching satellite data..."):
+ indices_data = get_satellite_indices(
+ farm_lat,
+ farm_lon,
+ [start_date_str, end_date_str]
)
- if search_term:
- filtered_df = farm_df[
- farm_df['Farm_ID'].astype(str).str.contains(search_term, case=False) |
- farm_df['Variety'].astype(str).str.contains(search_term, case=False) |
- farm_df['Age'].astype(str).str.contains(search_term, case=False) |
- farm_df['Channel'].astype(str).str.contains(search_term, case=False)
- ]
- else:
- filtered_df = farm_df
-
- if not filtered_df.empty:
- csv = filtered_df.to_csv(index=False).encode('utf-8')
- st.download_button(
- label="دانلود دادهها (CSV)",
- data=csv,
- file_name="farm_data.csv",
- mime="text/csv",
- key="download_data",
- help="دادههای فیلترشده را دانلود کنید"
- )
- st.dataframe(
- filtered_df,
- use_container_width=True,
- height=450,
- hide_index=True
- )
- st.info(f"نمایش {len(filtered_df)} مزرعه از {len(farm_df)} مزرعه", icon="ℹ️")
- else:
- st.warning("هیچ دادهای یافت نشد.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
-
-# Map Page
-elif selected == "نقشه مزارع":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("## نقشه مزارع با شاخصهای ماهوارهای", unsafe_allow_html=True)
+ # Get time series data for the period
+ time_series = get_time_series(
+ farm_lat,
+ farm_lon,
+ start_date_str,
+ end_date_str
+ )
- col1, col2 = st.columns([1, 3])
+ # Fetch weather data
+ with st.spinner("Fetching weather data..."):
+ weather_data = get_weather_data(farm_lat, farm_lon)
- with col1:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("### تنظیمات نقشه")
-
- selected_farm = st.selectbox(
- "انتخاب مزرعه",
- options=coordinates_df['Farm_ID'].tolist(),
- index=0,
- format_func=lambda x: f"مزرعه {x}",
- help="مزرعه موردنظر را برای نمایش نقشه انتخاب کنید"
- )
-
- selected_date = st.date_input(
- "انتخاب تاریخ",
- value=datetime.now(),
- format="YYYY-MM-DD",
- help="تاریخی را برای تولید نقشه انتخاب کنید"
- )
-
- selected_layer = st.selectbox(
- "انتخاب شاخص",
- options=["NDVI", "NDMI", "EVI", "NDWI"],
- format_func=lambda x: {
- "NDVI": "شاخص پوشش گیاهی (NDVI)",
- "NDMI": "شاخص رطوبت (NDMI)",
- "EVI": "شاخص پیشرفته گیاهی (EVI)",
- "NDWI": "شاخص آب (NDWI)"
- }[x],
- help="شاخص موردنظر را برای تحلیل انتخاب کنید"
- )
-
- generate_map = st.button(
- "تولید نقشه",
- type="primary",
- use_container_width=True,
- help="نقشه را با تنظیمات انتخابشده تولید کنید"
- )
-
- st.markdown('
', unsafe_allow_html=True)
-
- st.markdown("### راهنمای شاخصها")
-
- with st.expander("شاخص پوشش گیاهی (NDVI)", expanded=selected_layer == "NDVI"):
- st.markdown("""
- **شاخص تفاضل نرمالشده پوشش گیاهی (NDVI)** معیاری برای سنجش سلامت و تراکم پوشش گیاهی است.
+ # Display tabs
+ tab1, tab2, tab3, tab4 = st.tabs([
+ "Current Status",
+ "Time Series Analysis",
+ "Weather Data",
+ "Weekly Report"
+ ])
+
+ with tab1:
+ if indices_data:
+ # Display satellite indices
+ st.subheader("Vegetation Indices")
- - **مقادیر بالا (0.6 تا 1.0)**: پوشش گیاهی متراکم و سالم
- - **مقادیر متوسط (0.2 تا 0.6)**: پوشش گیاهی متوسط
- - **مقادیر پایین (-1.0 تا 0.2)**: پوشش گیاهی کم یا خاک لخت
+ col1, col2, col3, col4 = st.columns(4)
- فرمول: NDVI = (NIR - RED) / (NIR + RED)
- """, unsafe_allow_html=True)
-
- with st.expander("شاخص رطوبت (NDMI)", expanded=selected_layer == "NDMI"):
- st.markdown("""
- **شاخص تفاضل نرمالشده رطوبت (NDMI)** برای ارزیابی محتوای رطوبت گیاهان استفاده میشود.
+ with col1:
+ ndvi_val = indices_data['values']['ndvi']
+ ndvi_color = "green" if ndvi_val > 0.5 else "yellow" if ndvi_val > 0.2 else "red"
+ st.metric("NDVI", f"{ndvi_val:.3f}", delta="Good" if ndvi_val > 0.5 else "Medium" if ndvi_val > 0.2 else "Poor", delta_color="normal")
- - **مقادیر بالا (0.4 تا 1.0)**: محتوای رطوبت بالا
- - **مقادیر متوسط (0.0 تا 0.4)**: محتوای رطوبت متوسط
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای رطوبت کم
+ with col2:
+ ndwi_val = indices_data['values']['ndwi']
+ st.metric("NDWI", f"{ndwi_val:.3f}", delta="Good" if ndwi_val > 0 else "Low", delta_color="normal")
- فرمول: NDMI = (NIR - SWIR) / (NIR + SWIR)
- """, unsafe_allow_html=True)
-
- with st.expander("شاخص پیشرفته گیاهی (EVI)", expanded=selected_layer == "EVI"):
- st.markdown("""
- **شاخص پیشرفته پوشش گیاهی (EVI)** نسخه بهبودیافته NDVI است که حساسیت کمتری به اثرات خاک و اتمسفر دارد.
+ with col3:
+ lai_val = indices_data['values']['lai']
+ st.metric("LAI", f"{lai_val:.3f}", delta="Good" if lai_val > 2 else "Medium" if lai_val > 1 else "Poor", delta_color="normal")
- - **مقادیر بالا (0.4 تا 1.0)**: پوشش گیاهی متراکم و سالم
- - **مقادیر متوسط (0.2 تا 0.4)**: پوشش گیاهی متوسط
- - **مقادیر پایین (0.0 تا 0.2)**: پوشش گیاهی کم
+ with col4:
+ chl_val = indices_data['values']['chl']
+ st.metric("CHL", f"{chl_val:.3f}", delta="Good" if chl_val > 2 else "Medium" if chl_val > 1.5 else "Poor", delta_color="normal")
- فرمول: EVI = 2.5 * ((NIR - RED) / (NIR + 6*RED - 7.5*BLUE + 1))
- """, unsafe_allow_html=True)
-
- with st.expander("شاخص آب (NDWI)", expanded=selected_layer == "NDWI"):
- st.markdown("""
- **شاخص تفاضل نرمالشده آب (NDWI)** برای شناسایی پهنههای آبی و ارزیابی محتوای آب در گیاهان استفاده میشود.
+ # Display maps
+ st.subheader("Satellite Maps")
- - **مقادیر بالا (0.3 تا 1.0)**: پهنههای آبی
- - **مقادیر متوسط (0.0 تا 0.3)**: محتوای آب متوسط
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای آب کم یا خاک خشک
+ map_tabs = st.tabs(["NDVI", "NDWI", "LAI", "CHL"])
- فرمول: NDWI = (GREEN - NIR) / (GREEN + NIR)
- """, unsafe_allow_html=True)
-
- st.markdown('', unsafe_allow_html=True)
-
- with col2:
- map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"])
-
- with map_tab:
- st.markdown('
', unsafe_allow_html=True)
- if generate_map or 'last_map' not in st.session_state:
- with st.spinner('
'):
- m = create_ee_map(
- selected_farm,
- selected_date.strftime('%Y-%m-%d'),
- selected_layer
- )
- if m:
- st.session_state.last_map = m
- folium_static(m, width=900, height=600)
- st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} با موفقیت تولید شد.", icon="✅")
- else:
- st.error("خطا در تولید نقشه. لطفاً دوباره تلاش کنید.", icon="❌")
- elif 'last_map' in st.session_state:
- folium_static(st.session_state.last_map, width=900, height=600)
- st.markdown('
', unsafe_allow_html=True)
- st.info("""
- **نکته:** این نقشه بر اساس تصاویر ماهوارهای Sentinel-2 تولید شده است.
- برای دقت بیشتر، تاریخی را انتخاب کنید که ابرناکی کمتری داشته باشد.
- """, icon="ℹ️")
-
- with stats_tab:
- if 'last_map' in st.session_state:
- stats = calculate_farm_stats(selected_farm, selected_layer)
-
- col1, col2, col3, col4 = st.columns(4)
-
- with col1:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{stats["mean"]:.2f}
', unsafe_allow_html=True)
- st.markdown(f'
میانگین {selected_layer}
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- with col2:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{stats["max"]:.2f}
', unsafe_allow_html=True)
- st.markdown(f'
حداکثر {selected_layer}
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- with col3:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{stats["min"]:.2f}
', unsafe_allow_html=True)
- st.markdown(f'
حداقل {selected_layer}
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- with col4:
- st.markdown('
', unsafe_allow_html=True)
- st.markdown(f'
{stats["std_dev"]:.2f}
', unsafe_allow_html=True)
- st.markdown(f'
انحراف معیار
', unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
- fig = px.histogram(
- x=stats["histogram_data"],
- nbins=20,
- title=f"توزیع مقادیر {selected_layer} در مزرعه {selected_farm}",
- labels={"x": f"مقدار {selected_layer}", "y": "فراوانی"},
- color_discrete_sequence=["#2ecc71"]
- )
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- bargap=0.1,
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=400
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- dates = pd.date_range(end=selected_date, periods=30, freq='D')
- values = [stats["mean"] + np.random.normal(0, stats["std_dev"] / 2) for _ in range(30)]
- values = np.clip(values, stats["min"], stats["max"])
-
- fig = px.line(
- x=dates,
- y=values,
- title=f"روند تغییرات {selected_layer} در 30 روز گذشته",
- labels={"x": "تاریخ", "y": f"مقدار {selected_layer}"},
- markers=True,
- line_shape="spline"
+ with map_tabs[0]:
+ ndvi_map = display_satellite_map(
+ farm_lat,
+ farm_lon,
+ indices_data['urls']['ndvi'],
+ "NDVI"
)
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- hovermode="x unified",
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=400
+ st.write("NDVI (Normalized Difference Vegetation Index) - Higher values (green) indicate healthy vegetation")
+ folium_static(ndvi_map, width=800, height=500)
+
+ with map_tabs[1]:
+ ndwi_map = display_satellite_map(
+ farm_lat,
+ farm_lon,
+ indices_data['urls']['ndwi'],
+ "NDWI"
)
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- farm_names = coordinates_df['Farm_ID'].tolist()[:5]
- comparison_values = [stats["mean"] + np.random.uniform(-0.2, 0.2) for _ in range(len(farm_names))]
-
- fig = px.bar(
- x=farm_names,
- y=comparison_values,
- title=f"مقایسه {selected_layer} بین مزارع",
- labels={"x": "مزرعه", "y": f"مقدار {selected_layer}"},
- color=comparison_values,
- color_continuous_scale="Viridis"
+ st.write("NDWI (Normalized Difference Water Index) - Higher values (blue) indicate more water content")
+ folium_static(ndwi_map, width=800, height=500)
+
+ with map_tabs[2]:
+ lai_map = display_satellite_map(
+ farm_lat,
+ farm_lon,
+ indices_data['urls']['lai'],
+ "LAI"
)
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- coloraxis_showscale=False,
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=400
+ st.write("LAI (Leaf Area Index) - Higher values (darker green) indicate more leaf area")
+ folium_static(lai_map, width=800, height=500)
+
+ with map_tabs[3]:
+ chl_map = display_satellite_map(
+ farm_lat,
+ farm_lon,
+ indices_data['urls']['chl'],
+ "CHL"
)
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
- else:
- st.warning("لطفاً ابتدا یک نقشه تولید کنید.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
-
-# Data Entry Page
-elif selected == "ورود اطلاعات":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("## ورود اطلاعات روزانه مزارع", unsafe_allow_html=True)
-
- tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
-
- with tab1:
- col1, col2 = st.columns(2)
-
- with col1:
- selected_week = st.selectbox(
- "انتخاب هفته",
- options=[str(i) for i in range(1, 23)],
- format_func=lambda x: f"هفته {x}",
- help="هفته موردنظر را برای ورود اطلاعات انتخاب کنید"
- )
-
- with col2:
- days = day_df['Day'].unique().tolist()
- selected_day = st.selectbox("انتخاب روز", options=days, help="روز موردنظر را برای ورود اطلاعات انتخاب کنید")
-
- filtered_farms = farm_df[farm_df['Week'] == int(selected_week)]
- filtered_farms = filtered_farms[filtered_farms['Farm_ID'].isin(day_df[day_df['Day'] == selected_day]['Farm_ID'])]
-
- if filtered_farms.empty:
- st.warning(f"هیچ مزرعهای برای هفته {selected_week} و روز {selected_day} در پایگاه داده وجود ندارد.", icon="⚠️")
+ st.write("CHL (Chlorophyll Content) - Higher values (green) indicate more chlorophyll")
+ folium_static(chl_map, width=800, height=500)
else:
- st.markdown("### ورود دادههای مزارع")
+ st.warning("No satellite data available for the selected date range. Try extending the date range.")
+
+ with tab2:
+ if not time_series.empty:
+ st.subheader("Time Series Analysis")
- data_key = f"data_{selected_week}_{selected_day}"
- if data_key not in st.session_state:
- st.session_state[data_key] = pd.DataFrame({
- 'Farm_ID': filtered_farms['Farm_ID'],
- 'Station1': [0] * len(filtered_farms),
- 'Station2': [0] * len(filtered_farms),
- 'Station3': [0] * len(filtered_farms),
- 'Station4': [0] * len(filtered_farms),
- 'Station5': [0] * len(filtered_farms),
- 'Well1': [0] * len(filtered_farms),
- 'Well2': [0] * len(filtered_farms),
- 'CurrentMoisture': [0] * len(filtered_farms),
- 'CurrentNitrogen': [0] * len(filtered_farms),
- 'CurrentHeight': [0] * len(filtered_farms)
- })
+ # Plot time series
+ ts_tabs = st.tabs(["NDVI", "NDWI", "LAI", "CHL", "Comparison"])
- edited_df = st.data_editor(
- st.session_state[data_key],
- use_container_width=True,
- num_rows="fixed",
- column_config={
- "Farm_ID": st.column_config.TextColumn("مزرعه", disabled=True),
- "Station1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 1 را وارد کنید"),
- "Station2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 2 را وارد کنید"),
- "Station3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 3 را وارد کنید"),
- "Station4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 4 را وارد کنید"),
- "Station5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=400, step=1, help="ارتفاع ایستگاه 5 را وارد کنید"),
- "Well1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=200, step=1, help="مقدار چاهک 1 را وارد کنید"),
- "Well2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=200, step=1, help="مقدار چاهک 2 را وارد کنید"),
- "CurrentMoisture": st.column_config.NumberColumn("رطوبت", min_value=0, max_value=100, step=0.1, help="رطوبت فعلی را وارد کنید"),
- "CurrentNitrogen": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=5, step=0.01, help="نیتروژن فعلی را وارد کنید"),
- "CurrentHeight": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True, help="میانگین ارتفاع بهطور خودکار محاسبه میشود")
- },
- hide_index=True
- )
+ with ts_tabs[0]:
+ fig = px.line(
+ time_series,
+ x='date',
+ y='ndvi',
+ title=f"NDVI Time Series for {selected_farm}",
+ labels={"date": "Date", "ndvi": "NDVI Value"},
+ markers=True
+ )
+ st.plotly_chart(fig, use_container_width=True)
- for i in range(len(edited_df)):
- stations = [
- edited_df.iloc[i]['Station1'],
- edited_df.iloc[i]['Station2'],
- edited_df.iloc[i]['Station3'],
- edited_df.iloc[i]['Station4'],
- edited_df.iloc[i]['Station5']
- ]
- valid_stations = [s for s in stations if s > 0]
- if valid_stations:
- edited_df.iloc[i, edited_df.columns.get_loc('CurrentHeight')] = round(sum(valid_stations) / len(valid_stations), 1)
+ with ts_tabs[1]:
+ fig = px.line(
+ time_series,
+ x='date',
+ y='ndwi',
+ title=f"NDWI Time Series for {selected_farm}",
+ labels={"date": "Date", "ndwi": "NDWI Value"},
+ markers=True
+ )
+ st.plotly_chart(fig, use_container_width=True)
- st.session_state[data_key] = edited_df
+ with ts_tabs[2]:
+ fig = px.line(
+ time_series,
+ x='date',
+ y='lai',
+ title=f"LAI Time Series for {selected_farm}",
+ labels={"date": "Date", "lai": "LAI Value"},
+ markers=True
+ )
+ st.plotly_chart(fig, use_container_width=True)
- if st.button("ذخیره اطلاعات", type="primary", use_container_width=True, help="دادهها را ذخیره کنید"):
- new_data = edited_df.copy()
- new_data['Week'] = int(selected_week)
- new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d')
- new_data['Variety'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Variety'])
- new_data['Age'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Age'])
- new_data['Area'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Area'])
- new_data['Channel'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Channel'])
- new_data['Administration'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Administration'])
+ with ts_tabs[3]:
+ fig = px.line(
+ time_series,
+ x='date',
+ y='chl',
+ title=f"CHL Time Series for {selected_farm}",
+ labels={"date": "Date", "chl": "CHL Value"},
+ markers=True
+ )
+ st.plotly_chart(fig, use_container_width=True)
+
+ with ts_tabs[4]:
+ # Comparison of all indices
+ fig = go.Figure()
- st.session_state.heights_df = pd.concat([st.session_state.heights_df, new_data], ignore_index=True)
- st.success(f"دادههای هفته {selected_week} برای روز {selected_day} با موفقیت ذخیره شدند.", icon="✅")
- st.balloons()
-
- with tab2:
- st.markdown("### آپلود فایل اکسل")
-
- uploaded_file = st.file_uploader(
- "فایل اکسل یا CSV خود را آپلود کنید",
- type=["xlsx", "xls", "csv"],
- help="فایل دادههای مزارع را آپلود کنید"
- )
-
- if uploaded_file is not None:
- try:
- if uploaded_file.name.endswith('.csv'):
- df = pd.read_csv(uploaded_file)
- else:
- df = pd.read_excel(uploaded_file)
- st.dataframe(df, use_container_width=True)
+ fig.add_trace(go.Scatter(
+ x=time_series['date'],
+ y=time_series['ndvi'],
+ mode='lines+markers',
+ name='NDVI'
+ ))
- if st.button("ذخیره فایل", type="primary", use_container_width=True, help="فایل را ذخیره کنید"):
- st.session_state.heights_df = pd.concat([st.session_state.heights_df, df], ignore_index=True)
- st.success("فایل با موفقیت ذخیره شد.", icon="✅")
- st.balloons()
- except Exception as e:
- st.error(f"خطا در خواندن فایل: {e}", icon="❌")
-
- st.markdown("### راهنمای فرمت فایل")
- st.markdown("""
- فایل اکسل یا CSV باید شامل ستونهای زیر باشد:
-
- - Farm_ID
- - Station1 تا Station5
- - Well1 و Well2
- - CurrentMoisture (رطوبت)
- - CurrentNitrogen
-
- میتوانید از [این فایل نمونه](https://example.com/sample.xlsx) به عنوان الگو استفاده کنید.
- """, unsafe_allow_html=True)
-
- st.markdown("""
-
-
-
-
-
-
-
فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید
-
- """, unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
-
-# Data Analysis Page
-elif selected == "تحلیل دادهها":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("## تحلیل هوشمند دادهها", unsafe_allow_html=True)
-
- col1, col2 = st.columns([1, 2])
-
- with col1:
- st_lottie(lottie_analysis, height=250, key="analysis_animation", speed=1.2)
-
- with col2:
- st.markdown("""
-
-
تحلیل پیشرفته دادههای مزارع
-
در این بخش میتوانید تحلیلهای پیشرفته روی دادههای مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.
-
- """, unsafe_allow_html=True)
-
- tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریتهها", "تحلیل رطوبت", "پیشبینی"])
+ fig.add_trace(go.Scatter(
+ x=time_series['date'],
+ y=time_series['ndwi'],
+ mode='lines+markers',
+ name='NDWI'
+ ))
+
+ fig.add_trace(go.Scatter(
+ x=time_series['date'],
+ y=time_series['lai'],
+ mode='lines+markers',
+ name='LAI'
+ ))
+
+ fig.add_trace(go.Scatter(
+ x=time_series['date'],
+ y=time_series['chl'],
+ mode='lines+markers',
+ name='CHL'
+ ))
+
+ fig.update_layout(
+ title=f"Vegetation Indices Comparison for {selected_farm}",
+ xaxis_title="Date",
+ yaxis_title="Index Value",
+ legend_title="Index"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+ else:
+ st.warning("No time series data available for the selected date range. Try extending the date range.")
- with tab1:
- st.markdown("### تحلیل رشد مزارع")
-
- col1, col2 = st.columns(2)
-
- with col1:
- selected_variety = st.selectbox(
- "انتخاب واریته",
- ["all"] + list(farm_df['Variety'].unique()),
- format_func=lambda x: "همه واریتهها" if x == "all" else x,
- key="growth_variety",
- help="واریته موردنظر را برای تحلیل رشد انتخاب کنید"
- )
-
- with col2:
- selected_age = st.selectbox(
- "انتخاب سن",
- ["all"] + list(farm_df['Age'].unique()),
- format_func=lambda x: "همه سنین" if x == "all" else x,
- key="growth_age",
- help="سن موردنظر را برای تحلیل رشد انتخاب کنید"
- )
-
- growth_data = generate_real_growth_data(selected_variety, selected_age)
-
- if growth_data['individual']:
- chart_data = []
- for farm_data in growth_data['individual']:
- for i, week in enumerate(farm_data['weeks']):
- chart_data.append({
- 'Farm': farm_data['farm_id'],
- 'Week': week,
- 'Height': farm_data['heights'][i],
- 'Variety': farm_data['variety'],
- 'Age': farm_data['age']
- })
+ with tab3:
+ if weather_data:
+ st.subheader("Weather Data")
- chart_df = pd.DataFrame(chart_data)
+ # Current weather
+ current = weather_data['current']
+ forecast = weather_data['forecast']
- chart = alt.Chart(chart_df).mark_line(point=True).encode(
- x=alt.X('Week:Q', title='هفته'),
- y=alt.Y('Height:Q', title='ارتفاع (سانتیمتر)'),
- color=alt.Color('Farm:N', title='مزرعه'),
- tooltip=['Farm', 'Week', 'Height', 'Variety', 'Age']
- ).properties(
- width='container',
- height=450,
- title='روند رشد مزارع بر اساس هفته'
- ).interactive()
+ col1, col2, col3 = st.columns(3)
- st.altair_chart(chart, use_container_width=True)
+ with col1:
+ st.metric("Temperature", f"{current['temp']}°C")
- st.markdown("### تحلیل نرخ رشد")
+ with col2:
+ st.metric("Humidity", f"{current['humidity']}%")
- growth_rates = []
- for farm_data in growth_data['individual']:
- heights = farm_data['heights']
- for i in range(1, len(heights)):
- if heights[i] > 0 and heights[i-1] > 0:
- growth_rate = heights[i] - heights[i-1]
- growth_rates.append({
- 'Farm': farm_data['farm_id'],
- 'Week': farm_data['weeks'][i],
- 'Growth Rate': growth_rate,
- 'Variety': farm_data['variety'],
- 'Age': farm_data['age']
- })
+ with col3:
+ st.metric("Wind Speed", f"{current['wind_speed']} m/s")
- growth_rate_df = pd.DataFrame(growth_rates)
+ st.write(f"Current conditions: {current['description'].title()}")
- chart = alt.Chart(growth_rate_df).mark_bar().encode(
- x=alt.X('Week:O', title='هفته'),
- y=alt.Y('mean(Growth Rate):Q', title='نرخ رشد (سانتیمتر در هفته)'),
- color=alt.Color('Farm:N', title='مزرعه'),
- tooltip=['Farm', 'Week', 'mean(Growth Rate)']
- ).properties(
- width='container',
- height=450,
- title='نرخ رشد هفتگی مزارع'
- ).interactive()
+ # Weather forecast
+ st.subheader("7-Day Forecast")
- st.altair_chart(chart, use_container_width=True)
- else:
- st.warning("دادهای برای نمایش وجود ندارد.", icon="⚠️")
-
- with tab2:
- st.markdown("### مقایسه واریتهها")
-
- variety_age_groups = farm_df.groupby(['Variety', 'Age']).size().reset_index(name='Count')
-
- fig = px.density_heatmap(
- variety_age_groups,
- x='Variety',
- y='Age',
- z='Count',
- title='توزیع مزارع بر اساس واریته و سن',
- color_continuous_scale='Viridis'
- )
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- xaxis_title="واریته",
- yaxis_title="سن",
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=450
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- variety_heights = farm_df.groupby('Variety')['CurrentHeight'].apply(list).to_dict()
-
- fig = go.Figure()
- for variety, heights in variety_heights.items():
- fig.add_trace(go.Box(
- y=heights,
- name=variety,
- boxpoints='outliers',
- marker_color=f'hsl({hash(variety) % 360}, 70%, 50%)'
- ))
- fig.update_layout(
- title='مقایسه ارتفاع بر اساس واریته',
- yaxis_title='ارتفاع (سانتیمتر)',
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- boxmode='group',
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=450
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- variety_stats = {}
- for variety, heights in variety_heights.items():
- variety_stats[variety] = {
- 'میانگین': np.mean(heights),
- 'میانه': np.median(heights),
- 'انحراف معیار': np.std(heights),
- 'حداقل': np.min(heights),
- 'حداکثر': np.max(heights)
- }
- variety_stats_df = pd.DataFrame(variety_stats).T
- st.dataframe(variety_stats_df, use_container_width=True)
-
- with tab3:
- st.markdown("### تحلیل رطوبت مزارع")
-
- farms = farm_df['Farm_ID'].unique()[:10]
- dates = pd.date_range(end=datetime.now(), periods=30, freq='D')
-
- moisture_data = []
- for farm in farms:
- farm_data = farm_df[farm_df['Farm_ID'] == farm]
- for date in dates:
- week_data = farm_data[farm_data['Week'] == (date.isocalendar()[1] % 23 + 1)]
- moisture = week_data['CurrentMoisture'].mean() if not week_data.empty else np.random.uniform(50, 80)
- moisture = max(0, min(100, moisture))
- moisture_data.append({
- 'Farm': farm,
- 'Date': date,
- 'Moisture': moisture
- })
-
- moisture_df = pd.DataFrame(moisture_data)
-
- fig = px.line(
- moisture_df,
- x='Date',
- y='Moisture',
- color='Farm',
- title='روند رطوبت مزارع در 30 روز گذشته',
- labels={'Date': 'تاریخ', 'Moisture': 'رطوبت (%)', 'Farm': 'مزرعه'}
- )
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- hovermode="x unified",
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=450
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- st.markdown("### همبستگی رطوبت و ارتفاع")
-
- correlation_data = []
- for farm in farms:
- farm_data = farm_df[farm_df['Farm_ID'] == farm]
- for _, row in farm_data.iterrows():
- correlation_data.append({
- 'Farm': farm,
- 'Moisture': row['CurrentMoisture'],
- 'Height': row['CurrentHeight']
- })
-
- correlation_df = pd.DataFrame(correlation_data)
-
- fig = px.scatter(
- correlation_df,
- x='Moisture',
- y='Height',
- color='Farm',
- title='همبستگی بین رطوبت و ارتفاع',
- labels={'Moisture': 'رطوبت (%)', 'Height': 'ارتفاع (سانتیمتر)', 'Farm': 'مزرعه'},
- trendline='ols'
- )
- fig.update_layout(
- font=dict(family="Vazirmatn", size=14),
- template="plotly_white",
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff',
- height=450
- )
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
-
- correlation = correlation_df['Moisture'].corr(correlation_df['Height'])
- st.info(f"ضریب همبستگی بین رطوبت و ارتفاع: {correlation:.2f}", icon="ℹ️")
-
- with tab4:
- st.markdown("### پیشبینی رشد مزارع")
-
- selected_farm_for_prediction = st.selectbox(
- "انتخاب مزرعه",
- options=farm_df['Farm_ID'].tolist(),
- format_func=lambda x: f"مزرعه {x}",
- help="مزرعه موردنظر را برای پیشبینی رشد انتخاب کنید"
- )
-
- farm_data = farm_df[farm_df['Farm_ID'] == selected_farm_for_prediction]
- historical_weeks = farm_data['Week'].values
- historical_heights = farm_data['CurrentHeight'].values
-
- if len(historical_weeks) > 1 and len(historical_heights) > 1:
- model = LinearRegression()
- model.fit(historical_weeks.reshape(-1, 1), historical_heights)
-
- future_weeks = np.array(range(max(historical_weeks) + 1, 30)).reshape(-1, 1)
- future_heights = model.predict(future_weeks)
- lower_bound = future_heights - 15
- upper_bound = future_heights + 15
+ # Create forecast dataframe
+ forecast_df = pd.DataFrame(forecast)
+ # Plot temperature forecast
fig = go.Figure()
+
fig.add_trace(go.Scatter(
- x=historical_weeks,
- y=historical_heights,
+ x=forecast_df['date'],
+ y=forecast_df['temp_max'],
mode='lines+markers',
- name='دادههای تاریخی',
- line=dict(color='#2ecc71', width=4),
- marker=dict(size=10, color='#2ecc71')
- ))
- fig.add_trace(go.Scatter(
- x=future_weeks.flatten(),
- y=future_heights,
- mode='lines',
- name='پیشبینی',
- line=dict(color='#3498db', width=3, dash='dash')
- ))
- fig.add_trace(go.Scatter(
- x=future_weeks.flatten(),
- y=lower_bound,
- mode='lines',
- name='حد پایین',
- line=dict(color='#e74c3c', width=2, dash='dot'),
- showlegend=True
+ name='Max Temp',
+ line=dict(color='red')
))
+
fig.add_trace(go.Scatter(
- x=future_weeks.flatten(),
- y=upper_bound,
- mode='lines',
- name='حد بالا',
- line=dict(color='#e74c3c', width=2, dash='dot'),
- fill='tonexty',
- showlegend=True
+ x=forecast_df['date'],
+ y=forecast_df['temp_min'],
+ mode='lines+markers',
+ name='Min Temp',
+ line=dict(color='blue')
))
+
fig.update_layout(
- title=f'پیشبینی رشد مزرعه {selected_farm_for_prediction}',
- xaxis_title='هفته',
- yaxis_title='ارتفاع (سانتیمتر)',
- font=dict(family='Vazirmatn', size=16),
- hovermode='x unified',
- template='plotly_white',
- height=500,
- plot_bgcolor='#f5faff',
- paper_bgcolor='#f5faff'
+ title="Temperature Forecast",
+ xaxis_title="Date",
+ yaxis_title="Temperature (°C)",
+ legend_title="Temperature"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # Plot humidity forecast
+ fig = px.line(
+ forecast_df,
+ x='date',
+ y='humidity',
+ title="Humidity Forecast",
+ labels={"date": "Date", "humidity": "Humidity (%)"},
+ markers=True
)
- st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # Plot wind speed forecast
+ fig = px.line(
+ forecast_df,
+ x='date',
+ y='wind_speed',
+ title="Wind Speed Forecast",
+ labels={"date": "Date", "wind_speed": "Wind Speed (m/s)"},
+ markers=True
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
else:
- st.warning("دادههای کافی برای پیشبینی وجود ندارد.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
-
-# Report Generation Page
-elif selected == "گزارشگیری":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("## گزارشگیری", unsafe_allow_html=True)
+ st.warning("Weather data could not be retrieved. Please check your internet connection.")
- report_week = st.selectbox(
- "انتخاب هفته برای گزارش",
- options=[str(i) for i in range(1, 23)],
- help="هفته موردنظر را برای گزارش انتخاب کنید"
- )
- report_day = st.selectbox(
- "انتخاب روز برای گزارش",
- options=day_df['Day'].unique().tolist(),
- help="روز موردنظر را برای گزارش انتخاب کنید"
- )
-
- report_df = st.session_state.heights_df[
- (st.session_state.heights_df['Week'] == int(report_week)) &
- (st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID']))
- ]
-
- if not report_df.empty:
- st.markdown(f"### گزارش هفته {report_week} - روز {report_day}")
- st.dataframe(report_df, use_container_width=True)
-
- csv = report_df.to_csv(index=False).encode('utf-8')
- st.download_button(
- label="دانلود گزارش (CSV)",
- data=csv,
- file_name=f"report_week_{report_week}_day_{report_day}.csv",
- mime="text/csv",
- key="download_report",
- help="گزارش را به صورت فایل CSV دانلود کنید"
- )
-
- st_lottie(lottie_report, height=250, key="report_animation", speed=1.2)
- else:
- st.warning(f"دادهای برای هفته {report_week} و روز {report_day} یافت نشد.", icon="⚠️")
- st.markdown('
', unsafe_allow_html=True)
-
-# Settings Page
-elif selected == "تنظیمات":
- st.markdown('', unsafe_allow_html=True)
- st.markdown("## تنظیمات سامانه", unsafe_allow_html=True)
-
- st.markdown("""
-
-
تنظیمات پیشرفته
-
در این بخش میتوانید تنظیمات کلی سامانه، از جمله بهروزرسانی دادهها و پیکربندیهای ظاهری را مدیریت کنید.
-
- """, unsafe_allow_html=True)
-
- st.markdown("### بهروزرسانی دادهها")
-
- if st.button("بارگذاری مجدد دادهها", type="primary", use_container_width=True, help="دادهها را دوباره بارگذاری کنید"):
- st.session_state.heights_df = load_farm_data()
- st.success("دادهها با موفقیت بهروزرسانی شدند.", icon="✅")
-
- st.markdown("### تنظیمات ظاهری")
-
- theme = st.radio(
- "انتخاب تم",
- options=["سبز براق (پیشفرض)", "آبی کریستالی", "طلایی کلاسیک"],
- format_func=lambda x: x,
- help="تم موردنظر را برای رابط کاربری انتخاب کنید"
- )
-
- if theme == "آبی کریستالی":
- st.markdown("""
-
- """, unsafe_allow_html=True)
- elif theme == "طلایی کلاسیک":
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- st.markdown("### اطلاعات تماس")
- st.markdown("""
-
-
برای پشتیبانی یا مشکلات فنی، با ما تماس بگیرید:
-
ایمیل: support@dehkhoda.com
-
تلفن: +98 21 12345678
-
- """, unsafe_allow_html=True)
- st.markdown('
', unsafe_allow_html=True)
+ with tab4:
+ st.subheader("Weekly Report")
+
+ # Get the last 7 days of data
+ last_7_days = datetime.now() - timedelta(days=7)
+ last_7_days_str = last_7_days.strftime('%Y-%m-%d')
+
+ with st.spinner("Generating weekly report..."):
+ # Get weekly time series
+ weekly_data = get_time_series(
+ farm_lat,
+ farm_lon,
+ last_7_days_str,
+ end_date_str
+ )
+
+ if not weekly_data.empty:
+ # Weekly summary
+ weekly_avg = weekly_data.mean()
+
+ col1, col2, col3, col4 = st.columns(4)
+
+ with col1:
+ st.metric("Avg NDVI", f"{weekly_avg['ndvi']:.3f}")
+
+ with col2:
+ st.metric("Avg NDWI", f"{weekly_avg['ndwi']:.3f}")
+
+ with col3:
+ st.metric("Avg LAI", f"{weekly_avg['lai']:.3f}")
+
+ with col4:
+ st.metric("Avg CHL", f"{weekly_avg['chl']:.3f}")
+
+ # Plot weekly trends
+ st.subheader("Weekly Trends")
+
+ fig = go.Figure()
+
+ fig.add_trace(go.Scatter(
+ x=weekly_data['date'],
+ y=weekly_data['ndvi'],
+ mode='lines+markers',
+ name='NDVI'
+ ))
+
+ fig.add_trace(go.Scatter(
+ x=weekly_data['date'],
+ y=weekly_data['lai'],
+ mode='lines+markers',
+ name='LAI'
+ ))
+
+ fig.update_layout(
+ title=f"NDVI and LAI Weekly Trends for {selected_farm}",
+ xaxis_title="Date",
+ yaxis_title="Index Value",
+ legend_title="Index"
+ )
+
+ st.plotly_chart(fig, use_container_width=True)
+
+ # Weekly change
+ if len(weekly_data) > 1:
+ first_day = weekly_data.iloc[0]
+ last_day = weekly_data.iloc[-1]
+
+ ndvi_change = ((last_day['ndvi'] - first_day['ndvi']) / first_day['ndvi']) * 100 if first_day['ndvi'] != 0 else 0
+ lai_change = ((last_day['lai'] - first_day['lai']) / first_day['lai']) * 100 if first_day['lai'] != 0 else 0
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.metric("NDVI Change", f"{ndvi_change:.2f}%", delta=f"{ndvi_change:.2f}%")
+
+ with col2:
+ st.metric("LAI Change", f"{lai_change:.2f}%", delta=f"{lai_change:.2f}%")
+
+ # Growth status assessment
+ st.subheader("Growth Status Assessment")
+
+ if ndvi_change > 5 and lai_change > 5:
+ st.success("✅ Healthy Growth: The crop is showing good growth patterns with increasing vegetation indices.")
+ elif ndvi_change > 0 and lai_change > 0:
+ st.info("ℹ️ Moderate Growth: The crop is growing, but at a slower rate than expected.")
+ elif ndvi_change < 0 or lai_change < 0:
+ st.warning("⚠️ Growth Concern: The crop is showing signs of stress or declining health.")
+
+ # Recommendations
+ st.subheader("Recommendations")
+
+ if ndvi_change < 0:
+ st.warning("Consider checking for pest infestations or nutrient deficiencies.")
+
+ if ndwi_val < -0.3:
+ st.warning("Water stress detected. Consider irrigation schedule adjustments.")
+
+ if lai_val < 1.5:
+ st.warning("Low leaf area index. Investigate possible causes for poor canopy development.")
+
+ if chl_val < 1.5:
+ st.warning("Low chlorophyll content. Consider nitrogen fertilization.")
+ else:
+ st.warning("No data available for the last 7 days. Check your satellite data availability.")
+else:
+ st.error("Failed to initialize Google Earth Engine. Please check your credentials.")
-# Footer
-st.markdown("""
-
-""", unsafe_allow_html=True)
\ No newline at end of file