pranit144 commited on
Commit
42c7a8a
·
verified ·
1 Parent(s): 918956c

Upload 2 files

Browse files
Files changed (2) hide show
  1. ai_studio_code.html +503 -0
  2. ai_studio_code.py +206 -0
ai_studio_code.html ADDED
@@ -0,0 +1,503 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.0" />
6
+ <title>Farm Geofencing & AI Design</title>
7
+ <!-- External CSS -->
8
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" />
9
+ <link href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css" rel="stylesheet" />
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <style>
12
+ #map {
13
+ height: 70vh;
14
+ width: 100%;
15
+ border-radius: 8px;
16
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
17
+ }
18
+ .control-panel {
19
+ background: rgba(255,255,255,0.9);
20
+ padding: 15px;
21
+ border-radius: 8px;
22
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
23
+ }
24
+ .loading-overlay {
25
+ position: fixed;
26
+ top: 0;
27
+ left: 0;
28
+ width: 100%;
29
+ height: 100%;
30
+ background: rgba(255,255,255,0.8);
31
+ display: none;
32
+ justify-content: center;
33
+ align-items: center;
34
+ z-index: 9999;
35
+ }
36
+ .spinner {
37
+ width: 50px;
38
+ height: 50px;
39
+ border: 5px solid #f3f3f3;
40
+ border-top: 5px solid #3498db;
41
+ border-radius: 50%;
42
+ animation: spin 1s linear infinite;
43
+ }
44
+ @keyframes spin {
45
+ 0% { transform: rotate(0deg); }
46
+ 100% { transform: rotate(360deg); }
47
+ }
48
+ </style>
49
+ </head>
50
+ <body class="bg-gray-100">
51
+ <!-- Loading Overlay -->
52
+ <div id="loadingOverlay" class="loading-overlay">
53
+ <div class="spinner"></div>
54
+ </div>
55
+ <div class="container-fluid py-4">
56
+ <div class="row mb-4">
57
+ <div class="col-12">
58
+ <div class="d-flex justify-content-between align-items-center mb-4">
59
+ <h1 class="text-3xl font-bold">Farm Geofencing & AI Design</h1>
60
+ <div class="d-flex gap-2">
61
+ <button id="helpBtn" class="btn btn-info">
62
+ <i class="fas fa-question-circle"></i> Help
63
+ </button>
64
+ <button id="resetBtn" class="btn btn-warning">
65
+ <i class="fas fa-redo"></i> Reset
66
+ </button>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <div class="row">
72
+ <!-- Controls Panel -->
73
+ <div class="col-md-3">
74
+ <!-- Search Location (Optional - keeping for map centering) -->
75
+ <div class="control-panel mb-4">
76
+ <h5 class="mb-3">Search Location</h5>
77
+ <div class="mb-3">
78
+ <input type="text" id="addressInput" class="form-control mb-2" placeholder="Enter Address">
79
+ <button id="addressSearchBtn" class="btn btn-primary w-100">
80
+ <i class="fas fa-search"></i> Search Address
81
+ </button>
82
+ </div>
83
+ <div class="mb-3">
84
+ <div class="input-group mb-2">
85
+ <input type="number" id="latInput" class="form-control" placeholder="Latitude" step="0.000001">
86
+ <input type="number" id="lngInput" class="form-control" placeholder="Longitude" step="0.000001">
87
+ </div>
88
+ <button id="coordSearchBtn" class="btn btn-primary w-100">
89
+ <i class="fas fa-map-marker-alt"></i> Search Coordinates
90
+ </button>
91
+ </div>
92
+ </div>
93
+ <!-- Drawing Controls -->
94
+ <div class="control-panel mb-4">
95
+ <h5 class="mb-3">Drawing Tools</h5>
96
+ <div class="btn-group w-100 mb-2">
97
+ <button id="startDrawingBtn" class="btn btn-success">Start Drawing</button>
98
+ <button id="clearDrawingBtn" class="btn btn-danger">Clear</button>
99
+ </div>
100
+ <div class="form-check mt-2">
101
+ <input class="form-check-input" type="checkbox" id="snapToGridCheck">
102
+ <label class="form-check-label" for="snapToGridCheck">Snap to Grid</label>
103
+ </div>
104
+ </div>
105
+ <!-- Farm Design Inputs -->
106
+ <div class="control-panel mb-4">
107
+ <h5 class="mb-3">AI Farm Design</h5>
108
+ <div class="mb-3">
109
+ <label for="farmTypeSelect" class="form-label">Farming Type</label>
110
+ <select id="farmTypeSelect" class="form-select">
111
+ <option value="Horticulture">Horticulture (Fruits, Vegetables)</option>
112
+ <option value="Plantation">Plantation (Trees, Coffee, Tea)</option>
113
+ <option value="Poultry">Poultry (Chickens, Ducks)</option>
114
+ <option value="Dairy">Dairy (Cows, Goats)</option>
115
+ <option value="Mixed Farming">Mixed Farming</option>
116
+ </select>
117
+ </div>
118
+ <div class="mb-3">
119
+ <label for="preferencesInput" class="form-label">Additional Preferences</label>
120
+ <textarea id="preferencesInput" class="form-control" rows="3" placeholder="e.g., organic methods, specific crop types, number of animals, water source location..."></textarea>
121
+ </div>
122
+ <button id="generateDesignBtn" class="btn btn-info w-100">
123
+ <i class="fas fa-magic"></i> Generate Farm Design
124
+ </button>
125
+ </div>
126
+ </div>
127
+ <!-- Map & Geofence Data Display -->
128
+ <div class="col-md-9">
129
+ <div class="row">
130
+ <div class="col-12">
131
+ <div id="map"></div>
132
+ </div>
133
+ </div>
134
+ <!-- Tabbed Data Display -->
135
+ <div class="row mt-4">
136
+ <div class="col-12">
137
+ <div class="card">
138
+ <div class="card-body">
139
+ <ul class="nav nav-tabs" id="dataTabs">
140
+ <li class="nav-item">
141
+ <a class="nav-link active" data-bs-toggle="tab" href="#geofenceInfo">Geofence Info</a>
142
+ </li>
143
+ <li class="nav-item">
144
+ <a class="nav-link" data-bs-toggle="tab" href="#farmDesignOutput">AI Farm Design</a>
145
+ </li>
146
+ </ul>
147
+ <div class="tab-content mt-3">
148
+ <div class="tab-pane fade show active" id="geofenceInfo">
149
+ <div id="geofenceInfoContent">
150
+ <p>Draw a polygon on the map to see its area and coordinates.</p>
151
+ </div>
152
+ </div>
153
+ <div class="tab-pane fade" id="farmDesignOutput">
154
+ <div id="farmDesignContent">
155
+ <p>Click "Generate Farm Design" after drawing a geofence and selecting your farming type.</p>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Help Modal -->
168
+ <div class="modal fade" id="helpModal" tabindex="-1">
169
+ <div class="modal-dialog">
170
+ <div class="modal-content">
171
+ <div class="modal-header">
172
+ <h5 class="modal-title">How to Use the Dashboard</h5>
173
+ <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
174
+ </div>
175
+ <div class="modal-body">
176
+ <h6>Drawing a Farm Area (Geofence)</h6>
177
+ <ol>
178
+ <li>Click "Start Drawing" to activate polygon drawing mode.</li>
179
+ <li>Click on the map to add vertices for your farm's boundary.</li>
180
+ <li>Double-click (or click the first point you drew) to complete the polygon.</li>
181
+ <li>Once drawn, the farm's area and coordinates will appear in the "Geofence Info" tab.</li>
182
+ <li>Use "Clear" to remove the drawn area and its data.</li>
183
+ <li>Check "Snap to Grid" to align polygon points to a grid for cleaner shapes.</li>
184
+ </ol>
185
+ <h6>AI Farm Design</h6>
186
+ <ol>
187
+ <li>First, draw your farm's geofence on the map.</li>
188
+ <li>Select your primary farming type (e.g., Horticulture, Poultry) from the dropdown.</li>
189
+ <li>Enter any additional preferences or specific requirements in the text area.</li>
190
+ <li>Click "Generate Farm Design". The AI will analyze your farm's location (via a satellite image of the geofenced area) and your input to provide a detailed textual design plan.</li>
191
+ <li>The AI's design plan will appear in the "AI Farm Design" tab. This plan includes layout recommendations and a descriptive prompt for how an image generation AI could visualize the design.</li>
192
+ </ol>
193
+ <h6>Searching Locations</h6>
194
+ <ul>
195
+ <li>Enter an address or latitude/longitude coordinates and click "Search" to center the map.</li>
196
+ </ul>
197
+ </div>
198
+ </div>
199
+ </div>
200
+ </div>
201
+
202
+ <!-- External Scripts -->
203
+ <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
204
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
205
+ <script src="https://kit.fontawesome.com/your-fontawesome-kit.js"></script> <!-- Ensure this points to your Font Awesome kit -->
206
+ <script src="https://cdn.jsdelivr.net/npm/toastify-js"></script>
207
+ <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBvVLjWmCja331H8SuIZ4UlJdZytuYkC6Y&libraries=drawing,places"></script>
208
+
209
+ <!-- Custom JavaScript -->
210
+ <script>
211
+ let map, drawingManager, polygon;
212
+ let geocoder;
213
+
214
+ function initMap() {
215
+ map = new google.maps.Map(document.getElementById('map'), {
216
+ center: { lat: 20.5937, lng: 78.9629 }, // Default to India
217
+ zoom: 5,
218
+ mapTypeControl: true,
219
+ fullscreenControl: true,
220
+ streetViewControl: true,
221
+ streetViewControlOptions: {
222
+ position: google.maps.ControlPosition.RIGHT_BOTTOM
223
+ }
224
+ });
225
+ geocoder = new google.maps.Geocoder();
226
+ setupDrawingManager();
227
+ setupEventListeners();
228
+ }
229
+
230
+ // Toggle draw mode on click
231
+ document.getElementById('startDrawingBtn').addEventListener('click', function() {
232
+ if (drawingManager.getDrawingMode() == google.maps.drawing.OverlayType.POLYGON) {
233
+ drawingManager.setDrawingMode(null);
234
+ this.textContent = "Start Drawing";
235
+ } else {
236
+ // Clear existing polygon before drawing a new one
237
+ clearPolygon();
238
+ drawingManager.setDrawingMode(google.maps.drawing.OverlayType.POLYGON);
239
+ this.textContent = "Stop Drawing";
240
+ }
241
+ });
242
+
243
+ function setupDrawingManager() {
244
+ drawingManager = new google.maps.drawing.DrawingManager({
245
+ drawingMode: null, // Start with no drawing mode enabled
246
+ drawingControl: false, // Don't show the default drawing controls
247
+ polygonOptions: {
248
+ fillColor: '#4CAF50',
249
+ fillOpacity: 0.3,
250
+ strokeWeight: 2,
251
+ strokeColor: '#4CAF50',
252
+ editable: true // Allow editing after drawing
253
+ }
254
+ });
255
+ drawingManager.setMap(map);
256
+
257
+ // Event listener for when a polygon is completed
258
+ google.maps.event.addListener(drawingManager, 'polygoncomplete', function(poly) {
259
+ // If there was an old polygon, clear it
260
+ if (polygon) {
261
+ polygon.setMap(null);
262
+ }
263
+ polygon = poly;
264
+ drawingManager.setDrawingMode(null); // Exit drawing mode
265
+ document.getElementById('startDrawingBtn').textContent = "Start Drawing";
266
+
267
+ if (document.getElementById('snapToGridCheck').checked) {
268
+ snapPolygonToGrid(polygon);
269
+ }
270
+
271
+ updateGeofenceData();
272
+
273
+ // Add listener for polygon path changes (for editing)
274
+ google.maps.event.addListener(polygon.getPath(), 'set_at', updateGeofenceData);
275
+ google.maps.event.addListener(polygon.getPath(), 'insert_at', updateGeofenceData);
276
+ });
277
+ }
278
+
279
+ function snapPolygonToGrid(poly) {
280
+ const gridSize = 0.0005; // Finer grid for more precision
281
+ let path = poly.getPath();
282
+ for (let i = 0; i < path.getLength(); i++) {
283
+ let pt = path.getAt(i);
284
+ let snappedLat = Math.round(pt.lat() / gridSize) * gridSize;
285
+ let snappedLng = Math.round(pt.lng() / gridSize) * gridSize;
286
+ path.setAt(i, new google.maps.LatLng(snappedLat, snappedLng));
287
+ }
288
+ }
289
+
290
+ function setupEventListeners() {
291
+ document.getElementById('clearDrawingBtn').addEventListener('click', clearAll);
292
+ document.getElementById('helpBtn').addEventListener('click', function() {
293
+ new bootstrap.Modal(document.getElementById('helpModal')).show();
294
+ });
295
+ document.getElementById('resetBtn').addEventListener('click', function() {
296
+ clearAll();
297
+ map.setCenter({ lat: 20.5937, lng: 78.9629 }); // Reset to India
298
+ map.setZoom(5);
299
+ });
300
+ document.getElementById('addressSearchBtn').addEventListener('click', searchByAddress);
301
+ document.getElementById('coordSearchBtn').addEventListener('click', searchByCoordinates);
302
+ document.getElementById('snapToGridCheck').addEventListener('change', function() {
303
+ if (polygon && this.checked) {
304
+ snapPolygonToGrid(polygon);
305
+ updateGeofenceData(); // Recalculate area after snapping
306
+ }
307
+ });
308
+ document.getElementById('generateDesignBtn').addEventListener('click', generateFarmDesign);
309
+ }
310
+
311
+ function clearPolygon() {
312
+ if (polygon) {
313
+ polygon.setMap(null);
314
+ polygon = null;
315
+ }
316
+ document.getElementById('geofenceInfoContent').innerHTML = '<p>Draw a polygon on the map to see its area and coordinates.</p>';
317
+ document.getElementById('farmDesignContent').innerHTML = '<p>Click "Generate Farm Design" after drawing a geofence and selecting your farming type.</p>';
318
+ }
319
+
320
+ function clearAll() {
321
+ clearPolygon();
322
+ // Reset drawing mode if it was active
323
+ drawingManager.setDrawingMode(null);
324
+ document.getElementById('startDrawingBtn').textContent = "Start Drawing";
325
+ }
326
+
327
+ function searchByAddress() {
328
+ let address = document.getElementById('addressInput').value;
329
+ if (!address) {
330
+ showToast("Please enter an address");
331
+ return;
332
+ }
333
+ geocoder.geocode({ address: address }, function(results, status) {
334
+ if (status === google.maps.GeocoderStatus.OK) {
335
+ let location = results[0].geometry.location;
336
+ map.setCenter(location);
337
+ map.setZoom(12);
338
+ } else {
339
+ showToast("Geocode was not successful: " + status);
340
+ }
341
+ });
342
+ }
343
+
344
+ function searchByCoordinates() {
345
+ let lat = parseFloat(document.getElementById('latInput').value);
346
+ let lng = parseFloat(document.getElementById('lngInput').value);
347
+ if (!isNaN(lat) && !isNaN(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) {
348
+ map.setCenter({ lat: lat, lng: lng });
349
+ map.setZoom(12); // Zoom in on the specified coordinates
350
+ } else {
351
+ showToast("Invalid coordinates. Latitude must be between -90 and 90, Longitude between -180 and 180.");
352
+ }
353
+ }
354
+
355
+ function updateGeofenceData() {
356
+ if (!polygon) {
357
+ document.getElementById('geofenceInfoContent').innerHTML = '<p>Draw a polygon on the map to see its area and coordinates.</p>';
358
+ return;
359
+ }
360
+
361
+ showLoading(true);
362
+
363
+ const path = polygon.getPath();
364
+ const coordinates = [];
365
+ path.forEach(function(latLng) {
366
+ coordinates.push({ lat: latLng.lat(), lng: latLng.lng() });
367
+ });
368
+
369
+ fetch('/calculate_area', {
370
+ method: 'POST',
371
+ headers: {
372
+ 'Content-Type': 'application/json',
373
+ },
374
+ body: JSON.stringify({ coordinates: coordinates }),
375
+ })
376
+ .then(response => response.json())
377
+ .then(data => {
378
+ showLoading(false);
379
+ const container = document.getElementById('geofenceInfoContent');
380
+ if (data.error) {
381
+ container.innerHTML = `<p class="text-danger">Error: ${data.error}</p>`;
382
+ showToast(`Error: ${data.error}`);
383
+ return;
384
+ }
385
+
386
+ let coordsHtml = '<h6>Coordinates:</h6><ul>';
387
+ data.coordinates.forEach(coord => {
388
+ coordsHtml += `<li>Lat: ${coord.lat.toFixed(6)}, Lng: ${coord.lng.toFixed(6)}</li>`;
389
+ });
390
+ coordsHtml += '</ul>';
391
+
392
+ container.innerHTML = `
393
+ <h6>Area:</h6>
394
+ <ul>
395
+ <li><strong>${data.area_hectares.toFixed(2)} hectares</strong></li>
396
+ <li>${data.area_sq_meters.toFixed(2)} sq meters</li>
397
+ <li>${data.area_sq_km.toFixed(2)} sq km</li>
398
+ <li>${data.area_acres.toFixed(2)} acres</li>
399
+ </ul>
400
+ ${coordsHtml}
401
+ `;
402
+ })
403
+ .catch(error => {
404
+ console.error('Error fetching area calculation:', error);
405
+ showLoading(false);
406
+ document.getElementById('geofenceInfoContent').innerHTML = '<p class="text-danger">Failed to calculate area. Please try again.</p>';
407
+ showToast("Failed to calculate area.");
408
+ });
409
+ }
410
+
411
+ function generateFarmDesign() {
412
+ if (!polygon) {
413
+ showToast("Please draw a farm area (geofence) on the map first.");
414
+ return;
415
+ }
416
+
417
+ showLoading(true);
418
+
419
+ const path = polygon.getPath();
420
+ const coordinates = [];
421
+ path.forEach(function(latLng) {
422
+ coordinates.push({ lat: latLng.lat(), lng: latLng.lng() });
423
+ });
424
+
425
+ const farmType = document.getElementById('farmTypeSelect').value;
426
+ const preferences = document.getElementById('preferencesInput').value;
427
+
428
+ fetch('/generate_farm_design', {
429
+ method: 'POST',
430
+ headers: {
431
+ 'Content-Type': 'application/json',
432
+ },
433
+ body: JSON.stringify({
434
+ coordinates: coordinates,
435
+ farm_type: farmType,
436
+ preferences: preferences
437
+ }),
438
+ })
439
+ .then(response => response.json())
440
+ .then(data => {
441
+ showLoading(false);
442
+ const container = document.getElementById('farmDesignContent');
443
+ if (data.error) {
444
+ container.innerHTML = `<p class="text-danger">Error: ${data.error}</p>`;
445
+ showToast(`Error: ${data.error}`);
446
+ return;
447
+ }
448
+
449
+ let designHtml = `<h6>AI-Generated Farm Design Plan for ${farmType} Farm:</h6>
450
+ <div class="card p-3 mb-3">
451
+ ${data.design_plan.split('\n').map(p => `<p>${p}</p>`).join('')}
452
+ </div>`;
453
+
454
+ if (data.visual_design_prompt) {
455
+ designHtml += `<h6 class="mt-4">Prompt for Image Generation AI:</h6>
456
+ <div class="card p-3 bg-light">
457
+ <p class="mb-0"><em>"${data.visual_design_prompt}"</em></p>
458
+ <small class="text-muted mt-2">
459
+ (Use this prompt with a dedicated text-to-image AI (e.g., DALL-E, Midjourney, Stable Diffusion) to visualize the design.
460
+ The current Google Gemini API provides textual advice and prompts for external image generation.)
461
+ </small>
462
+ </div>`;
463
+ }
464
+
465
+ if (data.map_image_url) {
466
+ designHtml += `<h6 class="mt-4">Farm Area Context:</h6>
467
+ <img src="${data.map_image_url}" alt="Farm Area Satellite Image" class="img-fluid rounded shadow-sm mt-2" style="max-width: 600px; border: 1px solid #ddd;">
468
+ <small class="text-muted d-block mt-1">Satellite image of the geofenced area used for AI analysis.</small>`;
469
+ }
470
+
471
+
472
+ container.innerHTML = designHtml;
473
+ showToast("Farm design generated successfully!");
474
+ // Switch to the AI Farm Design tab
475
+ new bootstrap.Tab(document.querySelector('#dataTabs a[href="#farmDesignOutput"]')).show();
476
+ })
477
+ .catch(error => {
478
+ console.error('Error generating farm design:', error);
479
+ showLoading(false);
480
+ document.getElementById('farmDesignContent').innerHTML = '<p class="text-danger">Failed to generate farm design. Please check console for details and ensure API keys are correct.</p>';
481
+ showToast("Failed to generate farm design.");
482
+ });
483
+ }
484
+
485
+ function showLoading(show) {
486
+ document.getElementById('loadingOverlay').style.display = show ? 'flex' : 'none';
487
+ }
488
+
489
+ function showToast(message) {
490
+ Toastify({
491
+ text: message,
492
+ duration: 3000,
493
+ gravity: "top",
494
+ position: "right",
495
+ backgroundColor: "#333",
496
+ stopOnFocus: true
497
+ }).showToast();
498
+ }
499
+
500
+ window.onload = initMap;
501
+ </script>
502
+ </body>
503
+ </html>
ai_studio_code.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ import math
3
+ import logging
4
+ import os
5
+ import google.generativeai as genai
6
+ import requests
7
+ import base64 # For potentially handling image data, though we're using URLs for Gemini Vision
8
+
9
+ app = Flask(__name__)
10
+ logging.basicConfig(level=logging.INFO)
11
+
12
+ # Configuration – ensure your API keys are valid
13
+ # Retrieve API keys from environment variables
14
+ GEMINI_API_KEY = os.getenv('GEMINI_API_KEY')
15
+ GOOGLE_MAPS_STATIC_API_KEY = os.getenv('GOOGLE_MAPS_STATIC_API_KEY') # For fetching map images
16
+
17
+ # Configure Gemini API
18
+ if GEMINI_API_KEY:
19
+ genai.configure(api_key=GEMINI_API_KEY)
20
+ else:
21
+ logging.error("GEMINI_API_KEY not set. Gemini API functionality will be limited or unavailable.")
22
+
23
+ def validate_coordinates(lat, lon):
24
+ try:
25
+ lat = float(lat)
26
+ lon = float(lon)
27
+ if not (-90 <= lat <= 90 and -180 <= lon <= 180):
28
+ return None, None
29
+ return lat, lon
30
+ except (TypeError, ValueError):
31
+ return None, None
32
+
33
+ @app.route('/')
34
+ def index():
35
+ return render_template('index.html')
36
+
37
+ @app.route('/calculate_area', methods=['POST'])
38
+ def calculate_area():
39
+ """
40
+ Calculates the area of a polygon given its coordinates in square meters.
41
+ Uses the Shoelace formula for area calculation on a sphere.
42
+ """
43
+ data = request.json
44
+ coordinates = data.get('coordinates')
45
+
46
+ if not coordinates or not isinstance(coordinates, list) or len(coordinates) < 3:
47
+ return jsonify({"error": "Invalid polygon coordinates provided. Need at least 3 points."}), 400
48
+
49
+ area_sq_meters = calculate_polygon_area_haversine(coordinates)
50
+ if area_sq_meters is None:
51
+ return jsonify({"error": "Error calculating area"}), 500
52
+
53
+ area_sq_km = area_sq_meters / 1_000_000
54
+ area_acres = area_sq_meters * 0.000247105
55
+ area_hectares = area_sq_meters / 10_000
56
+
57
+ return jsonify({
58
+ "area_sq_meters": area_sq_meters,
59
+ "area_sq_km": area_sq_km,
60
+ "area_acres": area_acres,
61
+ "area_hectares": area_hectares,
62
+ "coordinates": coordinates
63
+ })
64
+
65
+ def calculate_polygon_area_haversine(coords):
66
+ """
67
+ Calculates the area of a spherical polygon using the Haversine formula
68
+ for edge lengths and then applying spherical excess.
69
+ This is an approximation and might not be perfectly accurate for very large polygons.
70
+ For small farm plots, it should be sufficiently accurate.
71
+ """
72
+ R = 6378137 # Earth's radius in meters
73
+ area = 0.0
74
+
75
+ if len(coords) < 3:
76
+ return 0.0
77
+
78
+ for i in range(len(coords)):
79
+ lat1_rad = math.radians(coords[i]['lat'])
80
+ lon1_rad = math.radians(coords[i]['lng'])
81
+ lat2_rad = math.radians(coords[(i + 1) % len(coords)]['lat'])
82
+ lon2_rad = math.radians(coords[(i + 1) % len(coords)]['lng'])
83
+
84
+ # Using Gauss's area formula for spherical polygons (related to spherical excess)
85
+ area += (lon2_rad - lon1_rad) * (2 + math.sin(lat1_rad) + math.sin(lat2_rad))
86
+
87
+ area = area * R * R / 2.0
88
+ return abs(area)
89
+
90
+ def get_polygon_center(coords):
91
+ """Calculates the approximate center of a polygon."""
92
+ if not coords:
93
+ return None
94
+ lat_sum = sum(p['lat'] for p in coords)
95
+ lon_sum = sum(p['lng'] for p in coords)
96
+ return {'lat': lat_sum / len(coords), 'lng': lon_sum / len(coords)}
97
+
98
+ @app.route('/generate_farm_design', methods=['POST'])
99
+ def generate_farm_design():
100
+ """
101
+ Generates a farm design plan using Gemini based on geofenced area,
102
+ farm type, and farmer's preferences.
103
+ """
104
+ data = request.json
105
+ coordinates = data.get('coordinates')
106
+ farm_type = data.get('farm_type')
107
+ preferences = data.get('preferences')
108
+
109
+ if not coordinates or not isinstance(coordinates, list) or len(coordinates) < 3:
110
+ return jsonify({"error": "Invalid polygon coordinates provided for design. Need at least 3 points."}), 400
111
+ if not farm_type:
112
+ return jsonify({"error": "Farm type is required."}), 400
113
+ if not GOOGLE_MAPS_STATIC_API_KEY:
114
+ return jsonify({"error": "Google Maps Static API Key not configured on the server."}), 500
115
+ if not GEMINI_API_KEY:
116
+ return jsonify({"error": "Gemini API Key not configured on the server."}), 500
117
+
118
+ try:
119
+ # 1. Get the center of the polygon for the static map image
120
+ center_point = get_polygon_center(coordinates)
121
+ if not center_point:
122
+ return jsonify({"error": "Could not determine polygon center for map image."}), 500
123
+
124
+ # Construct path for the polygon on the static map
125
+ path_str = "color:0x4CAF50FF|weight:2|fillcolor:0x4CAF504C"
126
+ for coord in coordinates:
127
+ path_str += f"|{coord['lat']},{coord['lng']}"
128
+
129
+ # Get Google Static Map image URL
130
+ # Adjust zoom level based on area if possible, for simplicity using fixed for now.
131
+ # Max width/height for static maps is typically 640x640.
132
+ map_image_url = (
133
+ f"https://maps.googleapis.com/maps/api/staticmap?center={center_point['lat']},{center_point['lng']}"
134
+ f"&zoom=15&size=640x640&maptype=satellite&markers=color:red%7C{center_point['lat']},{center_point['lng']}"
135
+ f"&path={path_str}"
136
+ f"&key={GOOGLE_MAPS_STATIC_API_KEY}"
137
+ )
138
+ logging.info(f"Generated Static Map URL: {map_image_url}")
139
+
140
+ # 2. Prepare the prompt for Gemini Pro Vision
141
+ model = genai.GenerativeModel('gemini-pro-vision')
142
+
143
+ # Craft a comprehensive prompt for the AI
144
+ user_prompt = (
145
+ f"I am a farmer planning to set up a '{farm_type}' farm on the enclosed land shown in the satellite image. "
146
+ f"My additional preferences are: '{preferences}'. "
147
+ f"Please analyze the terrain in the image and provide a detailed, optimal farm layout plan. "
148
+ f"Include recommendations for:"
149
+ f"\n- **Overall layout design (e.g., zones for different activities, orientation)**"
150
+ 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).**"
151
+ f"\n- **Resource management (water, shade, sun exposure, potential waste management).**"
152
+ f"\n- **Accessibility (paths, roads).**"
153
+ f"\n- **Any terrain-specific considerations from the image.**"
154
+ 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.')."
155
+ )
156
+
157
+ # 3. Call Gemini Pro Vision
158
+ # Gemini Pro Vision can take image URLs directly
159
+ response = model.generate_content([user_prompt, {'mime_type': 'image/jpeg', 'image_url': map_image_url}])
160
+
161
+ design_plan = response.text
162
+ logging.info(f"Gemini response: {design_plan}")
163
+
164
+ # Extract the image generation prompt from Gemini's response if it followed the instruction
165
+ image_gen_prompt_match = ""
166
+ # A simple heuristic to find the image prompt if Gemini puts it at the end
167
+ if "descriptive prompt" in design_plan.lower():
168
+ parts = design_plan.rsplit("descriptive prompt:", 1)
169
+ if len(parts) > 1:
170
+ design_plan = parts[0].strip()
171
+ image_gen_prompt_match = parts[1].strip()
172
+
173
+ if not image_gen_prompt_match and "text-to-image AI" in design_plan:
174
+ # Fallback: if Gemini embeds it differently
175
+ start_index = design_plan.lower().find("text-to-image ai")
176
+ if start_index != -1:
177
+ end_index = design_plan.find(".", start_index + len("text-to-image ai"))
178
+ if end_index != -1:
179
+ image_gen_prompt_match = design_plan[start_index:end_index+1]
180
+ # Try to remove it from the main design_plan if it's found there
181
+ design_plan = design_plan.replace(image_gen_prompt_match, "").strip()
182
+
183
+
184
+ return jsonify({
185
+ "design_plan": design_plan,
186
+ "visual_design_prompt": image_gen_prompt_match, # This is the prompt for another image gen AI
187
+ "map_image_url": map_image_url # Optionally return the static map URL for context
188
+ })
189
+
190
+ except Exception as e:
191
+ logging.error(f"Error generating farm design: {str(e)}")
192
+ # More detailed error handling for API specific issues could be added
193
+ if "400 Bad Request" in str(e) and "API key" in str(e):
194
+ return jsonify({"error": "Gemini API Key might be invalid or improperly configured.", "details": str(e)}), 500
195
+ return jsonify({"error": "Failed to generate farm design plan", "details": str(e)}), 503
196
+
197
+ @app.errorhandler(404)
198
+ def not_found_error(error):
199
+ return jsonify({"error": "Resource not found"}), 404
200
+
201
+ @app.errorhandler(500)
202
+ def internal_error(error):
203
+ return jsonify({"error": "Internal server error"}), 500
204
+
205
+ if __name__ == '__main__':
206
+ app.run(debug=True, port=5000)