diff --git a/NOI Hackathon 2025 - Gruppo FOS.pdf b/NOI Hackathon 2025 - Gruppo FOS.pdf new file mode 100644 index 0000000..f7a1c79 Binary files /dev/null and b/NOI Hackathon 2025 - Gruppo FOS.pdf differ diff --git a/PlantDashboard/.DS_Store b/PlantDashboard/.DS_Store new file mode 100644 index 0000000..b81a9ba Binary files /dev/null and b/PlantDashboard/.DS_Store differ diff --git a/PlantDashboard/__pycache__/data_handler.cpython-313.pyc b/PlantDashboard/__pycache__/data_handler.cpython-313.pyc new file mode 100644 index 0000000..e3f3ada Binary files /dev/null and b/PlantDashboard/__pycache__/data_handler.cpython-313.pyc differ diff --git a/PlantDashboard/__pycache__/image_generator.cpython-313.pyc b/PlantDashboard/__pycache__/image_generator.cpython-313.pyc new file mode 100644 index 0000000..96656fd Binary files /dev/null and b/PlantDashboard/__pycache__/image_generator.cpython-313.pyc differ diff --git a/PlantDashboard/__pycache__/plant_model.cpython-313.pyc b/PlantDashboard/__pycache__/plant_model.cpython-313.pyc new file mode 100644 index 0000000..c466b88 Binary files /dev/null and b/PlantDashboard/__pycache__/plant_model.cpython-313.pyc differ diff --git a/PlantDashboard/data_handler.py b/PlantDashboard/data_handler.py new file mode 100644 index 0000000..d23c57a --- /dev/null +++ b/PlantDashboard/data_handler.py @@ -0,0 +1,133 @@ +import pandas as pd +import json +import csv +import os +from datetime import datetime + +class DataHandler: + def __init__(self): + self.data_directory = "plant_data" + self.ensure_data_directory() + + def ensure_data_directory(self): + """Ensure the data directory exists""" + if not os.path.exists(self.data_directory): + os.makedirs(self.data_directory) + + def save_prediction_data(self, parameters, prediction, filename=None): + """Save prediction data to CSV""" + if filename is None: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"prediction_{timestamp}.csv" + + filepath = os.path.join(self.data_directory, filename) + + # Combine parameters and prediction results + data = {**parameters} + data.update({ + 'final_height': prediction['final_height'], + 'growth_rate': prediction['growth_rate'], + 'health_score': prediction['health_score'], + 'optimal_conditions': prediction['optimal_conditions'], + 'yield': prediction['yield'], + 'timestamp': datetime.now().isoformat() + }) + + # Convert to DataFrame + df = pd.DataFrame([data]) + + try: + df.to_csv(filepath, index=False) + return filepath + except Exception as e: + print(f"Error saving data: {e}") + return None + + def load_historical_data(self): + """Load all historical prediction data""" + data_files = [f for f in os.listdir(self.data_directory) if f.endswith('.csv')] + + if not data_files: + return pd.DataFrame() + + all_data = [] + for file in data_files: + filepath = os.path.join(self.data_directory, file) + try: + df = pd.read_csv(filepath) + all_data.append(df) + except Exception as e: + print(f"Error loading {file}: {e}") + + if all_data: + return pd.concat(all_data, ignore_index=True) + else: + return pd.DataFrame() + + def export_to_json(self, data, filename): + """Export data to JSON format""" + filepath = os.path.join(self.data_directory, filename) + + try: + with open(filepath, 'w') as f: + json.dump(data, f, indent=2, default=str) + return filepath + except Exception as e: + print(f"Error exporting to JSON: {e}") + return None + + def import_from_json(self, filename): + """Import data from JSON format""" + filepath = os.path.join(self.data_directory, filename) + + try: + with open(filepath, 'r') as f: + data = json.load(f) + return data + except Exception as e: + print(f"Error importing from JSON: {e}") + return None + + def get_plant_statistics(self, plant_type=None): + """Get statistics for plant predictions""" + df = self.load_historical_data() + + if df.empty: + return {} + + if plant_type: + df = df[df['plant_type'] == plant_type] + + if df.empty: + return {} + + stats = { + 'total_predictions': len(df), + 'avg_final_height': df['final_height'].mean(), + 'avg_health_score': df['health_score'].mean(), + 'avg_growth_rate': df['growth_rate'].mean(), + 'best_conditions': df['optimal_conditions'].max(), + 'worst_conditions': df['optimal_conditions'].min() + } + + return stats + + def create_comparison_report(self, plant_types): + """Create a comparison report for different plant types""" + df = self.load_historical_data() + + if df.empty: + return {} + + report = {} + for plant_type in plant_types: + plant_data = df[df['plant_type'] == plant_type] + if not plant_data.empty: + report[plant_type] = { + 'count': len(plant_data), + 'avg_height': plant_data['final_height'].mean(), + 'avg_health': plant_data['health_score'].mean(), + 'success_rate': len(plant_data[plant_data['health_score'] > 70]) / len(plant_data) * 100 + } + + return report diff --git a/PlantDashboard/demo_launcher.py b/PlantDashboard/demo_launcher.py new file mode 100644 index 0000000..00db47a --- /dev/null +++ b/PlantDashboard/demo_launcher.py @@ -0,0 +1,77 @@ +""" +Plant Growth Graphics Demo Launcher +Simple version focusing on visual capabilities +""" + +import sys +import os + +# Add the current directory to the Python path +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, current_dir) + +def check_requirements(): + """Check if required packages are available""" + required_packages = { + 'tkinter': 'tkinter', + 'matplotlib': 'matplotlib', + 'seaborn': 'seaborn', + 'numpy': 'numpy', + 'PIL': 'Pillow' + } + + missing_packages = [] + + for package, pip_name in required_packages.items(): + try: + __import__(package) + print(f"โœ… {package} - OK") + except ImportError: + print(f"โŒ {package} - Missing") + missing_packages.append(pip_name) + + if missing_packages: + print(f"\n๐Ÿ“ฆ Install missing packages with:") + print(f"pip install {' '.join(missing_packages)}") + return False + + return True + +def main(): + print("๐ŸŽจ Plant Growth Graphics Demo") + print("=" * 40) + print("Checking requirements...") + + if not check_requirements(): + print("\nโŒ Please install missing packages first!") + return + + print("\n๐Ÿš€ Starting graphics demo...") + + try: + from graphics_demo import PlantGrowthGraphicsDemo + import tkinter as tk + + root = tk.Tk() + app = PlantGrowthGraphicsDemo(root) + + print("โœ… Graphics demo ready!") + print("\n๐ŸŽฎ Demo Features:") + print(" โ€ข Real-time parameter visualization") + print(" โ€ข Interactive plant growth charts") + print(" โ€ข Dynamic plant evolution images") + print(" โ€ข Parameter heatmaps and 3D plots") + print(" โ€ข Live health indicators") + print(" โ€ข Animated growth sequences") + print("\n๐ŸŽ›๏ธ Try changing parameters with the sliders!") + print("๐ŸŽฌ Click 'Animate Growth' for dynamic effects!") + + root.mainloop() + + except Exception as e: + print(f"โŒ Error starting demo: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() diff --git a/PlantDashboard/graphics_demo.py b/PlantDashboard/graphics_demo.py new file mode 100644 index 0000000..c452f07 --- /dev/null +++ b/PlantDashboard/graphics_demo.py @@ -0,0 +1,694 @@ +import tkinter as tk +from tkinter import ttk +import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +import seaborn as sns +import numpy as np +from PIL import Image, ImageDraw, ImageFont +import random +import math +from datetime import datetime + +class PlantGrowthGraphicsDemo: + def __init__(self, root): + self.root = root + self.root.title("๐ŸŒฑ Plant Growth Graphics Demo") + self.root.geometry("1400x900") + self.root.configure(bg='#f0f0f0') + + # Demo variables + self.current_plant = tk.StringVar(value="tomato") + self.ambient_mode = tk.StringVar(value="controlled") + + # Environmental parameters for demo + self.env_params = { + 'temperature': tk.DoubleVar(value=22.0), + 'humidity': tk.DoubleVar(value=65.0), + 'soil_acidity': tk.DoubleVar(value=6.5), + 'pressure': tk.DoubleVar(value=1013.25), + 'brightness': tk.DoubleVar(value=50.0), + 'nutrients': tk.DoubleVar(value=75.0), + 'water': tk.DoubleVar(value=80.0), + 'co2': tk.DoubleVar(value=40.0) + } + + # Plant colors for visualization + self.plant_colors = { + 'tomato': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF6347'}, + 'basil': {'stem': '#228B22', 'leaf': '#90EE90', 'fruit': '#FFFFFF'}, + 'mint': {'stem': '#228B22', 'leaf': '#98FB98', 'fruit': '#FFFFFF'}, + 'lettuce': {'stem': '#228B22', 'leaf': '#ADFF2F', 'fruit': '#FFFFFF'}, + 'rosemary': {'stem': '#8B4513', 'leaf': '#556B2F', 'fruit': '#FFFFFF'}, + 'strawberry': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF1493'} + } + + self.setup_ui() + self.update_all_graphics() + + # Auto-update timer for dynamic effects + self.auto_update() + + def setup_ui(self): + # Main container + main_frame = ttk.Frame(self.root, padding="10") + main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # Configure grid weights + self.root.columnconfigure(0, weight=1) + self.root.rowconfigure(0, weight=1) + main_frame.columnconfigure(1, weight=1) + main_frame.rowconfigure(1, weight=1) + + # Title with animated effect + self.title_label = ttk.Label(main_frame, text="๐ŸŒฑ Plant Growth Graphics Demo", + font=('Arial', 16, 'bold')) + self.title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20)) + + # Left panel - Controls + self.setup_control_panel(main_frame) + + # Center panel - Main Visualization + self.setup_main_visualization(main_frame) + + # Right panel - Additional Graphics + self.setup_additional_graphics(main_frame) + + def setup_control_panel(self, parent): + control_frame = ttk.LabelFrame(parent, text="๐ŸŽ›๏ธ Graphics Controls", padding="10") + control_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10)) + + # Plant selection with immediate visual feedback + ttk.Label(control_frame, text="Plant Type:", font=('Arial', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=5) + plant_combo = ttk.Combobox(control_frame, textvariable=self.current_plant, + values=["tomato", "basil", "mint", "lettuce", "rosemary", "strawberry"]) + plant_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5) + plant_combo.bind('<>', self.on_plant_change) + + # Ambient mode with visual indicators + ttk.Label(control_frame, text="Visual Mode:", font=('Arial', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=5) + mode_frame = ttk.Frame(control_frame) + mode_frame.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5) + + ttk.Radiobutton(mode_frame, text="๐ŸŽฏ Controlled", variable=self.ambient_mode, + value="controlled", command=self.update_all_graphics).pack(side=tk.LEFT) + ttk.Radiobutton(mode_frame, text="๐ŸŒŠ Dynamic", variable=self.ambient_mode, + value="semi-controlled", command=self.update_all_graphics).pack(side=tk.LEFT) + ttk.Radiobutton(mode_frame, text="๐ŸŒช๏ธ Chaotic", variable=self.ambient_mode, + value="open", command=self.update_all_graphics).pack(side=tk.LEFT) + + # Visual effects controls + ttk.Separator(control_frame, orient='horizontal').grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=10) + + ttk.Label(control_frame, text="๐ŸŽจ Visual Parameters:", + font=('Arial', 10, 'bold')).grid(row=3, column=0, columnspan=2, pady=(10, 5)) + + # Parameter sliders with real-time visual updates + param_labels = { + 'temperature': '๐ŸŒก๏ธ Temperature', + 'humidity': '๐Ÿ’ง Humidity', + 'soil_acidity': '๐Ÿงช Soil pH', + 'pressure': '๐ŸŒฌ๏ธ Pressure', + 'brightness': 'โ˜€๏ธ Light', + 'nutrients': '๐ŸŒฟ Nutrients', + 'water': '๐Ÿ’ฆ Water', + 'co2': '๐Ÿซง CO2' + } + + row = 4 + for param, label in param_labels.items(): + ttk.Label(control_frame, text=label).grid(row=row, column=0, sticky=tk.W, pady=2) + + # Create frame for slider and value display + slider_frame = ttk.Frame(control_frame) + slider_frame.grid(row=row, column=1, sticky=(tk.W, tk.E), pady=2) + + scale = ttk.Scale(slider_frame, from_=0, to=100, + variable=self.env_params[param], orient=tk.HORIZONTAL, + command=lambda x, p=param: self.on_param_change(p)) + scale.pack(side=tk.LEFT, fill=tk.X, expand=True) + + # Value display + value_label = ttk.Label(slider_frame, text="0", width=4) + value_label.pack(side=tk.RIGHT) + + # Store reference for updates + setattr(self, f"{param}_label", value_label) + + row += 1 + + # Action buttons with visual feedback + button_frame = ttk.Frame(control_frame) + button_frame.grid(row=row, column=0, columnspan=2, pady=20) + + ttk.Button(button_frame, text="๐ŸŽฌ Animate Growth", + command=self.animate_growth).pack(fill=tk.X, pady=2) + ttk.Button(button_frame, text="๐ŸŽฒ Randomize", + command=self.randomize_parameters).pack(fill=tk.X, pady=2) + ttk.Button(button_frame, text="๐Ÿ”„ Reset Demo", + command=self.reset_demo).pack(fill=tk.X, pady=2) + + control_frame.columnconfigure(1, weight=1) + + def setup_main_visualization(self, parent): + viz_frame = ttk.LabelFrame(parent, text="๐Ÿ“Š Main Visualization", padding="10") + viz_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5) + + # Notebook for different visualization modes + self.notebook = ttk.Notebook(viz_frame) + self.notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # Growth Chart Tab + self.setup_growth_chart_tab() + + # Plant Evolution Tab + self.setup_plant_evolution_tab() + + # Parameter Heatmap Tab + self.setup_heatmap_tab() + + # 3D Visualization Tab + self.setup_3d_visualization_tab() + + viz_frame.columnconfigure(0, weight=1) + viz_frame.rowconfigure(0, weight=1) + + def setup_growth_chart_tab(self): + chart_frame = ttk.Frame(self.notebook) + self.notebook.add(chart_frame, text="๐Ÿ“ˆ Growth Chart") + + # Create matplotlib figure + self.fig, self.ax = plt.subplots(figsize=(8, 6)) + self.fig.patch.set_facecolor('#f0f0f0') + + self.canvas = FigureCanvasTkAgg(self.fig, chart_frame) + self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + + def setup_plant_evolution_tab(self): + evolution_frame = ttk.Frame(self.notebook) + self.notebook.add(evolution_frame, text="๐ŸŒฑ Plant Evolution") + + # Create scrollable frame for plant images + canvas_frame = tk.Canvas(evolution_frame, bg='white') + scrollbar = ttk.Scrollbar(evolution_frame, orient="vertical", command=canvas_frame.yview) + self.scrollable_frame = ttk.Frame(canvas_frame) + + self.scrollable_frame.bind( + "", + lambda e: canvas_frame.configure(scrollregion=canvas_frame.bbox("all")) + ) + + canvas_frame.create_window((0, 0), window=self.scrollable_frame, anchor="nw") + canvas_frame.configure(yscrollcommand=scrollbar.set) + + canvas_frame.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + # Plant evolution display + self.plant_images_frame = ttk.Frame(self.scrollable_frame) + self.plant_images_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + def setup_heatmap_tab(self): + heatmap_frame = ttk.Frame(self.notebook) + self.notebook.add(heatmap_frame, text="๐Ÿ”ฅ Parameter Heatmap") + + # Create seaborn heatmap + self.heatmap_fig, self.heatmap_ax = plt.subplots(figsize=(8, 6)) + self.heatmap_fig.patch.set_facecolor('#f0f0f0') + + self.heatmap_canvas = FigureCanvasTkAgg(self.heatmap_fig, heatmap_frame) + self.heatmap_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + + def setup_3d_visualization_tab(self): + viz_3d_frame = ttk.Frame(self.notebook) + self.notebook.add(viz_3d_frame, text="๐ŸŽฏ 3D Analysis") + + # Create 3D plot + self.fig_3d = plt.figure(figsize=(8, 6)) + self.fig_3d.patch.set_facecolor('#f0f0f0') + self.ax_3d = self.fig_3d.add_subplot(111, projection='3d') + + self.canvas_3d = FigureCanvasTkAgg(self.fig_3d, viz_3d_frame) + self.canvas_3d.get_tk_widget().pack(fill=tk.BOTH, expand=True) + + def setup_additional_graphics(self, parent): + additional_frame = ttk.LabelFrame(parent, text="๐Ÿ“‹ Live Stats & Info", padding="10") + additional_frame.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0)) + + # Real-time statistics display + stats_frame = ttk.LabelFrame(additional_frame, text="๐Ÿ“Š Live Statistics", padding="5") + stats_frame.pack(fill=tk.X, pady=(0, 10)) + + self.stats_text = tk.Text(stats_frame, height=8, width=25, wrap=tk.WORD, + font=('Courier', 9), bg='#f8f9fa') + self.stats_text.pack(fill=tk.BOTH, expand=True) + + # Visual health indicator + health_frame = ttk.LabelFrame(additional_frame, text="๐Ÿฅ Plant Health", padding="5") + health_frame.pack(fill=tk.X, pady=(0, 10)) + + self.health_canvas = tk.Canvas(health_frame, height=100, bg='white') + self.health_canvas.pack(fill=tk.X) + + # Parameter radar chart + radar_frame = ttk.LabelFrame(additional_frame, text="๐ŸŽฏ Parameter Radar", padding="5") + radar_frame.pack(fill=tk.BOTH, expand=True) + + self.radar_fig, self.radar_ax = plt.subplots(figsize=(4, 4), subplot_kw=dict(projection='polar')) + self.radar_fig.patch.set_facecolor('#f0f0f0') + + self.radar_canvas = FigureCanvasTkAgg(self.radar_fig, radar_frame) + self.radar_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) + + def on_plant_change(self, event=None): + self.update_all_graphics() + + def on_param_change(self, param): + # Update value display + value = self.env_params[param].get() + label = getattr(self, f"{param}_label") + label.config(text=f"{value:.1f}") + + # Update graphics in real-time + self.update_all_graphics() + + def update_all_graphics(self): + self.update_growth_chart() + self.update_plant_evolution() + self.update_parameter_heatmap() + self.update_3d_visualization() + self.update_statistics() + self.update_health_indicator() + self.update_radar_chart() + + def update_growth_chart(self): + self.ax.clear() + + # Generate mock growth data based on parameters + days = np.arange(0, 100, 1) + + # Base growth influenced by parameters + temp_factor = self.env_params['temperature'].get() / 100 + water_factor = self.env_params['water'].get() / 100 + light_factor = self.env_params['brightness'].get() / 100 + + # Create realistic growth curve + growth_rate = (temp_factor + water_factor + light_factor) / 3 + + if self.ambient_mode.get() == "open": + # Add chaos/randomness + noise = np.random.normal(0, 0.1, len(days)) + heights = np.cumsum(np.maximum(0, growth_rate + noise)) * 2 + elif self.ambient_mode.get() == "semi-controlled": + # Add some variation + noise = np.random.normal(0, 0.05, len(days)) + heights = np.cumsum(np.maximum(0, growth_rate + noise)) * 2 + else: + # Controlled growth + heights = np.cumsum([growth_rate] * len(days)) * 2 + + # Apply plant-specific characteristics + plant_multipliers = { + 'tomato': 1.5, 'basil': 0.8, 'mint': 0.6, + 'lettuce': 0.4, 'rosemary': 1.2, 'strawberry': 0.5 + } + + multiplier = plant_multipliers.get(self.current_plant.get(), 1.0) + heights = heights * multiplier + + # Plot with plant-specific colors + colors = self.plant_colors[self.current_plant.get()] + self.ax.plot(days, heights, color=colors['leaf'], linewidth=2, marker='o', markersize=2) + + # Add growth phases with different colors + phase_colors = ['lightblue', 'lightgreen', 'lightyellow', 'lightcoral'] + phase_names = ['Germination', 'Seedling', 'Vegetative', 'Mature'] + + for i, (color, name) in enumerate(zip(phase_colors, phase_names)): + start_day = i * 25 + end_day = (i + 1) * 25 + if end_day <= len(days): + self.ax.axvspan(start_day, end_day, alpha=0.3, color=color, label=name) + + self.ax.set_xlabel('Days') + self.ax.set_ylabel('Height (cm)') + self.ax.set_title(f'{self.current_plant.get().title()} Growth Simulation') + self.ax.grid(True, alpha=0.3) + self.ax.legend(loc='upper left') + + # Add current parameter indicators + current_day = int(self.env_params['temperature'].get()) + if current_day < len(heights): + self.ax.axvline(current_day, color='red', linestyle='--', alpha=0.7, label='Current Day') + self.ax.plot(current_day, heights[current_day], 'ro', markersize=8) + + self.canvas.draw() + + def update_plant_evolution(self): + # Clear previous images + for widget in self.plant_images_frame.winfo_children(): + widget.destroy() + + # Generate evolution stages + stages = ['Seed', 'Sprout', 'Young', 'Mature', 'Full Growth'] + + for i, stage in enumerate(stages): + stage_frame = ttk.Frame(self.plant_images_frame) + stage_frame.pack(fill=tk.X, pady=5) + + # Stage label + ttk.Label(stage_frame, text=f"Stage {i+1}: {stage}", + font=('Arial', 10, 'bold')).pack() + + # Generate plant image + plant_image = self.generate_plant_stage_image(i+1) + + # Convert to PhotoImage and display + photo = tk.PhotoImage(data=self.pil_to_tk_data(plant_image)) + image_label = tk.Label(stage_frame, image=photo) + image_label.image = photo # Keep reference + image_label.pack() + + def generate_plant_stage_image(self, stage): + """Generate a plant image for a specific growth stage""" + img = Image.new('RGB', (200, 150), color='#87CEEB') # Sky blue + draw = ImageDraw.Draw(img) + + # Draw ground + ground_y = 130 + draw.rectangle([0, ground_y, 200, 150], fill='#8B4513') # Brown ground + + # Get plant colors + colors = self.plant_colors[self.current_plant.get()] + + # Calculate plant size based on stage and parameters + health_factor = (sum(param.get() for param in self.env_params.values()) / len(self.env_params)) / 100 + plant_height = stage * 15 * health_factor + plant_width = plant_height * 0.6 + + center_x = 100 + base_y = ground_y + top_y = base_y - plant_height + + # Draw stem + stem_width = max(2, int(plant_height * 0.1)) + draw.rectangle([center_x - stem_width//2, int(top_y), + center_x + stem_width//2, base_y], fill=colors['stem']) + + # Draw leaves + num_leaves = min(stage * 2, 8) + for i in range(num_leaves): + leaf_y = base_y - (i + 1) * (plant_height / (num_leaves + 1)) + side = 1 if i % 2 == 0 else -1 + leaf_x = center_x + side * (plant_width * 0.3) + leaf_size = plant_width * 0.2 + + # Draw leaf based on plant type + if self.current_plant.get() == 'lettuce': + # Broad leaves + draw.ellipse([leaf_x - leaf_size, leaf_y - leaf_size//2, + leaf_x + leaf_size, leaf_y + leaf_size//2], fill=colors['leaf']) + else: + # Regular leaves + draw.ellipse([leaf_x - leaf_size//2, leaf_y - leaf_size//3, + leaf_x + leaf_size//2, leaf_y + leaf_size//3], fill=colors['leaf']) + + # Draw fruits for mature stages + if stage >= 4 and self.current_plant.get() in ['tomato', 'strawberry']: + fruit_color = colors['fruit'] + for i in range(min(stage - 3, 3)): + fruit_x = center_x + random.randint(-15, 15) + fruit_y = int(top_y + random.randint(10, int(plant_height//2))) + fruit_size = 5 + stage + draw.ellipse([fruit_x - fruit_size, fruit_y - fruit_size, + fruit_x + fruit_size, fruit_y + fruit_size], fill=fruit_color) + + # Add stage indicator + draw.text((5, 5), f"Stage {stage}", fill='black') + + return img + + def pil_to_tk_data(self, pil_image): + """Convert PIL image to tkinter PhotoImage data""" + import io + import base64 + + # Convert to PNG bytes + buffer = io.BytesIO() + pil_image.save(buffer, format='PNG') + + # Encode to base64 + img_data = base64.b64encode(buffer.getvalue()) + + return img_data + + def update_parameter_heatmap(self): + self.heatmap_ax.clear() + + # Create parameter correlation matrix + param_names = list(self.env_params.keys()) + param_values = [param.get() for param in self.env_params.values()] + + # Create a correlation-like matrix for visualization + matrix = np.zeros((len(param_names), len(param_names))) + + for i, val_i in enumerate(param_values): + for j, val_j in enumerate(param_values): + if i == j: + matrix[i][j] = val_i + else: + # Create interesting correlations + correlation = abs(val_i - val_j) / 100 + matrix[i][j] = correlation * 50 + + # Create heatmap + sns.heatmap(matrix, annot=True, fmt='.1f', cmap='RdYlGn', + xticklabels=[name.replace('_', ' ').title() for name in param_names], + yticklabels=[name.replace('_', ' ').title() for name in param_names], + ax=self.heatmap_ax, cbar_kws={'label': 'Parameter Intensity'}) + + self.heatmap_ax.set_title(f'{self.current_plant.get().title()} Parameter Heatmap') + plt.setp(self.heatmap_ax.get_xticklabels(), rotation=45, ha='right') + plt.setp(self.heatmap_ax.get_yticklabels(), rotation=0) + + self.heatmap_canvas.draw() + + def update_3d_visualization(self): + self.ax_3d.clear() + + # Create 3D scatter plot of parameters + temp = self.env_params['temperature'].get() + humidity = self.env_params['humidity'].get() + light = self.env_params['brightness'].get() + + # Generate some sample data points around current parameters + n_points = 50 + temps = np.random.normal(temp, 5, n_points) + humids = np.random.normal(humidity, 5, n_points) + lights = np.random.normal(light, 5, n_points) + + # Color points based on "health" (distance from optimal) + optimal_temp, optimal_humid, optimal_light = 25, 60, 50 + distances = np.sqrt((temps - optimal_temp)**2 + + (humids - optimal_humid)**2 + + (lights - optimal_light)**2) + + colors = plt.cm.RdYlGn_r(distances / distances.max()) + + scatter = self.ax_3d.scatter(temps, humids, lights, c=colors, s=50, alpha=0.7) + + # Highlight current point + self.ax_3d.scatter([temp], [humidity], [light], c='red', s=200, marker='*') + + self.ax_3d.set_xlabel('Temperature (ยฐC)') + self.ax_3d.set_ylabel('Humidity (%)') + self.ax_3d.set_zlabel('Light Intensity') + self.ax_3d.set_title(f'3D Parameter Space - {self.current_plant.get().title()}') + + self.canvas_3d.draw() + + def update_statistics(self): + self.stats_text.delete(1.0, tk.END) + + # Calculate mock statistics + params = {name: param.get() for name, param in self.env_params.items()} + + avg_param = sum(params.values()) / len(params) + health_score = min(100, avg_param * 1.2) + growth_rate = health_score / 20 + + stats_text = f"""๐ŸŒฑ PLANT STATISTICS +{'='*25} + +Plant Type: {self.current_plant.get().title()} +Mode: {self.ambient_mode.get().title()} + +๐Ÿ“Š Current Metrics: +Health Score: {health_score:.1f}% +Growth Rate: {growth_rate:.2f} cm/day +Avg Parameter: {avg_param:.1f} + +๐ŸŒก๏ธ Environment: +Temperature: {params['temperature']:.1f}ยฐC +Humidity: {params['humidity']:.1f}% +Soil pH: {params['soil_acidity']:.1f} +Light: {params['brightness']:.1f} lux + +๐Ÿ’ง Resources: +Water: {params['water']:.1f}% +Nutrients: {params['nutrients']:.1f}% +CO2: {params['co2']:.1f} ppm + +โฐ Updated: {datetime.now().strftime('%H:%M:%S')} +""" + + self.stats_text.insert(1.0, stats_text) + + def update_health_indicator(self): + self.health_canvas.delete("all") + + # Calculate health score + params = list(self.env_params.values()) + health_score = sum(param.get() for param in params) / len(params) + + # Draw health bar + bar_width = 180 + bar_height = 20 + x_start = 10 + y_start = 40 + + # Background + self.health_canvas.create_rectangle(x_start, y_start, + x_start + bar_width, y_start + bar_height, + fill='lightgray', outline='black') + + # Health bar + health_width = (health_score / 100) * bar_width + if health_score > 70: + color = 'green' + elif health_score > 40: + color = 'orange' + else: + color = 'red' + + self.health_canvas.create_rectangle(x_start, y_start, + x_start + health_width, y_start + bar_height, + fill=color, outline='') + + # Health text + self.health_canvas.create_text(100, 25, text=f"Health: {health_score:.1f}%", + font=('Arial', 12, 'bold')) + + # Status emoji + if health_score > 80: + emoji = "๐ŸŒŸ" + status = "Excellent" + elif health_score > 60: + emoji = "๐Ÿ˜Š" + status = "Good" + elif health_score > 40: + emoji = "๐Ÿ˜" + status = "Fair" + else: + emoji = "๐Ÿ˜Ÿ" + status = "Poor" + + self.health_canvas.create_text(100, 75, text=f"{emoji} {status}", + font=('Arial', 10)) + + def update_radar_chart(self): + self.radar_ax.clear() + + # Parameter names and values + param_names = ['Temp', 'Humid', 'pH', 'Press', 'Light', 'Nutri', 'Water', 'CO2'] + param_values = [param.get() for param in self.env_params.values()] + + # Number of variables + N = len(param_names) + + # Compute angle for each axis + angles = [n / float(N) * 2 * np.pi for n in range(N)] + angles += angles[:1] # Complete the circle + + # Add values + param_values += param_values[:1] # Complete the circle + + # Plot + self.radar_ax.plot(angles, param_values, 'o-', linewidth=2, + color=self.plant_colors[self.current_plant.get()]['leaf']) + self.radar_ax.fill(angles, param_values, alpha=0.25, + color=self.plant_colors[self.current_plant.get()]['leaf']) + + # Add labels + self.radar_ax.set_xticks(angles[:-1]) + self.radar_ax.set_xticklabels(param_names) + self.radar_ax.set_ylim(0, 100) + self.radar_ax.set_title(f'{self.current_plant.get().title()} Parameters', + pad=20, fontsize=10) + self.radar_ax.grid(True) + + self.radar_canvas.draw() + + def animate_growth(self): + """Animate parameter changes to show dynamic growth""" + def animate_step(step): + if step < 50: # 50 animation steps + # Gradually change parameters + for param in self.env_params.values(): + current = param.get() + target = random.uniform(20, 80) + new_value = current + (target - current) * 0.1 + param.set(new_value) + + self.update_all_graphics() + self.root.after(100, lambda: animate_step(step + 1)) + + animate_step(0) + + def randomize_parameters(self): + """Randomize all parameters for demo purposes""" + for param in self.env_params.values(): + param.set(random.uniform(10, 90)) + self.update_all_graphics() + + def reset_demo(self): + """Reset all parameters to default values""" + defaults = { + 'temperature': 22.0, + 'humidity': 65.0, + 'soil_acidity': 6.5, + 'pressure': 50.0, # Normalized for demo + 'brightness': 50.0, + 'nutrients': 75.0, + 'water': 80.0, + 'co2': 40.0 + } + + for param_name, default_value in defaults.items(): + self.env_params[param_name].set(default_value) + + self.current_plant.set("tomato") + self.ambient_mode.set("controlled") + self.update_all_graphics() + + def auto_update(self): + """Auto-update for dynamic effects""" + if self.ambient_mode.get() == "open": + # Add small random variations in open mode + for param in self.env_params.values(): + current = param.get() + variation = random.uniform(-1, 1) + new_value = max(0, min(100, current + variation)) + param.set(new_value) + + self.update_all_graphics() + + # Schedule next update + self.root.after(2000, self.auto_update) # Update every 2 seconds + +def main(): + root = tk.Tk() + app = PlantGrowthGraphicsDemo(root) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/PlantDashboard/image_generator.py b/PlantDashboard/image_generator.py new file mode 100644 index 0000000..2a025fe --- /dev/null +++ b/PlantDashboard/image_generator.py @@ -0,0 +1,239 @@ +from PIL import Image, ImageDraw, ImageFont +import random +import math +import os + +class ImageGenerator: + def __init__(self): + self.image_size = (400, 300) + self.plant_colors = { + 'tomato': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF6347'}, + 'basil': {'stem': '#228B22', 'leaf': '#90EE90', 'fruit': '#FFFFFF'}, + 'mint': {'stem': '#228B22', 'leaf': '#98FB98', 'fruit': '#FFFFFF'}, + 'lettuce': {'stem': '#228B22', 'leaf': '#ADFF2F', 'fruit': '#FFFFFF'}, + 'rosemary': {'stem': '#8B4513', 'leaf': '#556B2F', 'fruit': '#FFFFFF'}, + 'strawberry': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF1493'} + } + + def generate_evolution(self, plant_type, parameters, prediction, baseline_path=None): + """Generate a series of images showing plant evolution""" + growth_stages = prediction['growth_stages'] + health_score = prediction['health_score'] + + images = [] + + # Generate images for key growth stages + stage_indices = [0, len(growth_stages)//4, len(growth_stages)//2, + 3*len(growth_stages)//4, len(growth_stages)-1] + + for i, stage_idx in enumerate(stage_indices): + if stage_idx < len(growth_stages): + height = growth_stages[stage_idx] + image = self.generate_plant_image(plant_type, height, health_score, i+1) + images.append(image) + + return images + + def generate_plant_image(self, plant_type, height, health_score, stage): + """Generate a single plant image""" + img = Image.new('RGB', self.image_size, color='#87CEEB') # Sky blue background + draw = ImageDraw.Draw(img) + + # Draw ground + ground_y = self.image_size[1] - 50 + draw.rectangle([0, ground_y, self.image_size[0], self.image_size[1]], + fill='#8B4513') # Brown ground + + # Get plant colors (default to tomato since we removed plant type selection) + colors = self.plant_colors.get(plant_type, self.plant_colors['tomato']) + + # Calculate plant dimensions based on height and health + if stage == 0: # Initial/seed stage + plant_height = 5 # Very small initial plant + plant_width = 3 + else: + plant_height = min(height * 2, self.image_size[1] - 100) # Scale for display + plant_width = plant_height * 0.6 + + # Adjust colors based on health + health_factor = health_score / 100.0 + stem_color = self._adjust_color_health(colors['stem'], health_factor) + leaf_color = self._adjust_color_health(colors['leaf'], health_factor) + + # Plant center position + center_x = self.image_size[0] // 2 + base_y = ground_y + + if stage == 0: + # Draw seed/initial state + self._draw_seed(draw, center_x, base_y - 10, health_factor) + else: + # Draw stem + stem_width = max(2, int(plant_height * 0.05)) + stem_top_y = base_y - plant_height + draw.rectangle([center_x - stem_width//2, int(stem_top_y), + center_x + stem_width//2, base_y], fill=stem_color) + + # Draw leaves based on plant type and stage + self._draw_leaves(draw, plant_type, center_x, stem_top_y, base_y, + plant_width, leaf_color, stage) + + # Draw fruits/flowers if applicable + if stage >= 3 and plant_type in ['tomato', 'strawberry']: + self._draw_fruits(draw, plant_type, center_x, stem_top_y, base_y, + colors['fruit'], stage) + + # Add stage label + try: + font = ImageFont.load_default() + except: + font = None + + if stage == 0: + stage_text = f"Initial State - Seed" + else: + stage_text = f"Stage {stage} - Height: {height:.1f}cm" + + if font: + draw.text((10, 10), stage_text, fill='black', font=font) + else: + draw.text((10, 10), stage_text, fill='black') + + # Add health indicator + health_text = f"Health: {health_score:.1f}%" + health_color = 'green' if health_score > 70 else 'orange' if health_score > 40 else 'red' + if font: + draw.text((10, 30), health_text, fill=health_color, font=font) + else: + draw.text((10, 30), health_text, fill=health_color) + + return img + + def _draw_seed(self, draw, x, y, health_factor): + """Draw a seed for the initial state""" + seed_size = 8 + seed_color = '#8B4513' # Brown seed + + # Adjust seed color based on health + if health_factor > 0.7: + seed_color = '#654321' # Healthy brown + elif health_factor > 0.4: + seed_color = '#8B4513' # Normal brown + else: + seed_color = '#A0522D' # Pale brown + + # Draw seed + draw.ellipse([x - seed_size, y - seed_size//2, + x + seed_size, y + seed_size//2], fill=seed_color) + + # Draw small sprout if health is good + if health_factor > 0.5: + sprout_color = '#90EE90' + draw.line([x, y - seed_size//2, x, y - seed_size//2 - 5], + fill=sprout_color, width=2) + + def _draw_leaves(self, draw, plant_type, center_x, top_y, base_y, width, color, stage): + """Draw leaves based on plant type""" + plant_height = base_y - top_y + num_leaves = min(stage * 2, 8) # More leaves as plant grows + + for i in range(num_leaves): + # Calculate leaf position + y_pos = base_y - (i + 1) * (plant_height / (num_leaves + 1)) + side = 1 if i % 2 == 0 else -1 # Alternate sides + + leaf_x = center_x + side * (width * 0.3) + leaf_size = width * 0.2 * (1 + stage * 0.1) + + if plant_type == 'lettuce': + # Draw broad leaves for lettuce + self._draw_broad_leaf(draw, leaf_x, y_pos, leaf_size, color) + elif plant_type == 'rosemary': + # Draw needle-like leaves for rosemary + self._draw_needle_leaf(draw, leaf_x, y_pos, leaf_size, color) + else: + # Draw regular oval leaves (default for tomato) + self._draw_oval_leaf(draw, leaf_x, y_pos, leaf_size, color) + + def _draw_oval_leaf(self, draw, x, y, size, color): + """Draw an oval leaf""" + draw.ellipse([x - size//2, y - size//3, x + size//2, y + size//3], fill=color) + + def _draw_broad_leaf(self, draw, x, y, size, color): + """Draw a broad leaf for lettuce""" + points = [ + (x, y - size//2), + (x + size//2, y), + (x, y + size//2), + (x - size//2, y) + ] + draw.polygon(points, fill=color) + + def _draw_needle_leaf(self, draw, x, y, size, color): + """Draw needle-like leaves for rosemary""" + for i in range(3): + offset = (i - 1) * 3 + draw.line([x + offset, y - size//4, x + offset, y + size//4], + fill=color, width=2) + + def _draw_fruits(self, draw, plant_type, center_x, top_y, base_y, color, stage): + """Draw fruits based on plant type""" + if plant_type == 'tomato': + # Draw tomatoes + num_fruits = min(stage - 2, 4) + for i in range(num_fruits): + fruit_x = center_x + random.randint(-20, 20) + fruit_y = int(top_y + random.randint(10, (base_y - top_y) // 2)) + fruit_size = 8 + stage * 2 + draw.ellipse([fruit_x - fruit_size, fruit_y - fruit_size, + fruit_x + fruit_size, fruit_y + fruit_size], fill=color) + + elif plant_type == 'strawberry': + # Draw strawberries + num_fruits = min(stage - 2, 3) + for i in range(num_fruits): + fruit_x = center_x + random.randint(-15, 15) + fruit_y = base_y - random.randint(20, 40) + self._draw_strawberry(draw, fruit_x, fruit_y, color) + + def _draw_strawberry(self, draw, x, y, color): + """Draw a strawberry shape""" + # Draw strawberry body + points = [(x, y - 8), (x + 6, y), (x, y + 8), (x - 6, y)] + draw.polygon(points, fill=color) + + # Draw strawberry top (green) + draw.polygon([(x - 3, y - 8), (x, y - 12), (x + 3, y - 8)], fill='green') + + def _adjust_color_health(self, color_hex, health_factor): + """Adjust color based on plant health""" + # Convert hex to RGB + color_hex = color_hex.lstrip('#') + r, g, b = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4)) + + # Adjust brightness based on health + factor = 0.5 + health_factor * 0.5 # Range from 0.5 to 1.0 + r = int(r * factor) + g = int(g * factor) + b = int(b * factor) + + # Ensure values are within valid range + r = max(0, min(255, r)) + g = max(0, min(255, g)) + b = max(0, min(255, b)) + + return f'#{r:02x}{g:02x}{b:02x}' + + def save_evolution_sequence(self, images, filename): + """Save evolution images as separate files""" + if images: + try: + base_name = filename.rsplit('.', 1)[0] + for i, image in enumerate(images): + stage_filename = f"{base_name}_stage_{i+1}.png" + image.save(stage_filename) + return True + except Exception as e: + print(f"Error saving images: {e}") + return False + return False diff --git a/PlantDashboard/install_requirements.py b/PlantDashboard/install_requirements.py new file mode 100644 index 0000000..2f4c66e --- /dev/null +++ b/PlantDashboard/install_requirements.py @@ -0,0 +1,102 @@ +""" +Installation script for Plant Growth Forecasting Dashboard +(Without opencv-cv and seaborn dependencies) +""" + +import subprocess +import sys +import os + +def install_package(package): + """Install a package using pip""" + try: + subprocess.check_call([sys.executable, "-m", "pip", "install", package]) + return True + except subprocess.CalledProcessError: + return False + +def check_tkinter(): + """Check if tkinter is available""" + try: + import tkinter + return True + except ImportError: + return False + +def main(): + print("๐ŸŒฑ Plant Growth Forecasting Dashboard - Installation Script") + print("=" * 60) + + # Check Python version + if sys.version_info < (3.7, 0): + print("โŒ Error: Python 3.7 or higher is required") + print(f"Current version: {sys.version}") + sys.exit(1) + + print(f"โœ… Python version: {sys.version}") + + # Check tkinter availability + if not check_tkinter(): + print("โŒ tkinter is not available!") + print("Please install tkinter using your system package manager:") + print(" Ubuntu/Debian: sudo apt-get install python3-tk") + print(" CentOS/RHEL: sudo yum install tkinter") + print(" macOS: tkinter should be included with Python") + print(" Windows: tkinter should be included with Python") + sys.exit(1) + + print("โœ… tkinter is available") + + # Install requirements + requirements_file = os.path.join(os.path.dirname(__file__), "..", "requirements.txt") + + if not os.path.exists(requirements_file): + print("โŒ requirements.txt not found!") + sys.exit(1) + + print("\n๐Ÿ“ฆ Installing required packages...") + + try: + subprocess.check_call([ + sys.executable, "-m", "pip", "install", "-r", requirements_file + ]) + print("โœ… All packages installed successfully!") + except subprocess.CalledProcessError as e: + print(f"โŒ Error installing packages: {e}") + print("\nTrying to install core packages individually...") + + core_packages = [ + "matplotlib>=3.5.0", + "pandas>=1.3.0", + "numpy>=1.21.0", + "Pillow>=8.3.0", + "joblib>=1.1.0" + ] + + failed_packages = [] + for package in core_packages: + print(f"Installing {package}...") + if install_package(package): + print(f"โœ… {package} installed") + else: + print(f"โŒ Failed to install {package}") + failed_packages.append(package) + + if failed_packages: + print(f"\nโŒ Failed to install: {', '.join(failed_packages)}") + print("Please install these packages manually") + sys.exit(1) + + print("\n๐ŸŽ‰ Installation completed successfully!") + print("\nTo run the application:") + print(" python scripts/launcher.py") + print("\nOr run the main dashboard directly:") + print(" python scripts/main_dashboard.py") + + print("\n๐Ÿ“‹ Note: This version excludes opencv-cv and seaborn") + print(" โ€ข Heatmaps use matplotlib instead of seaborn") + print(" โ€ข Video functionality has been removed") + print(" โ€ข All other features remain fully functional") + +if __name__ == "__main__": + main() diff --git a/PlantDashboard/launcher.py b/PlantDashboard/launcher.py new file mode 100644 index 0000000..0aa6810 --- /dev/null +++ b/PlantDashboard/launcher.py @@ -0,0 +1,52 @@ +""" +Plant Growth Forecasting Dashboard Launcher +""" + +import sys +import os + +# Add the current directory to the Python path +current_dir = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, current_dir) + +try: + from main_dashboard import PlantGrowthDashboard + import tkinter as tk + + def main(): + print("๐ŸŒฑ Starting Plant Growth Forecasting Dashboard...") + print("Loading AI models and initializing interface...") + + root = tk.Tk() + + # Set application icon and styling + try: + root.iconname("Plant Growth Forecaster") + except: + pass + + app = PlantGrowthDashboard(root) + + print("โœ… Dashboard ready!") + print("Features available:") + print(" โ€ข AI-powered plant growth prediction") + print(" โ€ข Real-time parameter control") + print(" โ€ข Visual growth simulation") + print(" โ€ข Environmental analysis") + print(" โ€ข Data export/import") + print(" โ€ข Multiple ambient modes") + print(" โ€ข Plant evolution visualization") + + root.mainloop() + + if __name__ == "__main__": + main() + +except ImportError as e: + print(f"โŒ Error importing required modules: {e}") + print("Please ensure all required packages are installed:") + print(" pip install matplotlib pandas pillow numpy joblib") + sys.exit(1) +except Exception as e: + print(f"โŒ Error starting application: {e}") + sys.exit(1) diff --git a/PlantDashboard/main_dashboard.py b/PlantDashboard/main_dashboard.py new file mode 100644 index 0000000..be5fcf1 --- /dev/null +++ b/PlantDashboard/main_dashboard.py @@ -0,0 +1,473 @@ +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +import pandas as pd +import numpy as np +from PIL import Image, ImageTk +import json +import os +from datetime import datetime, timedelta +import joblib +from plant_model import PlantGrowthModel +from data_handler import DataHandler +from image_generator import ImageGenerator + +class PlantGrowthDashboard: + def __init__(self, root): + self.root = root + self.root.title("WeGrow") + self.root.geometry("1000x800") # More square dimensions + self.root.configure(bg='#f0f0f0') + + image = Image.open("public/transparentLogo.png") + # Convert to PhotoImage + icon = ImageTk.PhotoImage(image) + # Set as window icon + self.root.iconphoto(False, icon) + + # Initialize components + self.plant_model = PlantGrowthModel() + self.data_handler = DataHandler() + self.image_generator = ImageGenerator() + + # Variables - fixed plant type + self.current_plant = "tomato" # Fixed plant type + self.ambient_mode = tk.StringVar(value="controlled") + self.baseline_image_path = None + + # Environmental parameters with defaults + self.default_params = { + 'temperature': 22.0, + 'humidity': 65.0, + 'soil_acidity': 6.5, + 'pressure': 1013.25, + 'brightness': 50000.0, + 'nutrients': 75.0, + 'water': 80.0, + 'co2': 400.0 + } + + self.env_params = { + 'temperature': tk.DoubleVar(value=self.default_params['temperature']), + 'humidity': tk.DoubleVar(value=self.default_params['humidity']), + 'soil_acidity': tk.DoubleVar(value=self.default_params['soil_acidity']), + 'pressure': tk.DoubleVar(value=self.default_params['pressure']), + 'brightness': tk.DoubleVar(value=self.default_params['brightness']), + 'nutrients': tk.DoubleVar(value=self.default_params['nutrients']), + 'water': tk.DoubleVar(value=self.default_params['water']), + 'co2': tk.DoubleVar(value=self.default_params['co2']) + } + + self.setup_ui() + self.update_prediction() + + def setup_ui(self): + # Main container with square layout + main_frame = ttk.Frame(self.root, padding="8") + main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # Configure grid weights for square layout + self.root.columnconfigure(0, weight=1) + self.root.rowconfigure(0, weight=1) + main_frame.columnconfigure(0, weight=1) + main_frame.columnconfigure(1, weight=2) # Center panel wider + main_frame.columnconfigure(2, weight=1) + main_frame.rowconfigure(1, weight=1) + + # Title + title_label = ttk.Label(main_frame, text="๐ŸŒฑ Plant Growth Dashboard", + font=('Arial', 14, 'bold')) + title_label.grid(row=0, column=0, columnspan=3, pady=(0, 10)) + + # Left panel - Controls + self.setup_control_panel(main_frame) + + # Center panel - Plant Visualization + self.setup_visualization_panel(main_frame) + + # Right panel - Results only (no system messages) + self.setup_results_panel(main_frame) + + def setup_control_panel(self, parent): + control_frame = ttk.LabelFrame(parent, text="Environmental Controls", padding="6") + control_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 6)) + + # Ambient mode + ttk.Label(control_frame, text="Environment Mode:", font=('Arial', 9, 'bold')).grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=(0, 6)) + + mode_frame = ttk.Frame(control_frame) + mode_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 8)) + + ttk.Radiobutton(mode_frame, text="Controlled", variable=self.ambient_mode, + value="controlled", command=self.on_mode_change).pack(anchor=tk.W) + ttk.Radiobutton(mode_frame, text="Semi-Controlled", variable=self.ambient_mode, + value="semi-controlled", command=self.on_mode_change).pack(anchor=tk.W) + ttk.Radiobutton(mode_frame, text="Open", variable=self.ambient_mode, + value="open", command=self.on_mode_change).pack(anchor=tk.W) + + # Baseline image + ttk.Button(control_frame, text="๐Ÿ“ท Load Plant Image", + command=self.load_baseline_image).grid(row=2, column=0, columnspan=2, pady=(0, 10), sticky=(tk.W, tk.E)) + + # Environmental parameters + ttk.Label(control_frame, text="Parameters:", + font=('Arial', 9, 'bold')).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=(0, 4)) + + param_labels = { + 'temperature': '๐ŸŒก๏ธ Temp (ยฐC)', + 'humidity': '๐Ÿ’ง Humidity (%)', + 'soil_acidity': '๐Ÿงช pH', + 'pressure': '๐ŸŒฌ๏ธ Pressure', + 'brightness': 'โ˜€๏ธ Light', + 'nutrients': '๐ŸŒฟ Nutrients (%)', + 'water': '๐Ÿ’ฆ Water (%)', + 'co2': '๐Ÿซง CO2' + } + + row = 4 + for param, label in param_labels.items(): + # Compact parameter layout + param_frame = ttk.Frame(control_frame) + param_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=1) + + ttk.Label(param_frame, text=label, width=11, font=('Arial', 8)).pack(side=tk.LEFT) + + # Scale ranges based on parameter type + if param == 'co2': + scale_max = 1000 + elif param == 'brightness': + scale_max = 100000 + elif param == 'pressure': + scale_max = 1100 + elif param == 'soil_acidity': + scale_max = 14 + else: + scale_max = 100 + + scale = ttk.Scale(param_frame, from_=0, to=scale_max, + variable=self.env_params[param], orient=tk.HORIZONTAL, + command=lambda x, p=param: self.on_param_change(p)) + scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 4)) + + # Value label + value_label = ttk.Label(param_frame, text=f"{self.env_params[param].get():.1f}", width=5, font=('Arial', 8)) + value_label.pack(side=tk.RIGHT) + + # Store reference for updates + setattr(self, f"{param}_value_label", value_label) + + row += 1 + + # Set to Default button + ttk.Button(control_frame, text="๐Ÿ”„ Set to Default", + command=self.set_to_default).grid(row=row, column=0, columnspan=2, pady=(10, 0), sticky=(tk.W, tk.E)) + + control_frame.columnconfigure(0, weight=1) + control_frame.columnconfigure(1, weight=1) + + def setup_visualization_panel(self, parent): + viz_frame = ttk.LabelFrame(parent, text="Plant Visualization", padding="6") + viz_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=3) + + # Notebook for different views + notebook = ttk.Notebook(viz_frame) + notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # Plant Before Growth tab + self.setup_before_growth_tab(notebook) + + # Plant Evolution tab + self.setup_plant_evolution_tab(notebook) + + viz_frame.columnconfigure(0, weight=1) + viz_frame.rowconfigure(0, weight=1) + + def setup_before_growth_tab(self, notebook): + """Tab showing the plant before growth starts""" + before_frame = ttk.Frame(notebook) + notebook.add(before_frame, text="๐ŸŒฑ Initial Plant") + + # Create a frame for the initial plant display + display_frame = ttk.Frame(before_frame) + display_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8) + + # Title for initial state + title_label = ttk.Label(display_frame, text="Plant Initial State", + font=('Arial', 11, 'bold')) + title_label.pack(pady=(0, 8)) + + # Frame for the plant image + self.initial_plant_frame = ttk.Frame(display_frame) + self.initial_plant_frame.pack(fill=tk.BOTH, expand=True) + + # Initial plant image label + self.initial_plant_label = ttk.Label(self.initial_plant_frame, + text="Initial plant state will appear here", + font=('Arial', 9)) + self.initial_plant_label.pack(expand=True) + + # Plant info display + info_frame = ttk.LabelFrame(display_frame, text="Plant Information", padding="4") + info_frame.pack(fill=tk.X, pady=(8, 0)) + + self.plant_info_text = tk.Text(info_frame, height=8, width=35, wrap=tk.WORD, + font=('Arial', 8), bg='#f8f9fa') + self.plant_info_text.pack(fill=tk.BOTH, expand=True) + + # Submit button + submit_frame = ttk.Frame(display_frame) + submit_frame.pack(fill=tk.X, pady=(8, 0)) + + ttk.Button(submit_frame, text="๐Ÿ“ค Submit Plant Information & Photo", + command=self.submit_plant_data).pack(fill=tk.X) + + def setup_plant_evolution_tab(self, notebook): + """Evolution tab""" + evolution_frame = ttk.Frame(notebook) + notebook.add(evolution_frame, text="๐ŸŒฟ Growth Evolution") + + # Create scrollable frame for plant images + canvas_scroll = tk.Canvas(evolution_frame) + scrollbar = ttk.Scrollbar(evolution_frame, orient="vertical", command=canvas_scroll.yview) + self.scrollable_frame = ttk.Frame(canvas_scroll) + + self.scrollable_frame.bind( + "", + lambda e: canvas_scroll.configure(scrollregion=canvas_scroll.bbox("all")) + ) + + canvas_scroll.create_window((0, 0), window=self.scrollable_frame, anchor="nw") + canvas_scroll.configure(yscrollcommand=scrollbar.set) + + canvas_scroll.pack(side="left", fill="both", expand=True) + scrollbar.pack(side="right", fill="y") + + self.image_display_frame = ttk.Frame(self.scrollable_frame) + self.image_display_frame.pack(fill=tk.BOTH, expand=True, padx=6, pady=6) + + def setup_results_panel(self, parent): + results_frame = ttk.LabelFrame(parent, text="Growth Prediction", padding="6") + results_frame.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(6, 0)) + + # Prediction results only (no system messages) + ttk.Label(results_frame, text="Forecast Results:", + font=('Arial', 9, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=(0, 4)) + + self.results_text = tk.Text(results_frame, height=20, width=26, wrap=tk.WORD, font=('Arial', 8)) + self.results_text.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + results_frame.columnconfigure(0, weight=1) + results_frame.rowconfigure(1, weight=1) + + def on_mode_change(self): + self.update_prediction() + + def on_param_change(self, param): + value = self.env_params[param].get() + + # Update value label + value_label = getattr(self, f"{param}_value_label") + if param == 'soil_acidity': + value_label.config(text=f"{value:.1f}") + elif param in ['brightness', 'pressure', 'co2']: + value_label.config(text=f"{value:.0f}") + else: + value_label.config(text=f"{value:.1f}") + + # Auto-update prediction when parameters change + self.update_prediction() + + def set_to_default(self): + """Reset all parameters to default values""" + for param_name, default_value in self.default_params.items(): + self.env_params[param_name].set(default_value) + + self.ambient_mode.set("controlled") + self.update_prediction() + + def load_baseline_image(self): + file_path = filedialog.askopenfilename( + title="Select baseline plant image", + filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")] + ) + if file_path: + self.baseline_image_path = file_path + self.update_prediction() + + def submit_plant_data(self): + """Submit plant information and photo""" + try: + # Get current parameters + params = {param: var.get() for param, var in self.env_params.items()} + params['plant_type'] = self.current_plant + params['ambient_mode'] = self.ambient_mode.get() + + # Create submission data + submission_data = { + 'timestamp': datetime.now().isoformat(), + 'parameters': params, + 'baseline_image_path': self.baseline_image_path, + 'plant_info': self.plant_info_text.get(1.0, tk.END), + 'results': self.results_text.get(1.0, tk.END) + } + + # Show confirmation dialog + messagebox.showinfo("Submission Successful", + "Plant information and photo have been submitted successfully!\n\n" + f"Submission ID: {datetime.now().strftime('%Y%m%d_%H%M%S')}") + + except Exception as e: + messagebox.showerror("Submission Error", f"Error submitting data: {str(e)}") + + def update_prediction(self): + try: + # Get current parameters + params = {param: var.get() for param, var in self.env_params.items()} + params['plant_type'] = self.current_plant + params['ambient_mode'] = self.ambient_mode.get() + + # Generate prediction + prediction = self.plant_model.predict_growth(params) + + # Update initial plant display + self.update_initial_plant_display(params, prediction) + + # Generate plant evolution images + self.generate_plant_evolution(params, prediction) + + # Update results text + self.update_results_display(prediction) + + except Exception as e: + messagebox.showerror("Prediction Error", f"Error generating prediction: {str(e)}") + + def update_initial_plant_display(self, params, prediction): + """Update the initial plant state display""" + try: + # Generate initial plant image (stage 0) + initial_image = self.image_generator.generate_plant_image( + self.current_plant, 0.5, prediction['health_score'], 0 + ) + + # Resize image to fit better in square layout + initial_image = initial_image.resize((280, 210), Image.Resampling.LANCZOS) + + # Convert to PhotoImage and display + photo = ImageTk.PhotoImage(initial_image) + self.initial_plant_label.configure(image=photo, text="") + self.initial_plant_label.image = photo # Keep reference + + # Update plant information + self.update_plant_info(params, prediction) + + except Exception as e: + messagebox.showerror("Image Error", f"Could not generate initial plant image: {str(e)}") + + def update_plant_info(self, params, prediction): + """Update plant information display""" + self.plant_info_text.delete(1.0, tk.END) + + info_text = f"""๐ŸŒฑ PLANT STATUS +{'='*22} + +Plant Type: Tomato +Health Score: {prediction['health_score']:.1f}% +Growth Rate: {prediction['growth_rate']:.2f} cm/day + +๐ŸŒก๏ธ CONDITIONS: +Temperature: {params['temperature']:.1f}ยฐC +Humidity: {params['humidity']:.1f}% +Soil pH: {params['soil_acidity']:.1f} +Light: {params['brightness']:.0f} lux +Water: {params['water']:.1f}% +Nutrients: {params['nutrients']:.1f}% + +๐Ÿ“Š FORECAST: +Final Height: {prediction['final_height']:.1f} cm +Expected Yield: {prediction.get('yield', 'N/A')} +Optimal Conditions: {prediction['optimal_conditions']:.1f}% +""" + + self.plant_info_text.insert(1.0, info_text) + + def generate_plant_evolution(self, params, prediction): + """Generate and display plant evolution images""" + try: + # Generate plant evolution images + images = self.image_generator.generate_evolution( + self.current_plant, params, prediction, self.baseline_image_path + ) + + if images: + # Clear previous images + for widget in self.image_display_frame.winfo_children(): + widget.destroy() + + # Display evolution stages in a square grid + stages_per_row = 2 # Square layout + for i, image in enumerate(images): + row = i // stages_per_row + col = i % stages_per_row + + stage_frame = ttk.Frame(self.image_display_frame) + stage_frame.grid(row=row, column=col, padx=4, pady=4, sticky=(tk.W, tk.E)) + + # Stage label + ttk.Label(stage_frame, text=f"Stage {i+1}", + font=('Arial', 9, 'bold')).pack() + + # Resize image for compact display + resized_image = image.resize((180, 135), Image.Resampling.LANCZOS) + + # Convert PIL image to PhotoImage + photo = ImageTk.PhotoImage(resized_image) + image_label = tk.Label(stage_frame, image=photo) + image_label.image = photo # Keep a reference + image_label.pack() + + # Configure grid weights for even distribution + for col in range(stages_per_row): + self.image_display_frame.columnconfigure(col, weight=1) + + except Exception as e: + messagebox.showerror("Evolution Error", f"Could not generate evolution images: {str(e)}") + + def update_results_display(self, prediction): + self.results_text.delete(1.0, tk.END) + + results = f"""๐ŸŒฑ GROWTH FORECAST +{'='*18} + +Final Height: {prediction['final_height']:.1f} cm +Growth Rate: {prediction['growth_rate']:.2f} cm/day +Health Score: {prediction['health_score']:.1f}/100 + +๐Ÿ“… Growth Phases: +""" + + for phase in prediction.get('phases', []): + results += f"โ€ข {phase['name']}: Days {phase['start']}-{phase['end']}\n" + + results += f"\nโœ… Optimal Conditions: {prediction['optimal_conditions']:.1f}%" + results += f"\n๐Ÿ… Expected Yield: {prediction.get('yield', 'N/A')}" + + # Add environmental summary + results += f"\n\n๐ŸŒก๏ธ ENVIRONMENT SUMMARY:" + results += f"\nTemperature: {self.env_params['temperature'].get():.1f}ยฐC" + results += f"\nHumidity: {self.env_params['humidity'].get():.1f}%" + results += f"\nSoil pH: {self.env_params['soil_acidity'].get():.1f}" + results += f"\nLight Level: {self.env_params['brightness'].get():.0f} lux" + results += f"\nWater Level: {self.env_params['water'].get():.1f}%" + results += f"\nNutrients: {self.env_params['nutrients'].get():.1f}%" + results += f"\nCO2 Level: {self.env_params['co2'].get():.0f} ppm" + + self.results_text.insert(1.0, results) + +def main(): + root = tk.Tk() + app = PlantGrowthDashboard(root) + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/PlantDashboard/plant_model.py b/PlantDashboard/plant_model.py new file mode 100644 index 0000000..2ad9bff --- /dev/null +++ b/PlantDashboard/plant_model.py @@ -0,0 +1,303 @@ +import numpy as np +import joblib +import pickle +from datetime import datetime, timedelta +import random + +class PlantGrowthModel: + def __init__(self): + self.plant_characteristics = { + 'tomato': { + 'max_height': 150, + 'growth_rate_base': 2.5, + 'optimal_temp': (20, 25), + 'optimal_humidity': (60, 70), + 'optimal_ph': (6.0, 6.8), + 'phases': [ + {'name': 'Germination', 'duration': 7, 'growth_factor': 0.1}, + {'name': 'Seedling', 'duration': 14, 'growth_factor': 0.3}, + {'name': 'Vegetative', 'duration': 30, 'growth_factor': 1.0}, + {'name': 'Flowering', 'duration': 21, 'growth_factor': 0.7}, + {'name': 'Fruiting', 'duration': 28, 'growth_factor': 0.4} + ] + }, + 'basil': { + 'max_height': 60, + 'growth_rate_base': 1.8, + 'optimal_temp': (18, 24), + 'optimal_humidity': (50, 65), + 'optimal_ph': (6.0, 7.0), + 'phases': [ + {'name': 'Germination', 'duration': 5, 'growth_factor': 0.1}, + {'name': 'Seedling', 'duration': 10, 'growth_factor': 0.4}, + {'name': 'Vegetative', 'duration': 25, 'growth_factor': 1.0}, + {'name': 'Mature', 'duration': 30, 'growth_factor': 0.6} + ] + }, + 'mint': { + 'max_height': 40, + 'growth_rate_base': 2.0, + 'optimal_temp': (15, 22), + 'optimal_humidity': (65, 75), + 'optimal_ph': (6.0, 7.0), + 'phases': [ + {'name': 'Germination', 'duration': 7, 'growth_factor': 0.1}, + {'name': 'Seedling', 'duration': 12, 'growth_factor': 0.3}, + {'name': 'Vegetative', 'duration': 20, 'growth_factor': 1.0}, + {'name': 'Mature', 'duration': 35, 'growth_factor': 0.5} + ] + }, + 'lettuce': { + 'max_height': 25, + 'growth_rate_base': 1.2, + 'optimal_temp': (16, 20), + 'optimal_humidity': (70, 80), + 'optimal_ph': (6.0, 7.0), + 'phases': [ + {'name': 'Germination', 'duration': 4, 'growth_factor': 0.1}, + {'name': 'Seedling', 'duration': 8, 'growth_factor': 0.4}, + {'name': 'Vegetative', 'duration': 20, 'growth_factor': 1.0}, + {'name': 'Mature', 'duration': 18, 'growth_factor': 0.3} + ] + }, + 'rosemary': { + 'max_height': 120, + 'growth_rate_base': 0.8, + 'optimal_temp': (18, 25), + 'optimal_humidity': (40, 55), + 'optimal_ph': (6.0, 7.5), + 'phases': [ + {'name': 'Germination', 'duration': 14, 'growth_factor': 0.05}, + {'name': 'Seedling', 'duration': 21, 'growth_factor': 0.2}, + {'name': 'Vegetative', 'duration': 60, 'growth_factor': 1.0}, + {'name': 'Mature', 'duration': 90, 'growth_factor': 0.4} + ] + }, + 'strawberry': { + 'max_height': 30, + 'growth_rate_base': 1.5, + 'optimal_temp': (18, 24), + 'optimal_humidity': (60, 70), + 'optimal_ph': (5.5, 6.5), + 'phases': [ + {'name': 'Germination', 'duration': 10, 'growth_factor': 0.1}, + {'name': 'Seedling', 'duration': 15, 'growth_factor': 0.3}, + {'name': 'Vegetative', 'duration': 25, 'growth_factor': 1.0}, + {'name': 'Flowering', 'duration': 20, 'growth_factor': 0.6}, + {'name': 'Fruiting', 'duration': 30, 'growth_factor': 0.4} + ] + } + } + + def predict_growth(self, parameters): + """Predict plant growth based on environmental parameters""" + plant_type = parameters.get('plant_type', 'tomato') + ambient_mode = parameters.get('ambient_mode', 'controlled') + + if plant_type not in self.plant_characteristics: + plant_type = 'tomato' + + plant_info = self.plant_characteristics[plant_type] + + # Calculate environmental stress factors + stress_factors = self._calculate_stress_factors(parameters, plant_info) + + # Apply ambient mode effects + if ambient_mode == 'open': + # Add random variations for open environment + for factor in stress_factors: + stress_factors[factor] *= (0.8 + random.random() * 0.4) + elif ambient_mode == 'semi-controlled': + # Moderate variations + for factor in stress_factors: + stress_factors[factor] *= (0.9 + random.random() * 0.2) + + # Calculate overall health score + health_score = np.mean(list(stress_factors.values())) * 100 + + # Generate growth stages + growth_stages = self._simulate_growth_stages(plant_info, stress_factors) + + # Calculate final metrics + final_height = growth_stages[-1] if growth_stages else 0 + growth_rate = final_height / len(growth_stages) if growth_stages else 0 + + # Calculate optimal conditions percentage + optimal_conditions = self._calculate_optimal_conditions(parameters, plant_info) + + # Estimate yield + yield_estimate = self._estimate_yield(plant_type, health_score, final_height) + + # Generate phase information + phases = self._generate_phase_info(plant_info) + + return { + 'growth_stages': growth_stages, + 'final_height': final_height, + 'growth_rate': growth_rate, + 'health_score': health_score, + 'optimal_conditions': optimal_conditions, + 'yield': yield_estimate, + 'phases': phases, + 'stress_factors': stress_factors + } + + def _calculate_stress_factors(self, params, plant_info): + """Calculate stress factors for each environmental parameter""" + factors = {} + + # Temperature stress + temp = params.get('temperature', 20) + opt_temp = plant_info['optimal_temp'] + if opt_temp[0] <= temp <= opt_temp[1]: + factors['temperature'] = 1.0 + else: + deviation = min(abs(temp - opt_temp[0]), abs(temp - opt_temp[1])) + factors['temperature'] = max(0.1, 1.0 - deviation / 20.0) + + # Humidity stress + humidity = params.get('humidity', 60) + opt_humidity = plant_info['optimal_humidity'] + if opt_humidity[0] <= humidity <= opt_humidity[1]: + factors['humidity'] = 1.0 + else: + deviation = min(abs(humidity - opt_humidity[0]), abs(humidity - opt_humidity[1])) + factors['humidity'] = max(0.1, 1.0 - deviation / 40.0) + + # pH stress + ph = params.get('soil_acidity', 6.5) + opt_ph = plant_info['optimal_ph'] + if opt_ph[0] <= ph <= opt_ph[1]: + factors['ph'] = 1.0 + else: + deviation = min(abs(ph - opt_ph[0]), abs(ph - opt_ph[1])) + factors['ph'] = max(0.1, 1.0 - deviation / 2.0) + + # Water stress + water = params.get('water', 70) / 100.0 + factors['water'] = min(1.0, max(0.1, water)) + + # Nutrient stress + nutrients = params.get('nutrients', 70) / 100.0 + factors['nutrients'] = min(1.0, max(0.1, nutrients)) + + # Light stress + brightness = params.get('brightness', 30000) + optimal_light = 40000 # Optimal light in lux + light_factor = min(1.0, brightness / optimal_light) + factors['light'] = max(0.1, light_factor) + + # CO2 stress + co2 = params.get('co2', 400) + optimal_co2 = 600 # Optimal CO2 in ppm + co2_factor = min(1.0, co2 / optimal_co2) + factors['co2'] = max(0.3, co2_factor) + + return factors + + def _simulate_growth_stages(self, plant_info, stress_factors): + """Simulate daily growth stages""" + base_rate = plant_info['growth_rate_base'] + max_height = plant_info['max_height'] + phases = plant_info['phases'] + + overall_stress = np.mean(list(stress_factors.values())) + adjusted_rate = base_rate * overall_stress + + growth_stages = [] + current_height = 0 + day = 0 + + for phase in phases: + phase_duration = phase['duration'] + growth_factor = phase['growth_factor'] + + for _ in range(phase_duration): + daily_growth = adjusted_rate * growth_factor + # Add some randomness + daily_growth *= (0.8 + random.random() * 0.4) + + current_height += daily_growth + current_height = min(current_height, max_height) + growth_stages.append(current_height) + day += 1 + + return growth_stages + + def _calculate_optimal_conditions(self, params, plant_info): + """Calculate percentage of optimal conditions met""" + optimal_count = 0 + total_conditions = 3 # temp, humidity, pH + + temp = params.get('temperature', 20) + if plant_info['optimal_temp'][0] <= temp <= plant_info['optimal_temp'][1]: + optimal_count += 1 + + humidity = params.get('humidity', 60) + if plant_info['optimal_humidity'][0] <= humidity <= plant_info['optimal_humidity'][1]: + optimal_count += 1 + + ph = params.get('soil_acidity', 6.5) + if plant_info['optimal_ph'][0] <= ph <= plant_info['optimal_ph'][1]: + optimal_count += 1 + + return (optimal_count / total_conditions) * 100 + + def _estimate_yield(self, plant_type, health_score, final_height): + """Estimate plant yield based on health and growth""" + yield_factors = { + 'tomato': {'base': 2.0, 'unit': 'kg'}, + 'basil': {'base': 0.3, 'unit': 'kg'}, + 'mint': {'base': 0.2, 'unit': 'kg'}, + 'lettuce': {'base': 0.5, 'unit': 'kg'}, + 'rosemary': {'base': 0.1, 'unit': 'kg'}, + 'strawberry': {'base': 0.8, 'unit': 'kg'} + } + + if plant_type in yield_factors: + base_yield = yield_factors[plant_type]['base'] + unit = yield_factors[plant_type]['unit'] + + # Adjust yield based on health and height + health_factor = health_score / 100.0 + height_factor = min(1.0, final_height / 50.0) # Normalize height + + estimated_yield = base_yield * health_factor * height_factor + return f"{estimated_yield:.2f} {unit}" + + return "N/A" + + def _generate_phase_info(self, plant_info): + """Generate phase information with start/end days""" + phases = [] + current_day = 0 + + for phase in plant_info['phases']: + phases.append({ + 'name': phase['name'], + 'start': current_day, + 'end': current_day + phase['duration'] - 1 + }) + current_day += phase['duration'] + + return phases + + def save_model(self, filename): + """Save the model to a file""" + try: + with open(filename, 'wb') as f: + pickle.dump(self.plant_characteristics, f) + return True + except Exception as e: + print(f"Error saving model: {e}") + return False + + def load_model(self, filename): + """Load the model from a file""" + try: + with open(filename, 'rb') as f: + self.plant_characteristics = pickle.load(f) + return True + except Exception as e: + print(f"Error loading model: {e}") + return False diff --git a/PlantDashboard/public/.DS_Store b/PlantDashboard/public/.DS_Store new file mode 100644 index 0000000..61bd1cc Binary files /dev/null and b/PlantDashboard/public/.DS_Store differ diff --git a/PlantDashboard/public/Flower.jpg b/PlantDashboard/public/Flower.jpg new file mode 100644 index 0000000..5d21570 Binary files /dev/null and b/PlantDashboard/public/Flower.jpg differ diff --git a/PlantDashboard/public/TransparentFlower.png b/PlantDashboard/public/TransparentFlower.png new file mode 100644 index 0000000..7a278c8 Binary files /dev/null and b/PlantDashboard/public/TransparentFlower.png differ diff --git a/PlantDashboard/public/logo.jpg b/PlantDashboard/public/logo.jpg new file mode 100644 index 0000000..332b356 Binary files /dev/null and b/PlantDashboard/public/logo.jpg differ diff --git a/PlantDashboard/public/transparentLogo.png b/PlantDashboard/public/transparentLogo.png new file mode 100644 index 0000000..bd04425 Binary files /dev/null and b/PlantDashboard/public/transparentLogo.png differ diff --git a/PlantDashboard/requirements.txt b/PlantDashboard/requirements.txt new file mode 100644 index 0000000..8059b13 --- /dev/null +++ b/PlantDashboard/requirements.txt @@ -0,0 +1,36 @@ +# Core data science and visualization libraries +matplotlib>=3.5.0 +pandas>=1.3.0 +numpy>=1.21.0 + +# Image processing +Pillow>=8.3.0 + +# Machine learning and model persistence +joblib>=1.1.0 +scikit-learn>=1.0.0 + +# Additional utilities +python-dateutil>=2.8.0 +pytz>=2021.1 + +# Data export formats +openpyxl>=3.0.0 +xlsxwriter>=3.0.0 + +# Configuration management +pyyaml>=5.4.0 + +# Logging enhancements +colorlog>=6.6.0 + +# Performance monitoring (optional) +psutil>=5.8.0 +memory-profiler>=0.60.0 + +# Note: tkinter comes pre-installed with most Python distributions +# If tkinter is not available, install it using your system package manager: +# Ubuntu/Debian: sudo apt-get install python3-tk +# CentOS/RHEL: sudo yum install tkinter +# macOS: tkinter is included with Python from python.org +# Windows: tkinter is included with Python installer diff --git a/README.md b/README.md index b8a0311..e43b993 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,110 @@ -# team-2 +# WeGrow +> *Hackathon Project - [NOI Hackathon] [2025]* + +## ๐Ÿš€ Overview + +Hi everyone! We are WeGrow, + +## ๐ŸŽฏ Problem Statement + +Describe the challenge or problem your team is addressing in this hackathon. + +## ๐Ÿ’ก Solution + +Explain your approach and how your solution addresses the problem statement. + +## โœจ Features + +- [ ] Feature 1 +- [ ] Feature 2 +- [ ] Feature 3 +- [ ] Feature 4 + +## ๐Ÿ› ๏ธ Tech Stack + +**Frontend:** + +- Technology 1 +- Technology 2 + +**Backend:** + +- Technology 1 +- Technology 2 + +**Other Tools:** + +- Tool 1 +- Tool 2 + +## ๐Ÿ—๏ธ Architecture + +```text +[Add architecture diagram or description here] +``` + +## ๐Ÿš€ Using the application + +### Prerequisites + +```bash +# List any prerequisites here +# e.g., Node.js 18+, Python 3.9+ +``` + +### Installation + +1. Clone the repository + +```bash +git clone https://github.com/your-username/NOIProject.git +cd NOIProject +``` + +1. Install dependencies + +```bash +# Add installation commands here +# e.g., npm install, pip install -r requirements.txt +``` + +1. Set up environment variables + +```bash +# Copy example environment file +cp .env.example .env +# Edit .env with your configuration +``` + +1. Run the application + +```bash +# Add run commands here +# e.g., npm start, python app.py +``` + +## ๐Ÿ“ธ Screenshots + +Add screenshots of your application here + +## ๐ŸŽฅ Demo + +Add link to demo video or live deployment + +## ๐Ÿง‘โ€๐Ÿ’ป Team + +Meet our amazing team of 4: + +| Name | Role | GitHub | LinkedIn | +|------|------|---------|----------| +| Member 1 | Role | [@username](https://github.com/username) | [LinkedIn](https://linkedin.com/in/username) | +| Member 2 | Role | [@username](https://github.com/username) | [LinkedIn](https://linkedin.com/in/username) | +| Member 3 | Role | [@username](https://github.com/username) | [LinkedIn](https://linkedin.com/in/username) | +| Member 4 | Role | [@username](https://github.com/username) | [LinkedIn](https://linkedin.com/in/username) | + +## ๐Ÿ“„ License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- diff --git a/path.txt b/path.txt new file mode 100644 index 0000000..0803001 --- /dev/null +++ b/path.txt @@ -0,0 +1,41 @@ +ROADMAP + -understand codebase + + -add the possibility to retrieve all the data when the user click the submit button + -format the data in an usable format + + -resolve numpy.float64 bug + -differences between the three modes: + Open mode: + -the temp, humidity, light, water parameters are setted by the meteo api, the rest is setted by the user + + SemiControlled mode: + -the user choose how to set the parameters + + Controlled mode: + -all the values are set by the user + + + -default modes are setted with general values in an optimal condition for your plant + + -The forecast results will have as input the text description obtained by the model + (the output will be in the text folder inside data/texts folder, format "date1-date2-enum.txt") + -The growth evolution will have as input the photo/s obtained by the model + (the output will be in the text folder inside data/images folder, format "date1-date2-enum.jpg) + + -make the plant information as a text field, remove all the connections with other functions + + -set the value of light as Daily Light Integral (DLI) + + -forecast for a period of time: + -create a calendar widget with start and end data + -based on how much does it take to create an image, for each subperiod of time create a new image to show + (it this will be coded add all a button to iterate over the folder) + + -when the user click the load plant image, all previous record will be eliminated + + -final updates of README.md, hackathon page, forgejo, github page, small design adjustments. + + +DURING DINNER + -update all on forgejo, hackathon page, forgejo \ No newline at end of file