rajkhanke commited on
Commit
a35962c
·
verified ·
1 Parent(s): 28f53d0

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +339 -0
  2. templates/index.html +372 -0
app.py ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import json
3
+ from flask import Flask, render_template, request, jsonify, Response
4
+ from google import genai
5
+ import markdown
6
+
7
+ app = Flask(__name__)
8
+
9
+ # Replace with your actual Gemini API key
10
+ client = genai.Client(api_key="AIzaSyBtXV2xJbrWVV57B5RWy_meKXOA59HFMeY")
11
+
12
+ def validate_coordinates(lat, lon):
13
+ """Validate and convert latitude and longitude to float."""
14
+ try:
15
+ return float(lat), float(lon)
16
+ except (TypeError, ValueError):
17
+ return None, None
18
+
19
+ @app.route('/')
20
+ def index():
21
+ return render_template('index.html')
22
+
23
+ @app.route('/get_weather_data', methods=['GET'])
24
+ def get_weather_data():
25
+ """
26
+ Fetch weather data using Open-Meteo's forecast endpoint:
27
+ - daily: temperature_2m_max (max_temp), temperature_2m_min (min_temp), precipitation_sum (rain)
28
+ - hourly: relative_humidity_2m, soil_moisture_3_to_9cm, cloudcover, windspeed_10m
29
+ - current_weather: for current temperature and wind speed
30
+ """
31
+ lat = request.args.get('lat')
32
+ lon = request.args.get('lon')
33
+ lat, lon = validate_coordinates(lat, lon)
34
+ if lat is None or lon is None:
35
+ return jsonify({"error": "Invalid coordinates"}), 400
36
+
37
+ try:
38
+ forecast_url = "https://api.open-meteo.com/v1/forecast"
39
+ forecast_params = {
40
+ "latitude": lat,
41
+ "longitude": lon,
42
+ "current_weather": "true",
43
+ "daily": "temperature_2m_max,temperature_2m_min,precipitation_sum",
44
+ "hourly": "relative_humidity_2m,soil_moisture_3_to_9cm,cloudcover,windspeed_10m",
45
+ "timezone": "auto"
46
+ }
47
+ resp = requests.get(forecast_url, params=forecast_params)
48
+ resp.raise_for_status()
49
+ data = resp.json()
50
+
51
+ daily = data.get("daily", {})
52
+ hourly = data.get("hourly", {})
53
+ current = data.get("current_weather", {})
54
+
55
+ # Daily data
56
+ max_temp = daily.get("temperature_2m_max", [None])[0]
57
+ min_temp = daily.get("temperature_2m_min", [None])[0]
58
+ rain = daily.get("precipitation_sum", [None])[0]
59
+
60
+ # Hourly data (averages)
61
+ humidity_list = hourly.get("relative_humidity_2m", [])
62
+ soil_list = hourly.get("soil_moisture_3_to_9cm", [])
63
+ cloud_list = hourly.get("cloudcover", [])
64
+ wind_list = hourly.get("windspeed_10m", [])
65
+
66
+ avg_humidity = sum(humidity_list)/len(humidity_list) if humidity_list else None
67
+ avg_soil_moisture = sum(soil_list)/len(soil_list) if soil_list else None
68
+ avg_cloud_cover = sum(cloud_list)/len(cloud_list) if cloud_list else None
69
+
70
+ # Current weather
71
+ current_temp = current.get("temperature")
72
+ wind_speed = current.get("windspeed")
73
+
74
+ weather = {
75
+ "max_temp": max_temp,
76
+ "min_temp": min_temp,
77
+ "rainfall": rain,
78
+ "humidity": avg_humidity,
79
+ "soil_moisture": avg_soil_moisture,
80
+ "current_temp": current_temp,
81
+ "wind_speed": wind_speed,
82
+ "cloud_cover": avg_cloud_cover
83
+ }
84
+ return jsonify(weather)
85
+ except Exception as e:
86
+ return jsonify({"error": str(e)}), 500
87
+
88
+ @app.route('/get_soil_properties', methods=['GET'])
89
+ def get_soil_properties():
90
+ """Fetch soil properties using SoilGrids API and map to user-friendly names."""
91
+ lat = request.args.get('lat')
92
+ lon = request.args.get('lon')
93
+ lat, lon = validate_coordinates(lat, lon)
94
+ if lat is None or lon is None:
95
+ return jsonify({"error": "Invalid coordinates"}), 400
96
+
97
+ try:
98
+ prop_url = "https://rest.isric.org/soilgrids/v2.0/properties/query"
99
+ prop_params = {
100
+ "lon": str(lon),
101
+ "lat": str(lat),
102
+ "property": [
103
+ "bdod", "cec", "cfvo", "clay", "nitrogen",
104
+ "ocd", "phh2o", "sand", "silt",
105
+ "soc", "wv0010", "wv0033", "wv1500"
106
+ ],
107
+ "depth": "5-15cm",
108
+ "value": "mean"
109
+ }
110
+ headers = {"accept": "application/json"}
111
+ response = requests.get(prop_url, params=prop_params, headers=headers)
112
+ response.raise_for_status()
113
+ prop_data = response.json()
114
+
115
+ table_data = []
116
+ PARAMETER_NAMES = {
117
+ "bdod": "Bulk Density",
118
+ "cec": "CEC",
119
+ "cfvo": "Field Capacity",
120
+ "clay": "Clay",
121
+ "nitrogen": "Nitrogen",
122
+ "ocd": "Organic Carbon Density",
123
+ "phh2o": "pH",
124
+ "sand": "Sand",
125
+ "silt": "Silt",
126
+ "soc": "Soil Organic Carbon",
127
+ "wv0010": "Volumetric Water Content (0-10cm)",
128
+ "wv0033": "Volumetric Water Content (10-33cm)",
129
+ "wv1500": "Volumetric Water Content (1500)"
130
+ }
131
+ for layer in prop_data.get('properties', {}).get('layers', []):
132
+ parameter = layer.get('name')
133
+ display_name = PARAMETER_NAMES.get(parameter, parameter)
134
+ value = layer.get('depths', [{}])[0].get('values', {}).get('mean')
135
+ if value is None:
136
+ continue
137
+ if parameter in ["wv0010", "wv0033", "wv1500"]:
138
+ final_value = value / 10.0
139
+ unit = layer.get('unit_measure', {}).get("target_units", "")
140
+ elif parameter in ["phh2o"]:
141
+ final_value = value / 10.0
142
+ unit = layer.get('unit_measure', {}).get("mapped_units", "").replace("*10", "").strip()
143
+ else:
144
+ final_value = value
145
+ unit = layer.get('unit_measure', {}).get("mapped_units", "")
146
+ table_data.append([display_name, final_value, unit])
147
+
148
+ return jsonify({"soil_properties": table_data})
149
+ except Exception as e:
150
+ return jsonify({"error": str(e)}), 500
151
+
152
+ def call_gemini_api(input_data):
153
+ """
154
+ Enhanced prompt: We request a visually appealing Markdown report WITHOUT
155
+ showing raw CSS code blocks. Instead, we want a descriptive layout.
156
+
157
+ NOTE: We instruct the model to produce headings, paragraphs, and a table
158
+ in a color-rich, well-spaced manner, but NOT to display raw CSS code.
159
+ """
160
+ prompt = f"""
161
+ Create a visually appealing, farmer-friendly pest outbreak report in Markdown with the following:
162
+
163
+ 1. A large, centered heading: "Pest Outbreak Dashboard Report".
164
+ 2. A short paragraph indicating location (latitude: {input_data.get('latitude')}, longitude: {input_data.get('longitude')}), location as per lat,long(like just ex dont consider it as hardoced nagpur,india so kike fetch from lat,long) and the crop/farm context.
165
+ 3. Several subheadings (e.g., "Agricultural Inputs", "Pest Outbreak Analysis", "Best Agricultural Practices", "Insights") with short paragraphs.
166
+ 4. A colorfully styled table (no raw CSS code blocks) with:
167
+ - Pest Name
168
+ - Predicted Outbreak Month(s)
169
+ - Severity
170
+ - Precautionary Measures
171
+ 5. Provide bullet points for best practices.
172
+ 6. Use a friendly color scheme, with subtle hovers or highlights for rows, and consistent fonts.
173
+ 7. Avoid printing any raw code blocks.
174
+ 8. Incorporate the weather, soil, and agricultural data (like sowing date, irrigation method) into the narrative but do not list them as raw parameters.
175
+ 9. do not give off topic insitruction only pest outbreka report i want okay, and dotn use special characters and justified text
176
+ Important details from the user:
177
+ - Crop Type: {input_data.get('crop_type')}
178
+ - Sowing Date: {input_data.get('sowing_date')}
179
+ - Harvest Date: {input_data.get('harvest_date')}
180
+ - Current Growth Stage: {input_data.get('growth_stage')}
181
+ - Irrigation Frequency: {input_data.get('irrigation_freq')}
182
+ - Irrigation Method: {input_data.get('irrigation_method')}
183
+ - Soil Type: {input_data.get('soil_type')}
184
+
185
+ - Max Temp: {input_data.get('max_temp')}
186
+ - Min Temp: {input_data.get('min_temp')}
187
+ - Current Temp: {input_data.get('current_temp')}
188
+ - Humidity: {input_data.get('humidity')}
189
+ - Rainfall: {input_data.get('rain')}
190
+ - Soil Moisture: {input_data.get('soil_moisture')}
191
+ - Wind Speed: {input_data.get('wind_speed')}
192
+ - Cloud Cover: {input_data.get('cloud_cover')}
193
+ 10. also i want specific reocmmendation on pest control (seprate than precuatuonary measure below it in bullet pooints),best agriculktual practices,not generlaized one , but speicifc as perstudiyng each input paprmaetenrindetial okay,poepr sltying tbale should be rendered porpelry etc.. porper bold heaidng ,big fotn,left allgiemnd jsutified text
194
+ 11.again order first title the lat,long,location derive form lat long(ex : nagpur,india) then below it agiruclturla oinput parmeter analysis, then pest tbale then pest avidnaces practice in dpeth 10-12 with bueet pint safter that spciific agrficulturla best practices as per input parameters, after the damage of predicted pest some contnest and more dept contnext this shoudl be order
195
+ """
196
+ response = client.models.generate_content(
197
+ model="gemini-2.0-flash",
198
+ contents=prompt,
199
+ )
200
+ return response.text
201
+
202
+ @app.route('/predict', methods=['POST'])
203
+ def predict():
204
+ form_data = request.form.to_dict()
205
+ report_md = call_gemini_api(form_data)
206
+
207
+ # Convert raw markdown to HTML
208
+ report_html = markdown.markdown(report_md)
209
+
210
+ # Inject advanced, colorful styling into the final HTML
211
+ html_output = f"""<!DOCTYPE html>
212
+ <html lang="en">
213
+ <head>
214
+ <meta charset="UTF-8">
215
+ <title>Pest Outbreak Dashboard Report</title>
216
+ <!-- Tailwind for utility classes -->
217
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
218
+ <style>
219
+ /* Overall page background with a subtle gradient */
220
+ body {{
221
+ margin: 0;
222
+ padding: 2rem;
223
+ background: linear-gradient(120deg, #f7f7f7 0%, #e3f2fd 100%);
224
+ font-family: 'Segoe UI', Tahoma, sans-serif;
225
+ }}
226
+
227
+ .report-container {{
228
+ max-width: 1000px;
229
+ margin: 0 auto;
230
+ background-color: #ffffff;
231
+ border-radius: 8px;
232
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
233
+ padding: 2rem;
234
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
235
+ }}
236
+ .report-container:hover {{
237
+ transform: translateY(-4px);
238
+ box-shadow: 0 12px 24px rgba(0,0,0,0.15);
239
+ }}
240
+
241
+ /* Gradient heading for H1 */
242
+ .report-container h1 {{
243
+ text-align: center;
244
+ font-size: 2rem;
245
+ margin-bottom: 1.5rem;
246
+ color: #ffffff;
247
+ background: linear-gradient(to right, #81c784, #388e3c);
248
+ padding: 1rem;
249
+ border-radius: 6px;
250
+ box-shadow: inset 0 1px 2px rgba(0,0,0,0.1);
251
+ }}
252
+
253
+ /* Secondary headings (H2, H3) */
254
+ .report-container h2,
255
+ .report-container h3 {{
256
+ margin-top: 1.5rem;
257
+ margin-bottom: 0.75rem;
258
+ color: #2c3e50;
259
+ text-align: left;
260
+ }}
261
+
262
+ /* Paragraphs */
263
+ .report-container p {{
264
+ margin-bottom: 1rem;
265
+ color: #555555;
266
+ text-align: justify;
267
+ line-height: 1.6;
268
+ }}
269
+
270
+ /* Lists */
271
+ .report-container ul,
272
+ .report-container ol {{
273
+ margin-left: 1.5rem;
274
+ margin-bottom: 1rem;
275
+ color: #555555;
276
+ }}
277
+
278
+ /* Table styling */
279
+ .report-container table {{
280
+ width: 100%;
281
+ border-collapse: collapse;
282
+ margin: 1.5rem 0;
283
+ }}
284
+ .report-container thead tr {{
285
+ background: linear-gradient(to right, #81c784, #388e3c);
286
+ color: #ffffff;
287
+ }}
288
+ .report-container th,
289
+ .report-container td {{
290
+ border: 1px solid #ddd;
291
+ padding: 12px 15px;
292
+ text-align: left;
293
+ transition: background-color 0.2s ease;
294
+ }}
295
+ .report-container tbody tr:hover {{
296
+ background-color: #f9f9f9;
297
+ }}
298
+
299
+ /* Responsive table for smaller screens */
300
+ @media (max-width: 768px) {{
301
+ .report-container table,
302
+ .report-container thead,
303
+ .report-container tbody,
304
+ .report-container th,
305
+ .report-container td,
306
+ .report-container tr {{
307
+ display: block;
308
+ width: 100%;
309
+ }}
310
+ .report-container thead tr {{
311
+ display: none;
312
+ }}
313
+ .report-container td {{
314
+ border: none;
315
+ border-bottom: 1px solid #ddd;
316
+ position: relative;
317
+ padding-left: 50%;
318
+ text-align: left;
319
+ }}
320
+ .report-container td:before {{
321
+ content: attr(data-label);
322
+ position: absolute;
323
+ left: 15px;
324
+ font-weight: bold;
325
+ }}
326
+ }}
327
+ </style>
328
+ </head>
329
+ <body>
330
+ <div class="report-container">
331
+ {report_html}
332
+ </div>
333
+ </body>
334
+ </html>"""
335
+ return Response(html_output, mimetype="text/html")
336
+
337
+
338
+ if __name__ == '__main__':
339
+ app.run(debug=True)
templates/index.html ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Pest Outbreak Prediction System</title>
7
+ <!-- Bootstrap CSS (for grid utilities) -->
8
+ <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet" />
9
+ <!-- Tailwind CSS CDN for advanced styling -->
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <!-- Leaflet CSS for map -->
12
+ <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
13
+ <!-- Leaflet Control Geocoder CSS for search bar -->
14
+ <link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css" />
15
+ <style>
16
+ body {
17
+ background: #f2f2f2; /* light grey to match final report page */
18
+ margin: 0;
19
+ padding: 0;
20
+ }
21
+ .container-custom {
22
+ max-width: 1200px;
23
+ margin: 2rem auto;
24
+ padding: 1rem;
25
+ background: #ffffff;
26
+ border-radius: 8px;
27
+ box-shadow: 0 4px 12px rgba(0,0,0,0.1);
28
+ }
29
+ #map {
30
+ height: 400px;
31
+ border-radius: 0.75rem;
32
+ box-shadow: 0 8px 16px rgba(0,0,0,0.1);
33
+ }
34
+ .form-section {
35
+ background: #ffffff;
36
+ border-radius: 0.75rem;
37
+ box-shadow: 0 8px 16px rgba(0,0,0,0.1);
38
+ padding: 2rem;
39
+ margin-bottom: 1.5rem;
40
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
41
+ }
42
+ .form-section:hover {
43
+ transform: translateY(-4px);
44
+ box-shadow: 0 12px 24px rgba(0,0,0,0.15);
45
+ }
46
+ h1, h4 {
47
+ color: #2e7d32;
48
+ text-align: center;
49
+ }
50
+ /* Weather Card Styles */
51
+ .weather-card {
52
+ background: #ffffff;
53
+ border-radius: 0.5rem;
54
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
55
+ padding: 1rem;
56
+ text-align: center;
57
+ transition: transform 0.2s;
58
+ }
59
+ .weather-card:hover {
60
+ transform: translateY(-4px);
61
+ }
62
+ .weather-title {
63
+ font-weight: 600;
64
+ color: #2e7d32;
65
+ margin-bottom: 0.5rem;
66
+ }
67
+ /* Soil properties table styling */
68
+ #soilPropertiesTable table {
69
+ width: 100%;
70
+ border-collapse: collapse;
71
+ }
72
+ #soilPropertiesTable th, #soilPropertiesTable td {
73
+ padding: 0.75rem;
74
+ border: 1px solid #e2e8f0;
75
+ text-align: center;
76
+ }
77
+ #soilPropertiesTable th {
78
+ background-color: #81c784;
79
+ color: #fff;
80
+ }
81
+ </style>
82
+ </head>
83
+ <body>
84
+ <div class="container container-custom py-8">
85
+ <h1 class="text-4xl font-bold mb-8">Pest Outbreak Prediction System</h1>
86
+ <!-- The form submits to /predict to generate a detailed pest outbreak report -->
87
+ <form id="farmForm" action="/predict" method="post">
88
+ <!-- Map & Manual Inputs -->
89
+ <div class="form-section">
90
+ <h4 class="text-2xl font-semibold mb-4">Select Farm Location</h4>
91
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
92
+ <!-- Left: Satellite map with search bar -->
93
+ <div>
94
+ <div id="map"></div>
95
+ </div>
96
+ <!-- Right: Manual lat/lon input -->
97
+ <div class="flex flex-col justify-center space-y-4 p-4">
98
+ <div>
99
+ <label class="font-medium" for="manual_lat">Latitude:</label>
100
+ <input type="number" step="0.000001" id="manual_lat" class="form-control" placeholder="Enter latitude" />
101
+ </div>
102
+ <div>
103
+ <label class="font-medium" for="manual_lon">Longitude:</label>
104
+ <input type="number" step="0.000001" id="manual_lon" class="form-control" placeholder="Enter longitude" />
105
+ </div>
106
+ <button type="button" id="updateLocationBtn" class="btn btn-primary shadow-md hover:shadow-lg transition duration-300">
107
+ Update Location
108
+ </button>
109
+ </div>
110
+ </div>
111
+ <!-- Hidden fields to store final lat/lon -->
112
+ <input type="hidden" id="latitude" name="latitude" />
113
+ <input type="hidden" id="longitude" name="longitude" />
114
+ </div>
115
+
116
+ <!-- Weather Data Display -->
117
+ <div class="form-section">
118
+ <h4 class="text-2xl font-semibold mb-4">Weather Data</h4>
119
+ <div id="weatherDataContainer" class="grid grid-cols-1 md:grid-cols-4 gap-4">
120
+ <div class="col-span-full text-center text-gray-600">
121
+ Weather data will appear here after you select or update a location.
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <!-- Soil Data Section -->
127
+ <div class="form-section" id="soilPropertiesTable">
128
+ <h4 class="text-2xl font-semibold mb-4">Soil Nutrient Parameters</h4>
129
+ <div id="soilTableContainer">
130
+ <p class="text-center text-gray-600">Fetching soil data...</p>
131
+ </div>
132
+ </div>
133
+
134
+ <!-- Additional Agricultural Inputs -->
135
+ <div class="form-section">
136
+ <h4 class="text-2xl font-semibold mb-4">Additional Agricultural Inputs</h4>
137
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
138
+ <div>
139
+ <label class="font-medium" for="crop_type">Crop Type:</label>
140
+ <select class="form-control" id="crop_type" name="crop_type">
141
+ <option value="rice">Rice</option>
142
+ <option value="wheat">Wheat</option>
143
+ <option value="maize">Maize</option>
144
+ <option value="cotton">Cotton</option>
145
+ <option value="sugarcane">Sugarcane</option>
146
+ <option value="soybean">Soybean</option>
147
+ <option value="millet">Millet</option>
148
+ <option value="pulses">Pulses</option>
149
+ <option value="potato">Potato</option>
150
+ <option value="tomato">Tomato</option>
151
+ <option value="onion">Onion</option>
152
+ <option value="garlic">Garlic</option>
153
+ <option value="groundnut">Groundnut</option>
154
+ <option value="barley">Barley</option>
155
+ <option value="jute">Jute</option>
156
+ <option value="tea">Tea</option>
157
+ <option value="coffee">Coffee</option>
158
+ <option value="sorghum">Sorghum</option>
159
+ <option value="turmeric">Turmeric</option>
160
+ <option value="ginger">Ginger</option>
161
+ </select>
162
+ </div>
163
+ <div>
164
+ <label class="font-medium" for="sowing_date">Plant Sowing Date:</label>
165
+ <input type="date" class="form-control" id="sowing_date" name="sowing_date" />
166
+ </div>
167
+ <div>
168
+ <label class="font-medium" for="harvest_date">Expected Harvest Date:</label>
169
+ <input type="date" class="form-control" id="harvest_date" name="harvest_date" />
170
+ </div>
171
+ <div>
172
+ <label class="font-medium" for="growth_stage">Current Growth Stage:</label>
173
+ <select class="form-control" id="growth_stage" name="growth_stage">
174
+ <option value="sowing">Sowing</option>
175
+ <option value="germination">Germination</option>
176
+ <option value="vegetative">Vegetative</option>
177
+ <option value="flowering">Flowering</option>
178
+ <option value="fruiting">Fruiting</option>
179
+ <option value="harvest">Harvest</option>
180
+ </select>
181
+ </div>
182
+ <div>
183
+ <label class="font-medium" for="irrigation_freq">Irrigation Frequency:</label>
184
+ <select class="form-control" id="irrigation_freq" name="irrigation_freq">
185
+ <option value="daily">Daily</option>
186
+ <option value="alternate">Alternate Day</option>
187
+ <option value="twice_week">Twice a Week</option>
188
+ <option value="weekly">Weekly</option>
189
+ </select>
190
+ </div>
191
+ <div>
192
+ <label class="font-medium" for="irrigation_method">Irrigation Method:</label>
193
+ <select class="form-control" id="irrigation_method" name="irrigation_method">
194
+ <option value="drip">Drip</option>
195
+ <option value="sprinkler">Sprinkler</option>
196
+ <option value="flood">Flood</option>
197
+ <option value="manual">Manual</option>
198
+ </select>
199
+ </div>
200
+ <div>
201
+ <label class="font-medium" for="soil_type">Soil Type:</label>
202
+ <select class="form-control" id="soil_type" name="soil_type">
203
+ <option value="red">Red</option>
204
+ <option value="black">Black</option>
205
+ <option value="clay">Clay</option>
206
+ <option value="sandy">Sandy</option>
207
+ <option value="loamy">Loamy</option>
208
+ </select>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ <!-- Hidden Fields for passing Weather data to Gemini -->
214
+ <input type="hidden" id="max_temp_hidden" name="max_temp" />
215
+ <input type="hidden" id="min_temp_hidden" name="min_temp" />
216
+ <input type="hidden" id="current_temp_hidden" name="current_temp" />
217
+ <input type="hidden" id="humidity_hidden" name="humidity" />
218
+ <input type="hidden" id="rainfall_hidden" name="rain" />
219
+ <input type="hidden" id="soil_moisture_hidden" name="soil_moisture" />
220
+ <input type="hidden" id="wind_speed_hidden" name="wind_speed" />
221
+ <input type="hidden" id="cloud_cover_hidden" name="cloud_cover" />
222
+
223
+ <div class="form-section text-center">
224
+ <button type="submit" class="btn btn-success btn-lg shadow-xl transform hover:scale-105 transition duration-300">
225
+ Predict Pest Outbreak
226
+ </button>
227
+ </div>
228
+ </form>
229
+ </div>
230
+
231
+ <!-- Scripts -->
232
+ <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
233
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.bundle.min.js"></script>
234
+ <!-- Leaflet JS -->
235
+ <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
236
+ <!-- Leaflet Control Geocoder JS for search -->
237
+ <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
238
+ <script>
239
+ // Initialize the map using Esri's World Imagery (satellite view)
240
+ var map = L.map('map').setView([20.5937, 78.9629], 5);
241
+ L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
242
+ attribution: 'Tiles © Esri',
243
+ maxZoom: 18,
244
+ }).addTo(map);
245
+
246
+ // Explicitly define the geocoder provider using Nominatim
247
+ var geocoder = L.Control.Geocoder.nominatim();
248
+
249
+ // Add geocoder (search bar) to the map with the provider
250
+ L.Control.geocoder({
251
+ geocoder: geocoder,
252
+ defaultMarkGeocode: false
253
+ })
254
+ .on('markgeocode', function(e) {
255
+ var bbox = e.geocode.bbox;
256
+ var center = e.geocode.center;
257
+ map.fitBounds(bbox);
258
+ setMarker(center);
259
+ })
260
+ .addTo(map);
261
+
262
+ var marker;
263
+ function setMarker(latlng) {
264
+ if (marker) {
265
+ marker.setLatLng(latlng);
266
+ } else {
267
+ marker = L.marker(latlng).addTo(map);
268
+ }
269
+ // Update both hidden and manual fields
270
+ document.getElementById('latitude').value = latlng.lat.toFixed(6);
271
+ document.getElementById('longitude').value = latlng.lng.toFixed(6);
272
+ document.getElementById('manual_lat').value = latlng.lat.toFixed(6);
273
+ document.getElementById('manual_lon').value = latlng.lng.toFixed(6);
274
+
275
+ // Fetch weather and soil data
276
+ fetchWeatherData(latlng.lat, latlng.lng);
277
+ fetchSoilProperties(latlng.lat, latlng.lng);
278
+ }
279
+
280
+ map.on('click', function(e) {
281
+ setMarker(e.latlng);
282
+ });
283
+
284
+ // Update location using manual input
285
+ document.getElementById('updateLocationBtn').addEventListener('click', function(){
286
+ const lat = parseFloat(document.getElementById('manual_lat').value);
287
+ const lon = parseFloat(document.getElementById('manual_lon').value);
288
+ if(!isNaN(lat) && !isNaN(lon)){
289
+ const latlng = L.latLng(lat, lon);
290
+ map.setView(latlng, 10);
291
+ setMarker(latlng);
292
+ } else {
293
+ alert("Please enter valid latitude and longitude values.");
294
+ }
295
+ });
296
+
297
+ // Fetch weather data from backend (/get_weather_data)
298
+ function fetchWeatherData(lat, lng) {
299
+ fetch(`/get_weather_data?lat=${lat}&lon=${lng}`)
300
+ .then(response => response.json())
301
+ .then(data => {
302
+ // Update the weather card container
303
+ const container = document.getElementById('weatherDataContainer');
304
+ container.innerHTML = `
305
+ <div class="weather-card">
306
+ <div class="weather-title">Max Temp</div>
307
+ <div>${data.max_temp !== null ? data.max_temp + " °C" : "Not available"}</div>
308
+ </div>
309
+ <div class="weather-card">
310
+ <div class="weather-title">Min Temp</div>
311
+ <div>${data.min_temp !== null ? data.min_temp + " °C" : "Not available"}</div>
312
+ </div>
313
+ <div class="weather-card">
314
+ <div class="weather-title">Current Temp</div>
315
+ <div>${data.current_temp !== null ? data.current_temp + " °C" : "Not available"}</div>
316
+ </div>
317
+ <div class="weather-card">
318
+ <div class="weather-title">Humidity</div>
319
+ <div>${data.humidity !== null ? data.humidity.toFixed(1) + " %" : "Not available"}</div>
320
+ </div>
321
+ <div class="weather-card">
322
+ <div class="weather-title">Rain</div>
323
+ <div>${data.rainfall !== null ? data.rainfall + " mm" : "Not available"}</div>
324
+ </div>
325
+ <div class="weather-card">
326
+ <div class="weather-title">Soil Moisture</div>
327
+ <div>${data.soil_moisture !== null ? data.soil_moisture.toFixed(1) + " %" : "Not available"}</div>
328
+ </div>
329
+ <div class="weather-card">
330
+ <div class="weather-title">Wind Speed</div>
331
+ <div>${data.wind_speed !== null ? data.wind_speed + " km/h" : "Not available"}</div>
332
+ </div>
333
+ <div class="weather-card">
334
+ <div class="weather-title">Cloud Cover</div>
335
+ <div>${data.cloud_cover !== null ? data.cloud_cover.toFixed(1) + " %" : "Not available"}</div>
336
+ </div>
337
+ `;
338
+
339
+ // Also store these values in hidden fields so they pass to Gemini
340
+ document.getElementById('max_temp_hidden').value = data.max_temp ?? "";
341
+ document.getElementById('min_temp_hidden').value = data.min_temp ?? "";
342
+ document.getElementById('current_temp_hidden').value = data.current_temp ?? "";
343
+ document.getElementById('humidity_hidden').value = data.humidity ?? "";
344
+ document.getElementById('rainfall_hidden').value = data.rainfall ?? "";
345
+ document.getElementById('soil_moisture_hidden').value = data.soil_moisture ?? "";
346
+ document.getElementById('wind_speed_hidden').value = data.wind_speed ?? "";
347
+ document.getElementById('cloud_cover_hidden').value = data.cloud_cover ?? "";
348
+ })
349
+ .catch(error => console.error('Error fetching weather data:', error));
350
+ }
351
+
352
+ // Fetch soil properties from backend (/get_soil_properties)
353
+ function fetchSoilProperties(lat, lng) {
354
+ fetch(`/get_soil_properties?lat=${lat}&lon=${lng}`)
355
+ .then(response => response.json())
356
+ .then(data => {
357
+ if(data.soil_properties) {
358
+ let tableHTML = "<table><thead><tr><th>Parameter</th><th>Value</th><th>Unit</th></tr></thead><tbody>";
359
+ data.soil_properties.forEach(function(item) {
360
+ tableHTML += `<tr><td>${item[0]}</td><td>${item[1]}</td><td>${item[2]}</td></tr>`;
361
+ });
362
+ tableHTML += "</tbody></table>";
363
+ document.getElementById("soilTableContainer").innerHTML = tableHTML;
364
+ } else {
365
+ document.getElementById("soilTableContainer").innerHTML = "<p class='text-center text-gray-600'>No soil data available.</p>";
366
+ }
367
+ })
368
+ .catch(error => console.error('Error fetching soil properties:', error));
369
+ }
370
+ </script>
371
+ </body>
372
+ </html>