Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>Farm Geofencing & AI Design</title> | |
| <!-- External CSS --> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" /> | |
| <link href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css" rel="stylesheet" /> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| #map { | |
| height: 70vh; | |
| width: 100%; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| .control-panel { | |
| background: rgba(255,255,255,0.9); | |
| padding: 15px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .loading-overlay { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: rgba(255,255,255,0.8); | |
| display: none; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 9999; | |
| } | |
| .spinner { | |
| width: 50px; | |
| height: 50px; | |
| border: 5px solid #f3f3f3; | |
| border-top: 5px solid #3498db; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100"> | |
| <!-- Loading Overlay --> | |
| <div id="loadingOverlay" class="loading-overlay"> | |
| <div class="spinner"></div> | |
| </div> | |
| <div class="container-fluid py-4"> | |
| <div class="row mb-4"> | |
| <div class="col-12"> | |
| <div class="d-flex justify-content-between align-items-center mb-4"> | |
| <h1 class="text-3xl font-bold">Farm Geofencing & AI Design</h1> | |
| <div class="d-flex gap-2"> | |
| <button id="helpBtn" class="btn btn-info"> | |
| <i class="fas fa-question-circle"></i> Help | |
| </button> | |
| <button id="resetBtn" class="btn btn-warning"> | |
| <i class="fas fa-redo"></i> Reset | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <!-- Controls Panel --> | |
| <div class="col-md-3"> | |
| <!-- Search Location (Optional - keeping for map centering) --> | |
| <div class="control-panel mb-4"> | |
| <h5 class="mb-3">Search Location</h5> | |
| <div class="mb-3"> | |
| <input type="text" id="addressInput" class="form-control mb-2" placeholder="Enter Address"> | |
| <button id="addressSearchBtn" class="btn btn-primary w-100"> | |
| <i class="fas fa-search"></i> Search Address | |
| </button> | |
| </div> | |
| <div class="mb-3"> | |
| <div class="input-group mb-2"> | |
| <input type="number" id="latInput" class="form-control" placeholder="Latitude" step="0.000001"> | |
| <input type="number" id="lngInput" class="form-control" placeholder="Longitude" step="0.000001"> | |
| </div> | |
| <button id="coordSearchBtn" class="btn btn-primary w-100"> | |
| <i class="fas fa-map-marker-alt"></i> Search Coordinates | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Drawing Controls --> | |
| <div class="control-panel mb-4"> | |
| <h5 class="mb-3">Drawing Tools</h5> | |
| <div class="btn-group w-100 mb-2"> | |
| <button id="startDrawingBtn" class="btn btn-success">Start Drawing</button> | |
| <button id="clearDrawingBtn" class="btn btn-danger">Clear</button> | |
| </div> | |
| <div class="form-check mt-2"> | |
| <input class="form-check-input" type="checkbox" id="snapToGridCheck"> | |
| <label class="form-check-label" for="snapToGridCheck">Snap to Grid</label> | |
| </div> | |
| </div> | |
| <!-- Farm Design Inputs --> | |
| <div class="control-panel mb-4"> | |
| <h5 class="mb-3">AI Farm Design</h5> | |
| <div class="mb-3"> | |
| <label for="farmTypeSelect" class="form-label">Farming Type</label> | |
| <select id="farmTypeSelect" class="form-select"> | |
| <option value="Horticulture">Horticulture (Fruits, Vegetables)</option> | |
| <option value="Plantation">Plantation (Trees, Coffee, Tea)</option> | |
| <option value="Poultry">Poultry (Chickens, Ducks)</option> | |
| <option value="Dairy">Dairy (Cows, Goats)</option> | |
| <option value="Mixed Farming">Mixed Farming</option> | |
| </select> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="preferencesInput" class="form-label">Additional Preferences</label> | |
| <textarea id="preferencesInput" class="form-control" rows="3" placeholder="e.g., organic methods, specific crop types, number of animals, water source location..."></textarea> | |
| </div> | |
| <button id="generateDesignBtn" class="btn btn-info w-100"> | |
| <i class="fas fa-magic"></i> Generate Farm Design | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Map & Geofence Data Display --> | |
| <div class="col-md-9"> | |
| <div class="row"> | |
| <div class="col-12"> | |
| <div id="map"></div> | |
| </div> | |
| </div> | |
| <!-- Tabbed Data Display --> | |
| <div class="row mt-4"> | |
| <div class="col-12"> | |
| <div class="card"> | |
| <div class="card-body"> | |
| <ul class="nav nav-tabs" id="dataTabs"> | |
| <li class="nav-item"> | |
| <a class="nav-link active" data-bs-toggle="tab" href="#geofenceInfo">Geofence Info</a> | |
| </li> | |
| <li class="nav-item"> | |
| <a class="nav-link" data-bs-toggle="tab" href="#farmDesignOutput">AI Farm Design</a> | |
| </li> | |
| </ul> | |
| <div class="tab-content mt-3"> | |
| <div class="tab-pane fade show active" id="geofenceInfo"> | |
| <div id="geofenceInfoContent"> | |
| <p>Draw a polygon on the map to see its area and coordinates.</p> | |
| </div> | |
| </div> | |
| <div class="tab-pane fade" id="farmDesignOutput"> | |
| <div id="farmDesignContent"> | |
| <p>Click "Generate Farm Design" after drawing a geofence and selecting your farming type.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Help Modal --> | |
| <div class="modal fade" id="helpModal" tabindex="-1"> | |
| <div class="modal-dialog"> | |
| <div class="modal-content"> | |
| <div class="modal-header"> | |
| <h5 class="modal-title">How to Use the Dashboard</h5> | |
| <button type="button" class="btn-close" data-bs-dismiss="modal"></button> | |
| </div> | |
| <div class="modal-body"> | |
| <h6>Drawing a Farm Area (Geofence)</h6> | |
| <ol> | |
| <li>Click "Start Drawing" to activate polygon drawing mode.</li> | |
| <li>Click on the map to add vertices for your farm's boundary.</li> | |
| <li>Double-click (or click the first point you drew) to complete the polygon.</li> | |
| <li>Once drawn, the farm's area and coordinates will appear in the "Geofence Info" tab.</li> | |
| <li>Use "Clear" to remove the drawn area and its data.</li> | |
| <li>Check "Snap to Grid" to align polygon points to a grid for cleaner shapes.</li> | |
| </ol> | |
| <h6>AI Farm Design</h6> | |
| <ol> | |
| <li>First, draw your farm's geofence on the map.</li> | |
| <li>Select your primary farming type (e.g., Horticulture, Poultry) from the dropdown.</li> | |
| <li>Enter any additional preferences or specific requirements in the text area.</li> | |
| <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> | |
| <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> | |
| </ol> | |
| <h6>Searching Locations</h6> | |
| <ul> | |
| <li>Enter an address or latitude/longitude coordinates and click "Search" to center the map.</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- External Scripts --> | |
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> | |
| <script src="https://kit.fontawesome.com/your-fontawesome-kit.js"></script> <!-- Ensure this points to your Font Awesome kit --> | |
| <script src="https://cdn.jsdelivr.net/npm/toastify-js"></script> | |
| <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBvVLjWmCja331H8SuIZ4UlJdZytuYkC6Y&libraries=drawing,places"></script> | |
| <!-- Custom JavaScript --> | |
| <script> | |
| let map, drawingManager, polygon; | |
| let geocoder; | |
| function initMap() { | |
| map = new google.maps.Map(document.getElementById('map'), { | |
| center: { lat: 20.5937, lng: 78.9629 }, // Default to India | |
| zoom: 5, | |
| mapTypeControl: true, | |
| fullscreenControl: true, | |
| streetViewControl: true, | |
| streetViewControlOptions: { | |
| position: google.maps.ControlPosition.RIGHT_BOTTOM | |
| } | |
| }); | |
| geocoder = new google.maps.Geocoder(); | |
| setupDrawingManager(); | |
| setupEventListeners(); | |
| } | |
| // Toggle draw mode on click | |
| document.getElementById('startDrawingBtn').addEventListener('click', function() { | |
| if (drawingManager.getDrawingMode() == google.maps.drawing.OverlayType.POLYGON) { | |
| drawingManager.setDrawingMode(null); | |
| this.textContent = "Start Drawing"; | |
| } else { | |
| // Clear existing polygon before drawing a new one | |
| clearPolygon(); | |
| drawingManager.setDrawingMode(google.maps.drawing.OverlayType.POLYGON); | |
| this.textContent = "Stop Drawing"; | |
| } | |
| }); | |
| function setupDrawingManager() { | |
| drawingManager = new google.maps.drawing.DrawingManager({ | |
| drawingMode: null, // Start with no drawing mode enabled | |
| drawingControl: false, // Don't show the default drawing controls | |
| polygonOptions: { | |
| fillColor: '#4CAF50', | |
| fillOpacity: 0.3, | |
| strokeWeight: 2, | |
| strokeColor: '#4CAF50', | |
| editable: true // Allow editing after drawing | |
| } | |
| }); | |
| drawingManager.setMap(map); | |
| // Event listener for when a polygon is completed | |
| google.maps.event.addListener(drawingManager, 'polygoncomplete', function(poly) { | |
| // If there was an old polygon, clear it | |
| if (polygon) { | |
| polygon.setMap(null); | |
| } | |
| polygon = poly; | |
| drawingManager.setDrawingMode(null); // Exit drawing mode | |
| document.getElementById('startDrawingBtn').textContent = "Start Drawing"; | |
| if (document.getElementById('snapToGridCheck').checked) { | |
| snapPolygonToGrid(polygon); | |
| } | |
| updateGeofenceData(); | |
| // Add listener for polygon path changes (for editing) | |
| google.maps.event.addListener(polygon.getPath(), 'set_at', updateGeofenceData); | |
| google.maps.event.addListener(polygon.getPath(), 'insert_at', updateGeofenceData); | |
| }); | |
| } | |
| function snapPolygonToGrid(poly) { | |
| const gridSize = 0.0005; // Finer grid for more precision | |
| let path = poly.getPath(); | |
| for (let i = 0; i < path.getLength(); i++) { | |
| let pt = path.getAt(i); | |
| let snappedLat = Math.round(pt.lat() / gridSize) * gridSize; | |
| let snappedLng = Math.round(pt.lng() / gridSize) * gridSize; | |
| path.setAt(i, new google.maps.LatLng(snappedLat, snappedLng)); | |
| } | |
| } | |
| function setupEventListeners() { | |
| document.getElementById('clearDrawingBtn').addEventListener('click', clearAll); | |
| document.getElementById('helpBtn').addEventListener('click', function() { | |
| new bootstrap.Modal(document.getElementById('helpModal')).show(); | |
| }); | |
| document.getElementById('resetBtn').addEventListener('click', function() { | |
| clearAll(); | |
| map.setCenter({ lat: 20.5937, lng: 78.9629 }); // Reset to India | |
| map.setZoom(5); | |
| }); | |
| document.getElementById('addressSearchBtn').addEventListener('click', searchByAddress); | |
| document.getElementById('coordSearchBtn').addEventListener('click', searchByCoordinates); | |
| document.getElementById('snapToGridCheck').addEventListener('change', function() { | |
| if (polygon && this.checked) { | |
| snapPolygonToGrid(polygon); | |
| updateGeofenceData(); // Recalculate area after snapping | |
| } | |
| }); | |
| document.getElementById('generateDesignBtn').addEventListener('click', generateFarmDesign); | |
| } | |
| function clearPolygon() { | |
| if (polygon) { | |
| polygon.setMap(null); | |
| polygon = null; | |
| } | |
| document.getElementById('geofenceInfoContent').innerHTML = '<p>Draw a polygon on the map to see its area and coordinates.</p>'; | |
| document.getElementById('farmDesignContent').innerHTML = '<p>Click "Generate Farm Design" after drawing a geofence and selecting your farming type.</p>'; | |
| } | |
| function clearAll() { | |
| clearPolygon(); | |
| // Reset drawing mode if it was active | |
| drawingManager.setDrawingMode(null); | |
| document.getElementById('startDrawingBtn').textContent = "Start Drawing"; | |
| } | |
| function searchByAddress() { | |
| let address = document.getElementById('addressInput').value; | |
| if (!address) { | |
| showToast("Please enter an address"); | |
| return; | |
| } | |
| geocoder.geocode({ address: address }, function(results, status) { | |
| if (status === google.maps.GeocoderStatus.OK) { | |
| let location = results[0].geometry.location; | |
| map.setCenter(location); | |
| map.setZoom(12); | |
| } else { | |
| showToast("Geocode was not successful: " + status); | |
| } | |
| }); | |
| } | |
| function searchByCoordinates() { | |
| let lat = parseFloat(document.getElementById('latInput').value); | |
| let lng = parseFloat(document.getElementById('lngInput').value); | |
| if (!isNaN(lat) && !isNaN(lng) && lat >= -90 && lat <= 90 && lng >= -180 && lng <= 180) { | |
| map.setCenter({ lat: lat, lng: lng }); | |
| map.setZoom(12); // Zoom in on the specified coordinates | |
| } else { | |
| showToast("Invalid coordinates. Latitude must be between -90 and 90, Longitude between -180 and 180."); | |
| } | |
| } | |
| function updateGeofenceData() { | |
| if (!polygon) { | |
| document.getElementById('geofenceInfoContent').innerHTML = '<p>Draw a polygon on the map to see its area and coordinates.</p>'; | |
| return; | |
| } | |
| showLoading(true); | |
| const path = polygon.getPath(); | |
| const coordinates = []; | |
| path.forEach(function(latLng) { | |
| coordinates.push({ lat: latLng.lat(), lng: latLng.lng() }); | |
| }); | |
| fetch('/calculate_area', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ coordinates: coordinates }), | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| showLoading(false); | |
| const container = document.getElementById('geofenceInfoContent'); | |
| if (data.error) { | |
| container.innerHTML = `<p class="text-danger">Error: ${data.error}</p>`; | |
| showToast(`Error: ${data.error}`); | |
| return; | |
| } | |
| let coordsHtml = '<h6>Coordinates:</h6><ul>'; | |
| data.coordinates.forEach(coord => { | |
| coordsHtml += `<li>Lat: ${coord.lat.toFixed(6)}, Lng: ${coord.lng.toFixed(6)}</li>`; | |
| }); | |
| coordsHtml += '</ul>'; | |
| container.innerHTML = ` | |
| <h6>Area:</h6> | |
| <ul> | |
| <li><strong>${data.area_hectares.toFixed(2)} hectares</strong></li> | |
| <li>${data.area_sq_meters.toFixed(2)} sq meters</li> | |
| <li>${data.area_sq_km.toFixed(2)} sq km</li> | |
| <li>${data.area_acres.toFixed(2)} acres</li> | |
| </ul> | |
| ${coordsHtml} | |
| `; | |
| }) | |
| .catch(error => { | |
| console.error('Error fetching area calculation:', error); | |
| showLoading(false); | |
| document.getElementById('geofenceInfoContent').innerHTML = '<p class="text-danger">Failed to calculate area. Please try again.</p>'; | |
| showToast("Failed to calculate area."); | |
| }); | |
| } | |
| function generateFarmDesign() { | |
| if (!polygon) { | |
| showToast("Please draw a farm area (geofence) on the map first."); | |
| return; | |
| } | |
| showLoading(true); | |
| const path = polygon.getPath(); | |
| const coordinates = []; | |
| path.forEach(function(latLng) { | |
| coordinates.push({ lat: latLng.lat(), lng: latLng.lng() }); | |
| }); | |
| const farmType = document.getElementById('farmTypeSelect').value; | |
| const preferences = document.getElementById('preferencesInput').value; | |
| fetch('/generate_farm_design', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| coordinates: coordinates, | |
| farm_type: farmType, | |
| preferences: preferences | |
| }), | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| showLoading(false); | |
| const container = document.getElementById('farmDesignContent'); | |
| if (data.error) { | |
| container.innerHTML = `<p class="text-danger">Error: ${data.error}</p>`; | |
| showToast(`Error: ${data.error}`); | |
| return; | |
| } | |
| let designHtml = `<h6>AI-Generated Farm Design Plan for ${farmType} Farm:</h6> | |
| <div class="card p-3 mb-3"> | |
| ${data.design_plan.split('\n').map(p => `<p>${p}</p>`).join('')} | |
| </div>`; | |
| if (data.visual_design_prompt) { | |
| designHtml += `<h6 class="mt-4">Prompt for Image Generation AI:</h6> | |
| <div class="card p-3 bg-light"> | |
| <p class="mb-0"><em>"${data.visual_design_prompt}"</em></p> | |
| <small class="text-muted mt-2"> | |
| (Use this prompt with a dedicated text-to-image AI (e.g., DALL-E, Midjourney, Stable Diffusion) to visualize the design. | |
| The current Google Gemini API provides textual advice and prompts for external image generation.) | |
| </small> | |
| </div>`; | |
| } | |
| if (data.map_image_url) { | |
| designHtml += `<h6 class="mt-4">Farm Area Context:</h6> | |
| <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;"> | |
| <small class="text-muted d-block mt-1">Satellite image of the geofenced area used for AI analysis.</small>`; | |
| } | |
| container.innerHTML = designHtml; | |
| showToast("Farm design generated successfully!"); | |
| // Switch to the AI Farm Design tab | |
| new bootstrap.Tab(document.querySelector('#dataTabs a[href="#farmDesignOutput"]')).show(); | |
| }) | |
| .catch(error => { | |
| console.error('Error generating farm design:', error); | |
| showLoading(false); | |
| 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>'; | |
| showToast("Failed to generate farm design."); | |
| }); | |
| } | |
| function showLoading(show) { | |
| document.getElementById('loadingOverlay').style.display = show ? 'flex' : 'none'; | |
| } | |
| function showToast(message) { | |
| Toastify({ | |
| text: message, | |
| duration: 3000, | |
| gravity: "top", | |
| position: "right", | |
| backgroundColor: "#333", | |
| stopOnFocus: true | |
| }).showToast(); | |
| } | |
| window.onload = initMap; | |
| </script> | |
| </body> | |
| </html> |