Farmplanner / templates /index.html
pranit144's picture
Rename ai_studio_code.html to templates/index.html
f94a264 verified
<!DOCTYPE html>
<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>