Spaces:
Running
Running
File size: 9,264 Bytes
42c7a8a 12fb30c 42c7a8a 4996fd0 42c7a8a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
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
@app.route('/')
def index():
return render_template('index.html')
@app.route('/calculate_area', methods=['POST'])
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)}
@app.route('/generate_farm_design', methods=['POST'])
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
@app.errorhandler(404)
def not_found_error(error):
return jsonify({"error": "Resource not found"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "Internal server error"}), 500
if __name__ == '__main__':
app.run(debug=True, port=5000) |