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