Spaces:
Sleeping
Sleeping
| from flask import Flask, render_template, request, jsonify | |
| import math | |
| import logging | |
| import os | |
| import google.generativeai as genai | |
| import requests | |
| import base64 # For potentially handling image data, though we're using URLs for Gemini Vision | |
| app = Flask(__name__) | |
| logging.basicConfig(level=logging.INFO) | |
| # Configuration – ensure your API keys are valid | |
| # Retrieve API keys from environment variables | |
| GEMINI_API_KEY ="AIzaSyCe4TCtUC8C9EzYMiQP8VykIS1r4gHZKg0" | |
| GOOGLE_MAPS_STATIC_API_KEY = os.getenv('GOOGLE_MAPS_STATIC_API_KEY') # For fetching map images | |
| # Configure Gemini API | |
| if GEMINI_API_KEY: | |
| genai.configure(api_key=GEMINI_API_KEY) | |
| else: | |
| logging.error("GEMINI_API_KEY not set. Gemini API functionality will be limited or unavailable.") | |
| def validate_coordinates(lat, lon): | |
| try: | |
| lat = float(lat) | |
| lon = float(lon) | |
| if not (-90 <= lat <= 90 and -180 <= lon <= 180): | |
| return None, None | |
| return lat, lon | |
| except (TypeError, ValueError): | |
| return None, None | |
| def index(): | |
| return render_template('index.html') | |
| def calculate_area(): | |
| """ | |
| Calculates the area of a polygon given its coordinates in square meters. | |
| Uses the Shoelace formula for area calculation on a sphere. | |
| """ | |
| data = request.json | |
| coordinates = data.get('coordinates') | |
| if not coordinates or not isinstance(coordinates, list) or len(coordinates) < 3: | |
| return jsonify({"error": "Invalid polygon coordinates provided. Need at least 3 points."}), 400 | |
| area_sq_meters = calculate_polygon_area_haversine(coordinates) | |
| if area_sq_meters is None: | |
| return jsonify({"error": "Error calculating area"}), 500 | |
| area_sq_km = area_sq_meters / 1_000_000 | |
| area_acres = area_sq_meters * 0.000247105 | |
| area_hectares = area_sq_meters / 10_000 | |
| return jsonify({ | |
| "area_sq_meters": area_sq_meters, | |
| "area_sq_km": area_sq_km, | |
| "area_acres": area_acres, | |
| "area_hectares": area_hectares, | |
| "coordinates": coordinates | |
| }) | |
| def calculate_polygon_area_haversine(coords): | |
| """ | |
| Calculates the area of a spherical polygon using the Haversine formula | |
| for edge lengths and then applying spherical excess. | |
| This is an approximation and might not be perfectly accurate for very large polygons. | |
| For small farm plots, it should be sufficiently accurate. | |
| """ | |
| R = 6378137 # Earth's radius in meters | |
| area = 0.0 | |
| if len(coords) < 3: | |
| return 0.0 | |
| for i in range(len(coords)): | |
| lat1_rad = math.radians(coords[i]['lat']) | |
| lon1_rad = math.radians(coords[i]['lng']) | |
| lat2_rad = math.radians(coords[(i + 1) % len(coords)]['lat']) | |
| lon2_rad = math.radians(coords[(i + 1) % len(coords)]['lng']) | |
| # Using Gauss's area formula for spherical polygons (related to spherical excess) | |
| area += (lon2_rad - lon1_rad) * (2 + math.sin(lat1_rad) + math.sin(lat2_rad)) | |
| area = area * R * R / 2.0 | |
| return abs(area) | |
| def get_polygon_center(coords): | |
| """Calculates the approximate center of a polygon.""" | |
| if not coords: | |
| return None | |
| lat_sum = sum(p['lat'] for p in coords) | |
| lon_sum = sum(p['lng'] for p in coords) | |
| return {'lat': lat_sum / len(coords), 'lng': lon_sum / len(coords)} | |
| def generate_farm_design(): | |
| """ | |
| Generates a farm design plan using Gemini based on geofenced area, | |
| farm type, and farmer's preferences. | |
| """ | |
| data = request.json | |
| coordinates = data.get('coordinates') | |
| farm_type = data.get('farm_type') | |
| preferences = data.get('preferences') | |
| if not coordinates or not isinstance(coordinates, list) or len(coordinates) < 3: | |
| return jsonify({"error": "Invalid polygon coordinates provided for design. Need at least 3 points."}), 400 | |
| if not farm_type: | |
| return jsonify({"error": "Farm type is required."}), 400 | |
| if not GOOGLE_MAPS_STATIC_API_KEY: | |
| return jsonify({"error": "Google Maps Static API Key not configured on the server."}), 500 | |
| if not GEMINI_API_KEY: | |
| return jsonify({"error": "Gemini API Key not configured on the server."}), 500 | |
| try: | |
| # 1. Get the center of the polygon for the static map image | |
| center_point = get_polygon_center(coordinates) | |
| if not center_point: | |
| return jsonify({"error": "Could not determine polygon center for map image."}), 500 | |
| # Construct path for the polygon on the static map | |
| path_str = "color:0x4CAF50FF|weight:2|fillcolor:0x4CAF504C" | |
| for coord in coordinates: | |
| path_str += f"|{coord['lat']},{coord['lng']}" | |
| # Get Google Static Map image URL | |
| # Adjust zoom level based on area if possible, for simplicity using fixed for now. | |
| # Max width/height for static maps is typically 640x640. | |
| map_image_url = ( | |
| f"https://maps.googleapis.com/maps/api/staticmap?center={center_point['lat']},{center_point['lng']}" | |
| f"&zoom=15&size=640x640&maptype=satellite&markers=color:red%7C{center_point['lat']},{center_point['lng']}" | |
| f"&path={path_str}" | |
| f"&key={GOOGLE_MAPS_STATIC_API_KEY}" | |
| ) | |
| logging.info(f"Generated Static Map URL: {map_image_url}") | |
| # 2. Prepare the prompt for Gemini Pro Vision | |
| model = genai.GenerativeModel('gemini-2.5-flash-image-preview') | |
| # Craft a comprehensive prompt for the AI | |
| user_prompt = ( | |
| f"I am a farmer planning to set up a '{farm_type}' farm on the enclosed land shown in the satellite image. " | |
| f"My additional preferences are: '{preferences}'. " | |
| f"Please analyze the terrain in the image and provide a detailed, optimal farm layout plan. " | |
| f"Include recommendations for:" | |
| f"\n- **Overall layout design (e.g., zones for different activities, orientation)**" | |
| f"\n- **Specific elements for {farm_type} farming (e.g., for horticulture: crop rows, irrigation, greenhouses; for poultry: coop placement, runs; for dairy: barn, milking parlor, pastures).**" | |
| f"\n- **Resource management (water, shade, sun exposure, potential waste management).**" | |
| f"\n- **Accessibility (paths, roads).**" | |
| f"\n- **Any terrain-specific considerations from the image.**" | |
| f"\n\nAlso, provide a short, descriptive prompt (2-3 sentences) that could be used by a text-to-image AI to visualize this proposed layout overlaid on a similar satellite image of a farm, depicting the suggested elements (e.g., 'An aerial view of a farm, clearly demarcated with rows of crops, a poultry coop, and a small dairy barn, all integrated seamlessly into the landscape with efficient pathing.')." | |
| ) | |
| # 3. Call Gemini Pro Vision | |
| # Gemini Pro Vision can take image URLs directly | |
| response = model.generate_content([user_prompt, {'mime_type': 'image/jpeg', 'image_url': map_image_url}]) | |
| design_plan = response.text | |
| logging.info(f"Gemini response: {design_plan}") | |
| # Extract the image generation prompt from Gemini's response if it followed the instruction | |
| image_gen_prompt_match = "" | |
| # A simple heuristic to find the image prompt if Gemini puts it at the end | |
| if "descriptive prompt" in design_plan.lower(): | |
| parts = design_plan.rsplit("descriptive prompt:", 1) | |
| if len(parts) > 1: | |
| design_plan = parts[0].strip() | |
| image_gen_prompt_match = parts[1].strip() | |
| if not image_gen_prompt_match and "text-to-image AI" in design_plan: | |
| # Fallback: if Gemini embeds it differently | |
| start_index = design_plan.lower().find("text-to-image ai") | |
| if start_index != -1: | |
| end_index = design_plan.find(".", start_index + len("text-to-image ai")) | |
| if end_index != -1: | |
| image_gen_prompt_match = design_plan[start_index:end_index+1] | |
| # Try to remove it from the main design_plan if it's found there | |
| design_plan = design_plan.replace(image_gen_prompt_match, "").strip() | |
| return jsonify({ | |
| "design_plan": design_plan, | |
| "visual_design_prompt": image_gen_prompt_match, # This is the prompt for another image gen AI | |
| "map_image_url": map_image_url # Optionally return the static map URL for context | |
| }) | |
| except Exception as e: | |
| logging.error(f"Error generating farm design: {str(e)}") | |
| # More detailed error handling for API specific issues could be added | |
| if "400 Bad Request" in str(e) and "API key" in str(e): | |
| return jsonify({"error": "Gemini API Key might be invalid or improperly configured.", "details": str(e)}), 500 | |
| return jsonify({"error": "Failed to generate farm design plan", "details": str(e)}), 503 | |
| def not_found_error(error): | |
| return jsonify({"error": "Resource not found"}), 404 | |
| def internal_error(error): | |
| return jsonify({"error": "Internal server error"}), 500 | |
| if __name__ == '__main__': | |
| app.run(debug=True, port=5000) |