Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive GIS Application</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> | |
| <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> | |
| <script src="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js"></script> | |
| <link rel="stylesheet" href="https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css" /> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| #map { | |
| height: 70vh; | |
| width: 100%; | |
| border-radius: 0.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| } | |
| .control-panel { | |
| background-color: rgba(255, 255, 255, 0.9); | |
| border-radius: 0.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); | |
| padding: 1rem; | |
| margin-bottom: 1rem; | |
| } | |
| .toggle-btn { | |
| transition: all 0.3s ease; | |
| } | |
| .toggle-btn.active { | |
| background-color: #3b82f6; | |
| color: white; | |
| } | |
| .measurement-result { | |
| background-color: rgba(255, 255, 255, 0.9); | |
| border-radius: 0.5rem; | |
| padding: 0.5rem 1rem; | |
| margin-top: 0.5rem; | |
| font-size: 0.9rem; | |
| } | |
| .coordinates-display { | |
| position: absolute; | |
| bottom: 10px; | |
| right: 10px; | |
| background-color: rgba(255, 255, 255, 0.9); | |
| padding: 0.5rem 1rem; | |
| border-radius: 0.5rem; | |
| font-size: 0.8rem; | |
| z-index: 1000; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| } | |
| @media (max-width: 768px) { | |
| #map { | |
| height: 60vh; | |
| } | |
| .coordinates-display { | |
| font-size: 0.7rem; | |
| padding: 0.3rem 0.6rem; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-3xl font-bold text-blue-700 mb-2">Interactive GIS Application</h1> | |
| <p class="text-gray-600">Measure distances, calculate areas, and view coordinates on the map</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-4 gap-6"> | |
| <div class="lg:col-span-1 space-y-4"> | |
| <div class="control-panel"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800 flex items-center"> | |
| <i class="fas fa-tools mr-2"></i> Control Panel | |
| </h2> | |
| <div class="mb-4"> | |
| <h3 class="font-medium text-gray-700 mb-2 flex items-center"> | |
| <i class="fas fa-ruler-combined mr-2"></i> Distance Measurement | |
| </h3> | |
| <button id="distanceToggle" class="toggle-btn w-full py-2 px-4 bg-gray-200 rounded-lg flex items-center justify-between"> | |
| <span>Turn On</span> | |
| <i class="fas fa-toggle-off"></i> | |
| </button> | |
| <div id="distanceResult" class="measurement-result hidden"> | |
| <div class="flex justify-between"> | |
| <span>Total Distance:</span> | |
| <span id="distanceValue">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <h3 class="font-medium text-gray-700 mb-2 flex items-center"> | |
| <i class="fas fa-vector-square mr-2"></i> Area Measurement | |
| </h3> | |
| <button id="areaToggle" class="toggle-btn w-full py-2 px-4 bg-gray-200 rounded-lg flex items-center justify-between"> | |
| <span>Turn On</span> | |
| <i class="fas fa-toggle-off"></i> | |
| </button> | |
| <div id="areaResult" class="measurement-result hidden"> | |
| <div class="flex justify-between"> | |
| <span>Total Area:</span> | |
| <span id="areaValue">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h3 class="font-medium text-gray-700 mb-2 flex items-center"> | |
| <i class="fas fa-map-marker-alt mr-2"></i> Coordinates Display | |
| </h3> | |
| <button id="coordinatesToggle" class="toggle-btn w-full py-2 px-4 bg-gray-200 rounded-lg flex items-center justify-between"> | |
| <span>Turn On</span> | |
| <i class="fas fa-toggle-off"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="control-panel"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800 flex items-center"> | |
| <i class="fas fa-search mr-2"></i> Location Search | |
| </h2> | |
| <div class="mb-4"> | |
| <input type="text" id="searchInput" placeholder="Search for a location..." | |
| class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"> | |
| <button id="searchButton" class="mt-2 w-full py-2 px-4 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"> | |
| Search | |
| </button> | |
| </div> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800 flex items-center"> | |
| <i class="fas fa-info-circle mr-2"></i> Instructions | |
| </h2> | |
| <ul class="space-y-2 text-sm text-gray-600"> | |
| <li class="flex items-start"> | |
| <i class="fas fa-circle text-blue-500 mt-1 mr-2 text-xs"></i> | |
| <span>Toggle buttons to activate measurement tools</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-circle text-blue-500 mt-1 mr-2 text-xs"></i> | |
| <span>Click on map to start measuring distance</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-circle text-blue-500 mt-1 mr-2 text-xs"></i> | |
| <span>Double-click to finish distance measurement</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-circle text-blue-500 mt-1 mr-2 text-xs"></i> | |
| <span>Draw a polygon for area measurement</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-circle text-blue-500 mt-1 mr-2 text-xs"></i> | |
| <span>Enable coordinates to see current pointer location</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="lg:col-span-3 relative"> | |
| <div id="map"></div> | |
| <div id="coordinatesDisplay" class="coordinates-display hidden"> | |
| <div class="flex space-x-4"> | |
| <div> | |
| <span class="text-gray-500">Lat:</span> | |
| <span id="latValue">0</span> | |
| </div> | |
| <div> | |
| <span class="text-gray-500">Lng:</span> | |
| <span id="lngValue">0</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize the map centered on Indonesia | |
| const map = L.map('map').setView([-2.5489, 118.0149], 5); | |
| // Add OpenStreetMap tiles | |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' | |
| }).addTo(map); | |
| // Variables for measurement features | |
| let distanceMode = false; | |
| let areaMode = false; | |
| let coordinatesMode = false; | |
| let drawnItems = new L.FeatureGroup(); | |
| map.addLayer(drawnItems); | |
| // Initialize draw controls | |
| const drawControl = new L.Control.Draw({ | |
| draw: { | |
| polygon: false, | |
| polyline: false, | |
| rectangle: false, | |
| circle: false, | |
| marker: false, | |
| circlemarker: false | |
| }, | |
| edit: { | |
| featureGroup: drawnItems | |
| } | |
| }); | |
| map.addControl(drawControl); | |
| // DOM elements | |
| const distanceToggle = document.getElementById('distanceToggle'); | |
| const areaToggle = document.getElementById('areaToggle'); | |
| const coordinatesToggle = document.getElementById('coordinatesToggle'); | |
| const distanceResult = document.getElementById('distanceResult'); | |
| const areaResult = document.getElementById('areaResult'); | |
| const distanceValue = document.getElementById('distanceValue'); | |
| const areaValue = document.getElementById('areaValue'); | |
| const coordinatesDisplay = document.getElementById('coordinatesDisplay'); | |
| const latValue = document.getElementById('latValue'); | |
| const lngValue = document.getElementById('lngValue'); | |
| // Toggle distance measurement | |
| distanceToggle.addEventListener('click', function() { | |
| distanceMode = !distanceMode; | |
| if (distanceMode) { | |
| // Turn off area mode if it's on | |
| if (areaMode) { | |
| toggleAreaMode(); | |
| } | |
| this.classList.add('active'); | |
| this.innerHTML = '<span>Turn Off</span><i class="fas fa-toggle-on"></i>'; | |
| distanceResult.classList.remove('hidden'); | |
| // Enable polyline drawing | |
| const polyline = new L.Draw.Polyline(map, { | |
| shapeOptions: { | |
| color: '#3b82f6', | |
| weight: 3, | |
| dashArray: '5, 5' | |
| }, | |
| metric: true | |
| }); | |
| polyline.enable(); | |
| // Listen for draw events | |
| map.on('draw:created', function(e) { | |
| const type = e.layerType; | |
| const layer = e.layer; | |
| if (type === 'polyline') { | |
| drawnItems.addLayer(layer); | |
| updateDistance(layer); | |
| // Listen for edits | |
| layer.on('edit', function() { | |
| updateDistance(layer); | |
| }); | |
| } | |
| }); | |
| } else { | |
| this.classList.remove('active'); | |
| this.innerHTML = '<span>Turn On</span><i class="fas fa-toggle-off"></i>'; | |
| distanceResult.classList.add('hidden'); | |
| drawnItems.clearLayers(); | |
| } | |
| }); | |
| // Toggle area measurement | |
| areaToggle.addEventListener('click', function() { | |
| toggleAreaMode(); | |
| }); | |
| function toggleAreaMode() { | |
| areaMode = !areaMode; | |
| if (areaMode) { | |
| // Turn off distance mode if it's on | |
| if (distanceMode) { | |
| toggleDistanceMode(); | |
| } | |
| areaToggle.classList.add('active'); | |
| areaToggle.innerHTML = '<span>Turn Off</span><i class="fas fa-toggle-on"></i>'; | |
| areaResult.classList.remove('hidden'); | |
| // Enable polygon drawing | |
| const polygon = new L.Draw.Polygon(map, { | |
| shapeOptions: { | |
| color: '#10b981', | |
| weight: 2, | |
| fillOpacity: 0.3 | |
| }, | |
| metric: true | |
| }); | |
| polygon.enable(); | |
| // Listen for draw events | |
| map.on('draw:created', function(e) { | |
| const type = e.layerType; | |
| const layer = e.layer; | |
| if (type === 'polygon') { | |
| drawnItems.addLayer(layer); | |
| updateArea(layer); | |
| // Listen for edits | |
| layer.on('edit', function() { | |
| updateArea(layer); | |
| }); | |
| } | |
| }); | |
| } else { | |
| areaToggle.classList.remove('active'); | |
| areaToggle.innerHTML = '<span>Turn On</span><i class="fas fa-toggle-off"></i>'; | |
| areaResult.classList.add('hidden'); | |
| drawnItems.clearLayers(); | |
| } | |
| } | |
| function toggleDistanceMode() { | |
| distanceMode = !distanceMode; | |
| distanceToggle.classList.toggle('active'); | |
| distanceToggle.innerHTML = distanceMode ? | |
| '<span>Turn Off</span><i class="fas fa-toggle-on"></i>' : | |
| '<span>Turn On</span><i class="fas fa-toggle-off"></i>'; | |
| distanceResult.classList.toggle('hidden'); | |
| drawnItems.clearLayers(); | |
| } | |
| // Toggle coordinates display | |
| coordinatesToggle.addEventListener('click', function() { | |
| coordinatesMode = !coordinatesMode; | |
| if (coordinatesMode) { | |
| this.classList.add('active'); | |
| this.innerHTML = '<span>Turn Off</span><i class="fas fa-toggle-on"></i>'; | |
| coordinatesDisplay.classList.remove('hidden'); | |
| // Show initial coordinates | |
| latValue.textContent = map.getCenter().lat.toFixed(6); | |
| lngValue.textContent = map.getCenter().lng.toFixed(6); | |
| // Update coordinates on mouse move | |
| map.on('mousemove', updateCoordinates); | |
| } else { | |
| this.classList.remove('active'); | |
| this.innerHTML = '<span>Turn On</span><i class="fas fa-toggle-off"></i>'; | |
| coordinatesDisplay.classList.add('hidden'); | |
| map.off('mousemove', updateCoordinates); | |
| } | |
| }); | |
| // Update coordinates display | |
| function updateCoordinates(e) { | |
| latValue.textContent = e.latlng.lat.toFixed(6); | |
| lngValue.textContent = e.latlng.lng.toFixed(6); | |
| } | |
| // Calculate and update distance | |
| function updateDistance(layer) { | |
| const distance = L.GeometryUtil.length(layer); | |
| distanceValue.textContent = distance > 1000 ? | |
| (distance / 1000).toFixed(2) + ' km' : | |
| Math.round(distance) + ' m'; | |
| } | |
| // Calculate and update area | |
| function updateArea(layer) { | |
| const area = L.GeometryUtil.geodesicArea(layer.getLatLngs()[0]); | |
| areaValue.textContent = area > 10000 ? | |
| (area / 1000000).toFixed(2) + ' km²' : | |
| Math.round(area) + ' m²'; | |
| } | |
| // Clear measurements when switching modes | |
| function clearMeasurements() { | |
| drawnItems.clearLayers(); | |
| } | |
| // Location search functionality | |
| const searchInput = document.getElementById('searchInput'); | |
| const searchButton = document.getElementById('searchButton'); | |
| searchButton.addEventListener('click', searchLocation); | |
| searchInput.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| searchLocation(); | |
| } | |
| }); | |
| function searchLocation() { | |
| const query = searchInput.value.trim(); | |
| if (!query) return; | |
| fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}`) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data.length > 0) { | |
| const result = data[0]; | |
| const lat = parseFloat(result.lat); | |
| const lon = parseFloat(result.lon); | |
| map.setView([lat, lon], 14); | |
| // Add marker for the found location | |
| drawnItems.clearLayers(); | |
| L.marker([lat, lon]) | |
| .bindPopup(`<b>${result.display_name}</b>`) | |
| .addTo(drawnItems) | |
| .openPopup(); | |
| } else { | |
| alert('Location not found'); | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Search error:', error); | |
| alert('Error searching for location'); | |
| }); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=alterzick/geo-map-distance" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |