plantsofrajasthan

Plants Location Record

#exportBtn { background-color: #007bff; } #clearBtn { background-color: #dc3545; } #addSpeciesBtn { background-color: #28a745; padding: 6px 12px; font-size: 0.85em; margin-top: 5px; } #addSpeciesBtn:hover { background-color: #218838; } .status { margin-top: 10px; padding: 10px; border-radius: 4px; } .success { background-color: #d4edda; color: #155724; } .error { background-color: #f8d7da; color: #721c24; } .warning { background-color: #fff3cd; color: #856404; } /* Mobile responsive table styles */ @media screen and (max-width: 768px) { table, thead, tbody, th, td, tr { display: block; } thead tr { position: absolute; top: -9999px; left: -9999px; } tr { border: 1px solid #ccc; margin-bottom: 10px; padding: 10px; background-color: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } td { border: none; border-bottom: 1px solid #eee; position: relative; padding-left: 35%; padding-top: 10px; padding-bottom: 10px; word-wrap: break-word; } td:before { content: attr(data-label) ": "; position: absolute; left: 6px; width: 30%; padding-right: 10px; white-space: nowrap; font-weight: bold; color: #333; } td:last-child { border-bottom: none; } } /* Very small screens */ @media screen and (max-width: 480px) { #exportBtn, #clearBtn { width: calc(50% - 5px); display: inline-block; margin-right: 10px; } #clearBtn { margin-right: 0; } } .hidden { display: none; } .location-info { font-size: 0.9em; color: #666; margin-top: 5px; } /* Modal styles */ .modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 1000; } .modal-content { background-color: white; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); text-align: center; max-width: 400px; width: 90%; position: relative; animation: modalFadeIn 0.3s ease-out; } @keyframes modalFadeIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } .modal-icon { font-size: 48px; color: #4CAF50; margin-bottom: 15px; } .modal-title { font-size: 1.4em; font-weight: bold; color: #333; margin-bottom: 10px; } .modal-message { color: #666; margin-bottom: 20px; line-height: 1.4; } .modal-details { background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px; text-align: left; } .modal-details strong { color: #333; } .modal-close { background-color: #4CAF50; color: white; border: none; padding: 10px 30px; border-radius: 6px; cursor: pointer; font-size: 1em; } .modal-close:hover { background-color: #45a049; } /* Add species modal specific styles */ .add-species-modal { text-align: left; } .modal-form-group { margin-bottom: 15px; } .modal-form-group label { display: block; margin-bottom: 5px; font-weight: bold; } .modal-form-group input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; } .modal-buttons { display: flex; justify-content: space-between; margin-top: 20px; } .modal-cancel { background-color: #6c757d; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; } .modal-cancel:hover { background-color: #5a6268; } .modal-add { background-color: #28a745; color: white; border: none; padding: 10px 20px; border-radius: 6px; cursor: pointer; font-size: 1em; } .modal-add:hover { background-color: #218838; }
Area Name: Area by Coordinates
Plant Species: Select a plant species + Add Species
Latitude:
Longitude:
Remarks:
Get Current Location Submit
Note: Location access requires HTTPS or localhost. If location doesn't work, manually enter coordinates.

Recorded Plant Locations

Area Plant Species Latitude Longitude Remarks Date
Export CSV Clear Data
document.addEventListener('DOMContentLoaded', function() { // DOM Elements const locationBtn = document.getElementById('getLocation'); const latitudeInput = document.getElementById('latitude'); const longitudeInput = document.getElementById('longitude'); const statusDiv = document.getElementById('status'); const form = document.getElementById('locationForm'); const dataSection = document.getElementById('dataSection'); const tableBody = document.getElementById('tableBody'); const exportBtn = document.getElementById('exportBtn'); const clearBtn = document.getElementById('clearBtn'); const areaByCoordsBtn = document.getElementById('areaByCoords'); const areaInput = document.getElementById('area'); const addSpeciesBtn = document.getElementById('addSpeciesBtn'); const plantSpeciesSelect = document.getElementById('plantSpecies'); // Storage keys const STORAGE_KEY = 'simplePlantLocations'; const SPECIES_STORAGE_KEY = 'plantSpeciesList'; // Arrays to store data let plantEntries = []; let customSpecies = []; // Default species (always available) const defaultSpecies = [ ]; // Load existing data on page load loadData(); loadCustomSpecies(); updateSpeciesDropdown(); updateTable(); // Load custom species from localStorage function loadCustomSpecies() { try { const data = JSON.parse(localStorage.getItem(SPECIES_STORAGE_KEY) || '[]'); customSpecies = data; } catch (error) { console.error('Error loading custom species:', error); customSpecies = []; } } // Save custom species to localStorage function saveCustomSpecies() { try { localStorage.setItem(SPECIES_STORAGE_KEY, JSON.stringify(customSpecies)); return true; } catch (error) { console.error('Error saving custom species:', error); return false; } } // Update the species dropdown with default + custom species function updateSpeciesDropdown() { const currentValue = plantSpeciesSelect.value; // Clear existing options except the first placeholder while (plantSpeciesSelect.children.length > 1) { plantSpeciesSelect.removeChild(plantSpeciesSelect.lastChild); } // Combine default and custom species, remove duplicates const allSpecies = [...defaultSpecies, ...customSpecies]; const uniqueSpecies = [...new Set(allSpecies)].sort(); // Add options to dropdown uniqueSpecies.forEach(species => { const option = document.createElement('option'); option.value = species; option.textContent = species; plantSpeciesSelect.appendChild(option); }); // Restore selected value if it still exists if (currentValue && uniqueSpecies.includes(currentValue)) { plantSpeciesSelect.value = currentValue; } } // Show add species modal function showAddSpeciesModal() { const modalHTML = `
Add New Plant Species
Enter the name of the new plant species you want to add to the dropdown.
Species Name:
Cancel Add Species
`; document.body.insertAdjacentHTML('beforeend', modalHTML); // Focus on input setTimeout(() => { document.getElementById('newSpeciesName').focus(); }, 100); // Handle Enter key document.getElementById('newSpeciesName').addEventListener('keypress', function(e) { if (e.key === 'Enter') { addNewSpecies(); } }); // Handle Escape key document.addEventListener('keydown', handleEscapeKey); } // Handle escape key to close modal function handleEscapeKey(e) { if (e.key === 'Escape') { closeAddSpeciesModal(); } } // Close add species modal function closeAddSpeciesModal() { const modal = document.getElementById('addSpeciesModal'); if (modal) { document.removeEventListener('keydown', handleEscapeKey); modal.style.opacity = '0'; modal.style.transform = 'scale(0.8)'; setTimeout(() => modal.remove(), 300); } } // Add new species to the list function addNewSpecies() { const input = document.getElementById('newSpeciesName'); const speciesName = input.value.trim(); if (!speciesName) { showStatus('Please enter a species name', 'error'); input.focus(); return; } // Check if species already exists (case insensitive) const allSpecies = [...defaultSpecies, ...customSpecies]; const exists = allSpecies.some(species => species.toLowerCase() === speciesName.toLowerCase() ); if (exists) { showStatus('This species already exists in the list', 'error'); input.focus(); input.select(); return; } // Add to custom species array customSpecies.push(speciesName); // Save to localStorage if (saveCustomSpecies()) { updateSpeciesDropdown(); plantSpeciesSelect.value = speciesName; // Select the newly added species showStatus(`Species "${speciesName}" added successfully!`, 'success'); closeAddSpeciesModal(); } else { showStatus('Error saving species. Please try again.', 'error'); } } // Make functions globally accessible for onclick handlers window.closeAddSpeciesModal = closeAddSpeciesModal; window.addNewSpecies = addNewSpecies; // Check if geolocation is available function checkGeolocationSupport() { if (!navigator.geolocation) { showStatus('Geolocation is not supported by your browser. Please enter coordinates manually.', 'warning'); makeCoordinatesEditable(); return false; } // Check if we're on HTTPS or localhost if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') { showStatus('Location services require HTTPS. You can manually enter coordinates.', 'warning'); makeCoordinatesEditable(); return false; } return true; } // Make coordinate inputs editable for manual entry function makeCoordinatesEditable() { latitudeInput.removeAttribute('readonly'); longitudeInput.removeAttribute('readonly'); latitudeInput.placeholder = 'Enter latitude (e.g., 26.9124)'; longitudeInput.placeholder = 'Enter longitude (e.g., 75.7873)'; // Add input validation latitudeInput.addEventListener('input', validateCoordinates); longitudeInput.addEventListener('input', validateCoordinates); } // Validate coordinate inputs function validateCoordinates() { const lat = parseFloat(latitudeInput.value); const lon = parseFloat(longitudeInput.value); if (latitudeInput.value && (isNaN(lat) || lat < -90 || lat > 90)) { latitudeInput.style.borderColor = '#dc3545'; } else { latitudeInput.style.borderColor = '#ddd'; } if (longitudeInput.value && (isNaN(lon) || lon < -180 || lon > 180)) { longitudeInput.style.borderColor = '#dc3545'; } else { longitudeInput.style.borderColor = '#ddd'; } } // Save data to localStorage function saveData() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(plantEntries)); return true; } catch (error) { console.error('Storage error:', error); return false; } } // Load data from localStorage function loadData() { try { const data = localStorage.getItem(STORAGE_KEY); if (data) { plantEntries = JSON.parse(data); } else { plantEntries = []; } } catch (error) { console.error('Error loading data:', error); plantEntries = []; } } // Format date for display function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleString(); } // Format date for CSV - escape commas and quotes function formatDateForCSV(dateString) { const date = new Date(dateString); // Format as YYYY-MM-DD HH:MM:SS return date.toISOString().replace('T', ' ').substring(0, 19); } // CSV escape function to handle fields with commas or quotes function escapeCSV(field) { if (field === null || field === undefined) { return ''; } field = String(field); // If field contains commas, quotes, or newlines, wrap in quotes if (field.includes(',') || field.includes('"') || field.includes('\n')) { // Double up any quotes field = field.replace(/"/g, '""'); // Wrap in quotes return `"${field}"`; } return field; } // Get current location with improved error handling function getCurrentLocation() { if (!checkGeolocationSupport()) { return; } locationBtn.disabled = true; locationBtn.textContent = 'Getting Location...'; showStatus('Fetching your location... This may take a few seconds.', 'status'); const options = { enableHighAccuracy: true, timeout: 10000, // 10 seconds timeout maximumAge: 60000 // Accept cached position up to 1 minute old }; navigator.geolocation.getCurrentPosition( function(position) { const lat = position.coords.latitude.toFixed(6); const lon = position.coords.longitude.toFixed(6); latitudeInput.value = lat; longitudeInput.value = lon; const accuracy = position.coords.accuracy; showStatus(`Location fetched successfully! (Accuracy: ±${Math.round(accuracy)}m)`, 'success'); locationBtn.disabled = false; locationBtn.textContent = 'Get Current Location'; }, function(error) { let errorMessage = 'Error getting location: '; switch(error.code) { case error.PERMISSION_DENIED: errorMessage += 'Location permission denied. Please allow location access or enter coordinates manually.'; makeCoordinatesEditable(); break; case error.POSITION_UNAVAILABLE: errorMessage += 'Location information unavailable. Please enter coordinates manually.'; makeCoordinatesEditable(); break; case error.TIMEOUT: errorMessage += 'Location request timed out. Please try again or enter coordinates manually.'; makeCoordinatesEditable(); break; default: errorMessage += 'Unknown error occurred. Please enter coordinates manually.'; makeCoordinatesEditable(); } showStatus(errorMessage, 'error'); locationBtn.disabled = false; locationBtn.textContent = 'Get Current Location'; }, options ); } // Show status message function showStatus(message, type) { statusDiv.textContent = message; statusDiv.className = 'status ' + type; if (type === 'success') { setTimeout(() => { statusDiv.textContent = ''; statusDiv.className = 'status'; }, 5000); } } // Update table with entries function updateTable() { tableBody.innerHTML = ''; if (plantEntries.length === 0) { dataSection.classList.add('hidden'); return; } const fragment = document.createDocumentFragment(); // Sort entries by timestamp (most recent first) const sortedEntries = [...plantEntries].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)); sortedEntries.forEach(entry => { const row = document.createElement('tr'); // Create cells with data labels for mobile const areaCell = document.createElement('td'); areaCell.textContent = entry.area; areaCell.setAttribute('data-label', 'Area'); const speciesCell = document.createElement('td'); speciesCell.textContent = entry.plantSpecies; speciesCell.setAttribute('data-label', 'Plant Species'); const latCell = document.createElement('td'); latCell.textContent = entry.latitude; latCell.setAttribute('data-label', 'Latitude'); const longCell = document.createElement('td'); longCell.textContent = entry.longitude; longCell.setAttribute('data-label', 'Longitude'); const remarksCell = document.createElement('td'); remarksCell.textContent = entry.remarks || '-'; remarksCell.setAttribute('data-label', 'Remarks'); const dateCell = document.createElement('td'); dateCell.textContent = formatDate(entry.timestamp); dateCell.setAttribute('data-label', 'Date'); // Append cells to row row.appendChild(areaCell); row.appendChild(speciesCell); row.appendChild(latCell); row.appendChild(longCell); row.appendChild(remarksCell); row.appendChild(dateCell); fragment.appendChild(row); }); tableBody.appendChild(fragment); dataSection.classList.remove('hidden'); } // Export data to CSV function exportToCSV() { if (plantEntries.length === 0) { showStatus('No data to export', 'error'); return; } // Create CSV content let csvContent = "data:text/csv;charset=utf-8,"; csvContent += "Area,Plant Species,Latitude,Longitude,Remarks,Date\n"; plantEntries.forEach(entry => { const formattedDate = formatDateForCSV(entry.timestamp); csvContent += [ escapeCSV(entry.area), escapeCSV(entry.plantSpecies), escapeCSV(entry.latitude), escapeCSV(entry.longitude), escapeCSV(entry.remarks || ''), escapeCSV(formattedDate) ].join(',') + '\n'; }); // Create download link const encodedUri = encodeURI(csvContent); const link = document.createElement("a"); link.setAttribute("href", encodedUri); link.setAttribute("download", "plant-locations.csv"); document.body.appendChild(link); // Trigger download link.click(); document.body.removeChild(link); showStatus('CSV exported!', 'success'); } // Clear all data function clearData() { if (confirm('Are you sure you want to delete all data? This cannot be undone.')) { localStorage.removeItem(STORAGE_KEY); plantEntries = []; updateTable(); showStatus('All data cleared', 'success'); } } // Show success popup function showSuccessPopup(entry) { // Create popup HTML const popupHTML = `
✅
Entry Recorded Successfully!
Your plant location has been saved
Area: ${entry.area || 'Not specified'}
Plant Species: ${entry.plantSpecies}
Location: ${entry.latitude}, ${entry.longitude}
Remarks: ${entry.remarks || 'None'}
Time: ${formatDate(entry.timestamp)}
Continue
`; // Add popup to body document.body.insertAdjacentHTML('beforeend', popupHTML); // Auto-close after 5 seconds setTimeout(closeSuccessPopup, 5000); } // Close success popup function closeSuccessPopup() { const popup = document.getElementById('successPopup'); if (popup) { popup.style.opacity = '0'; popup.style.transform = 'scale(0.8)'; setTimeout(() => popup.remove(), 300); } } // Make closeSuccessPopup globally accessible window.closeSuccessPopup = closeSuccessPopup; // Fill area name with address from coordinates async function fillAreaByCoordinates() { const lat = latitudeInput.value.trim(); const lon = longitudeInput.value.trim(); if (!lat || !lon) { showStatus('Please get location or enter coordinates first', 'error'); return; } // Validate coordinates const latNum = parseFloat(lat); const lonNum = parseFloat(lon); if (isNaN(latNum) || latNum < -90 || latNum > 90 || isNaN(lonNum) || lonNum < -180 || lonNum > 180) { showStatus('Invalid coordinates. Please check latitude and longitude values.', 'error'); return; } // Disable button and show loading areaByCoordsBtn.disabled = true; areaByCoordsBtn.textContent = 'Getting Address...'; showStatus('Fetching address from coordinates...', 'status'); try { // Use OpenStreetMap Nominatim API for reverse geocoding (free, no API key required) const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latNum}&lon=${lonNum}&zoom=18&addressdetails=1`, { headers: { 'User-Agent': 'PlantLocationForm/1.0' } }); if (!response.ok) { throw new Error('Geocoding service unavailable'); } const data = await response.json(); if (data && data.display_name) { // Try to extract a meaningful location name let areaName = ''; if (data.address) { // Priority order for location naming const locationParts = [ // Tourist and historic sites (highest priority) data.address.tourism, // Tourist attractions like "Jaigarh Fort" data.address.historic, // Historic sites // Amenities and landmarks data.address.amenity, // Parks: "Ram Niwas Garden", monuments, hospitals: "SMS Hospital" data.address.leisure, // Parks, gardens: "Central Park", "Sisodia Rani Garden" data.address.natural, // Natural features: hills, lakes data.address.place_of_worship, // Religious places // Buildings and structures data.address.building, // Specific buildings: "Birla Auditorium" data.address.house_name, // Named houses or buildings data.address.building_name, // Building names // Government and institutions data.address.government, // Government buildings: "Raj Bhawan", "Secretariat" data.address.office, // Office buildings, complexes data.address.university, // Universities and colleges: "University of Rajasthan" data.address.college, // Colleges data.address.school, // Educational institutions data.address.hospital, // Hospitals and clinics // Commercial areas data.address.shop, // Major shopping areas: "Johari Bazaar" data.address.marketplace, // Markets and bazaars data.address.mall, // Shopping malls: "World Trade Park" data.address.commercial, // Commercial buildings data.address.hotel, // Hotels: "Rambagh Palace Hotel" data.address.guest_house, // Guest houses data.address.restaurant, // Notable restaurants data.address.cafe, // Cafes and eateries data.address.bank, // Banks and financial institutions data.address.pharmacy, // Pharmacies // Infrastructure data.address.transport, // Transport hubs data.address.fuel, // Petrol stations data.address.parking, // Parking areas // Land use and areas data.address.landuse, // Specific land use areas data.address.industrial, // Industrial areas data.address.residential, // Residential complexes data.address.farm, // Farms and agricultural areas data.address.allotments, // Garden allotments // Geographic divisions (lowest priority) data.address.neighbourhood, // Local neighborhoods: "Civil Lines", "Malviya Nagar" data.address.quarter, // City quarters data.address.suburb, // Suburbs: "Vaishali Nagar", "Mansarovar" data.address.hamlet, // Small settlements data.address.isolated_dwelling, // Isolated houses data.address.village, // Villages: "Kukas", "Goner" data.address.town, // Towns data.address.city_district, // City districts: "Jaipur North", "Jaipur South" data.address.municipality, // Municipalities data.address.borough, // Boroughs data.address.county, // Counties data.address.district, // Districts: "Jaipur District" data.address.province, // Provinces data.address.region, // Regions data.address.city // City ].filter(Boolean); if (locationParts.length > 0) { areaName = locationParts[0]; // Add additional context if it's a generic name if (locationParts.length > 1 && locationParts[0].length < 10) { areaName += `, ${locationParts[1]}`; } } } // Fallback to display name if no specific address parts found if (!areaName) { // Use the first part of display name (usually the most specific location) const parts = data.display_name.split(','); areaName = parts[0].trim(); // If first part is just a number (like house number), use first two parts if (parts.length > 1 && /^\d+$/.test(areaName)) { areaName = `${areaName}, ${parts[1].trim()}`; } } areaInput.value = areaName; showStatus('Address fetched successfully!', 'success'); } else { // Fallback to coordinate format if no address found const formattedLat = Math.abs(latNum).toFixed(4) + (latNum >= 0 ? 'N' : 'S'); const formattedLon = Math.abs(lonNum).toFixed(4) + (lonNum >= 0 ? 'E' : 'W'); areaInput.value = `${formattedLat}, ${formattedLon}`; showStatus('No address found. Used coordinate format instead.', 'warning'); } } catch (error) { console.error('Geocoding error:', error); // Fallback to coordinate format const formattedLat = Math.abs(latNum).toFixed(4) + (latNum >= 0 ? 'N' : 'S'); const formattedLon = Math.abs(lonNum).toFixed(4) + (lonNum >= 0 ? 'E' : 'W'); areaInput.value = `${formattedLat}, ${formattedLon}`; showStatus('Could not fetch address. Used coordinate format instead.', 'warning'); } // Re-enable button areaByCoordsBtn.disabled = false; areaByCoordsBtn.textContent = 'Area by Coordinates'; } // Event listeners locationBtn.addEventListener('click', getCurrentLocation); addSpeciesBtn.addEventListener('click', showAddSpeciesModal); form.addEventListener('submit', function(e) { e.preventDefault(); const areaValue = document.getElementById('area').value.trim(); const plantSpeciesValue = document.getElementById('plantSpecies').value; const remarksValue = document.getElementById('remarks').value.trim(); const latValue = latitudeInput.value.trim(); const lonValue = longitudeInput.value.trim(); if (!plantSpeciesValue) { showStatus('Please select a plant species', 'error'); return; } if (!latValue || !lonValue) { showStatus('Please get your location first or enter coordinates manually', 'error'); return; } // Validate coordinates const lat = parseFloat(latValue); const lon = parseFloat(lonValue); if (isNaN(lat) || lat < -90 || lat > 90) { showStatus('Invalid latitude. Must be between -90 and 90', 'error'); return; } if (isNaN(lon) || lon < -180 || lon > 180) { showStatus('Invalid longitude. Must be between -180 and 180', 'error'); return; } // Create new entry const newEntry = { area: areaValue, plantSpecies: plantSpeciesValue, latitude: lat.toFixed(6), longitude: lon.toFixed(6), remarks: remarksValue, timestamp: new Date().toISOString() }; // Add to array and save plantEntries.push(newEntry); if (saveData()) { updateTable(); showSuccessPopup(newEntry); // Reset form document.getElementById('area').value = ''; document.getElementById('plantSpecies').value = ''; document.getElementById('remarks').value = ''; latitudeInput.value = ''; longitudeInput.value = ''; } }); exportBtn.addEventListener('click', exportToCSV); clearBtn.addEventListener('click', clearData); areaByCoordsBtn.addEventListener('click', fillAreaByCoordinates); });