Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Foto a KML con UTM WGS84 Zona 18S</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.8.0/proj4.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| #videoElement { | |
| transform: scaleX(-1); | |
| } | |
| #canvasElement { | |
| transform: scaleX(-1); | |
| display: none; | |
| } | |
| #canvasWithOverlay { | |
| display: none; | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| .kml-download { | |
| animation: pulse 2s infinite; | |
| } | |
| .photo-overlay { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: rgba(0, 0, 0, 0.7); | |
| color: white; | |
| padding: 10px; | |
| font-family: Arial, sans-serif; | |
| } | |
| .photo-title { | |
| font-size: 18px; | |
| font-weight: bold; | |
| margin-bottom: 5px; | |
| text-align: center; | |
| } | |
| .photo-coords { | |
| font-size: 14px; | |
| text-align: center; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.05); } | |
| 100% { transform: scale(1); } | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="max-w-2xl mx-auto bg-white rounded-xl shadow-md overflow-hidden"> | |
| <div class="p-8"> | |
| <h1 class="text-3xl font-bold text-center text-blue-600 mb-6"> | |
| <i class="fas fa-camera-retro mr-2"></i> Foto a KML con UTM WGS84 Zona 18S | |
| </h1> | |
| <div class="mb-6 bg-blue-50 p-4 rounded-lg"> | |
| <h2 class="text-xl font-semibold text-blue-800 mb-2"> | |
| <i class="fas fa-info-circle mr-2"></i>Instrucciones: | |
| </h2> | |
| <ol class="list-decimal pl-5 space-y-2 text-gray-700"> | |
| <li>Permite el acceso a la c谩mara cuando se te solicite.</li> | |
| <li>Enfoca lo que quieras capturar en la vista previa.</li> | |
| <li>Haz clic en "Tomar Foto" para capturar la imagen.</li> | |
| <li>La aplicaci贸n obtendr谩 autom谩ticamente tu ubicaci贸n.</li> | |
| <li>Haz clic en "Generar KML" para descargar el archivo con la foto y coordenadas UTM WGS84 Zona 18S.</li> | |
| </ol> | |
| </div> | |
| <!-- Vista de c谩mara --> | |
| <div id="cameraView" class="mb-6"> | |
| <div class="relative bg-black rounded-lg overflow-hidden"> | |
| <video id="videoElement" autoplay playsinline class="w-full h-auto"></video> | |
| <canvas id="canvasElement"></canvas> | |
| <canvas id="canvasWithOverlay"></canvas> | |
| <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4"> | |
| <button id="captureBtn" class="w-16 h-16 mx-auto bg-white rounded-full flex items-center justify-center shadow-lg hover:bg-gray-100 transition"> | |
| <i class="fas fa-camera text-2xl text-gray-800"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Vista de foto capturada --> | |
| <div id="photoView" class="hidden mb-6"> | |
| <div class="relative bg-gray-200 rounded-lg overflow-hidden"> | |
| <img id="photoResult" src="" alt="Foto capturada" class="w-full h-auto"> | |
| <div id="photoOverlay" class="photo-overlay"> | |
| <div class="photo-title">Obra Q72 Ampliaci贸n Ruta S-839</div> | |
| <div id="utmCoords" class="photo-coords"></div> | |
| </div> | |
| <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4 flex justify-between items-center"> | |
| <button id="retakeBtn" class="px-4 py-2 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition"> | |
| <i class="fas fa-redo mr-2"></i>Volver a tomar | |
| </button> | |
| <div class="text-white"> | |
| <i class="fas fa-map-marker-alt mr-2"></i> | |
| <span id="locationInfo">Obteniendo ubicaci贸n...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controles --> | |
| <div class="flex flex-col space-y-4"> | |
| <button id="generateKmlBtn" class="hidden kml-download px-6 py-3 bg-green-600 text-white rounded-lg font-semibold hover:bg-green-700 transition flex items-center justify-center"> | |
| <i class="fas fa-file-download mr-2"></i> Generar y Descargar KML | |
| </button> | |
| <div id="errorMsg" class="hidden bg-red-100 border-l-4 border-red-500 text-red-700 p-4 rounded"> | |
| <i class="fas fa-exclamation-triangle mr-2"></i> | |
| <span id="errorText"></span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Definir proyecciones UTM Zona 18 Sur (EPSG:32718) | |
| proj4.defs("EPSG:4326", "+proj=longlat +datum=WGS84 +no_defs"); | |
| proj4.defs("EPSG:32718", "+proj=utm +zone=18 +south +datum=WGS84 +units=m +no_defs"); | |
| // Elementos del DOM | |
| const videoElement = document.getElementById('videoElement'); | |
| const canvasElement = document.getElementById('canvasElement'); | |
| const canvasWithOverlay = document.getElementById('canvasWithOverlay'); | |
| const photoResult = document.getElementById('photoResult'); | |
| const captureBtn = document.getElementById('captureBtn'); | |
| const retakeBtn = document.getElementById('retakeBtn'); | |
| const generateKmlBtn = document.getElementById('generateKmlBtn'); | |
| const cameraView = document.getElementById('cameraView'); | |
| const photoView = document.getElementById('photoView'); | |
| const locationInfo = document.getElementById('locationInfo'); | |
| const errorMsg = document.getElementById('errorMsg'); | |
| const errorText = document.getElementById('errorText'); | |
| const photoOverlay = document.getElementById('photoOverlay'); | |
| const utmCoords = document.getElementById('utmCoords'); | |
| // Variables de estado | |
| let photoDataUrl = ''; | |
| let photoWithOverlayDataUrl = ''; | |
| let currentLocation = null; | |
| let photoDateTime = ''; | |
| // Iniciar c谩mara | |
| async function startCamera() { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ | |
| video: { facingMode: 'environment' }, | |
| audio: false | |
| }); | |
| videoElement.srcObject = stream; | |
| } catch (err) { | |
| showError('No se pudo acceder a la c谩mara. Aseg煤rate de haber dado los permisos necesarios.'); | |
| console.error('Error al acceder a la c谩mara:', err); | |
| } | |
| } | |
| // Funci贸n para convertir grados decimales a UTM Zona 18 Sur (EPSG:32718) | |
| function toUTM(lat, lng) { | |
| try { | |
| // Convertir de WGS84 (EPSG:4326) a UTM Zona 18 Sur (EPSG:32718) | |
| const utmCoords = proj4("EPSG:4326", "EPSG:32718", [lng, lat]); | |
| return { | |
| zone: 18, | |
| hemisphere: 'S', | |
| easting: utmCoords[0].toFixed(2), | |
| northing: utmCoords[1].toFixed(2) | |
| }; | |
| } catch (error) { | |
| console.error('Error en conversi贸n UTM:', error); | |
| return { | |
| zone: 18, | |
| hemisphere: 'S', | |
| easting: 'N/A', | |
| northing: 'N/A' | |
| }; | |
| } | |
| } | |
| // Capturar foto y agregar overlay | |
| function capturePhoto() { | |
| const context = canvasElement.getContext('2d'); | |
| canvasElement.width = videoElement.videoWidth; | |
| canvasElement.height = videoElement.videoHeight; | |
| context.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height); | |
| photoDataUrl = canvasElement.toDataURL('image/jpeg'); | |
| photoResult.src = photoDataUrl; | |
| // Obtener fecha y hora actual | |
| const now = new Date(); | |
| photoDateTime = now.toISOString(); | |
| // Cambiar vistas | |
| cameraView.classList.add('hidden'); | |
| photoView.classList.remove('hidden'); | |
| // Obtener ubicaci贸n | |
| getLocation(); | |
| } | |
| // Crear imagen con overlay de coordenadas | |
| function createImageWithOverlay() { | |
| return new Promise((resolve) => { | |
| const img = new Image(); | |
| img.onload = function() { | |
| // Configurar canvas con overlay | |
| canvasWithOverlay.width = img.width; | |
| canvasWithOverlay.height = img.height; | |
| const ctx = canvasWithOverlay.getContext('2d'); | |
| // Dibujar la imagen original | |
| ctx.drawImage(img, 0, 0); | |
| // Configurar estilo para el texto | |
| ctx.font = 'bold 24px Arial'; | |
| ctx.fillStyle = 'white'; | |
| ctx.textAlign = 'center'; | |
| ctx.textBaseline = 'bottom'; | |
| // Dibujar fondo semitransparente para el texto | |
| const text = "Obra Q72 Ampliaci贸n Ruta S-839"; | |
| const textWidth = ctx.measureText(text).width; | |
| const padding = 20; | |
| const rectHeight = 80; | |
| ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; | |
| ctx.fillRect( | |
| (canvasWithOverlay.width - textWidth - padding * 2) / 2, | |
| canvasWithOverlay.height - rectHeight - 10, | |
| textWidth + padding * 2, | |
| rectHeight | |
| ); | |
| // Dibujar t铆tulo | |
| ctx.fillStyle = 'white'; | |
| ctx.font = 'bold 24px Arial'; | |
| ctx.fillText( | |
| "Obra Q72 Ampliaci贸n Ruta S-839", | |
| canvasWithOverlay.width / 2, | |
| canvasWithOverlay.height - 50 | |
| ); | |
| // Dibujar coordenadas UTM Zona 18S | |
| if (currentLocation) { | |
| const utm = toUTM(currentLocation.lat, currentLocation.lng); | |
| const coordsText = `UTM WGS84 Zona ${utm.zone}${utm.hemisphere} Este: ${utm.easting} Norte: ${utm.northing}`; | |
| ctx.font = '18px Arial'; | |
| ctx.fillText( | |
| coordsText, | |
| canvasWithOverlay.width / 2, | |
| canvasWithOverlay.height - 20 | |
| ); | |
| } | |
| // Obtener la imagen con overlay como Data URL | |
| photoWithOverlayDataUrl = canvasWithOverlay.toDataURL('image/jpeg'); | |
| resolve(photoWithOverlayDataUrl); | |
| }; | |
| img.src = photoDataUrl; | |
| }); | |
| } | |
| // Obtener ubicaci贸n | |
| function getLocation() { | |
| if (navigator.geolocation) { | |
| locationInfo.textContent = 'Obteniendo ubicaci贸n...'; | |
| navigator.geolocation.getCurrentPosition( | |
| async (position) => { | |
| currentLocation = { | |
| lat: position.coords.latitude, | |
| lng: position.coords.longitude, | |
| alt: position.coords.altitude || 0 | |
| }; | |
| // Convertir a UTM Zona 18S usando proj4js | |
| const utm = toUTM(currentLocation.lat, currentLocation.lng); | |
| // Mostrar en el overlay | |
| locationInfo.textContent = `UTM WGS84 Zona ${utm.zone}${utm.hemisphere}`; | |
| utmCoords.textContent = `Este: ${utm.easting} Norte: ${utm.northing}`; | |
| // Crear imagen con overlay | |
| await createImageWithOverlay(); | |
| // Mostrar bot贸n de generar KML | |
| generateKmlBtn.classList.remove('hidden'); | |
| }, | |
| (error) => { | |
| let errorMessage = 'No se pudo obtener la ubicaci贸n.'; | |
| switch(error.code) { | |
| case error.PERMISSION_DENIED: | |
| errorMessage = 'Permiso de ubicaci贸n denegado.'; | |
| break; | |
| case error.POSITION_UNAVAILABLE: | |
| errorMessage = 'Informaci贸n de ubicaci贸n no disponible.'; | |
| break; | |
| case error.TIMEOUT: | |
| errorMessage = 'Tiempo de espera para obtener ubicaci贸n agotado.'; | |
| break; | |
| } | |
| locationInfo.textContent = errorMessage; | |
| showError(errorMessage); | |
| }, | |
| { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 } | |
| ); | |
| } else { | |
| showError('Geolocalizaci贸n no es soportada por tu navegador.'); | |
| } | |
| } | |
| // Volver a tomar foto | |
| function retakePhoto() { | |
| cameraView.classList.remove('hidden'); | |
| photoView.classList.add('hidden'); | |
| generateKmlBtn.classList.add('hidden'); | |
| currentLocation = null; | |
| photoDataUrl = ''; | |
| photoWithOverlayDataUrl = ''; | |
| startCamera(); | |
| } | |
| // Generar y descargar KML con UTM WGS84 Zona 18S | |
| async function generateKml() { | |
| if (!currentLocation || !photoDataUrl) { | |
| showError('No hay foto o ubicaci贸n disponible.'); | |
| return; | |
| } | |
| // Asegurarse de que tenemos la imagen con overlay | |
| if (!photoWithOverlayDataUrl) { | |
| await createImageWithOverlay(); | |
| } | |
| // Convertir a UTM Zona 18S usando proj4js | |
| const utm = toUTM(currentLocation.lat, currentLocation.lng); | |
| // Crear contenido KML | |
| const kmlContent = `<?xml version="1.0" encoding="UTF-8"?> | |
| <kml xmlns="http://www.opengis.net/kml/2.2"> | |
| <Document> | |
| <name>Obra Q72 Ampliaci贸n Ruta S-839</name> | |
| <description><![CDATA[ | |
| <h1>OBRA Q72 AMPLIACI脫N RUTA S-839</h1> | |
| <img src="${photoWithOverlayDataUrl}" width="800" style="display: block; margin: 0 auto;"/> | |
| <h2>Informaci贸n de la obra</h2> | |
| <p><strong>Fecha:</strong> ${new Date(photoDateTime).toLocaleString()}</p> | |
| <p><strong>Coordenadas UTM WGS84 Zona 18S</strong></p> | |
| <p><strong>Este:</strong> ${utm.easting}</p> | |
| <p><strong>Norte:</strong> ${utm.northing}</p> | |
| <p><strong>Altitud:</strong> ${currentLocation.alt.toFixed(2)} metros</p> | |
| <p><strong>Coordenadas originales WGS84:</strong> ${currentLocation.lat.toFixed(6)}, ${currentLocation.lng.toFixed(6)}</p> | |
| ]]></description> | |
| <Placemark> | |
| <name>Ubicaci贸n exacta UTM WGS84 Zona 18S</name> | |
| <description><![CDATA[ | |
| <p><strong>Este:</strong> ${utm.easting}</p> | |
| <p><strong>Norte:</strong> ${utm.northing}</p> | |
| <p><strong>Zona:</strong> ${utm.zone}${utm.hemisphere}</p> | |
| ]]></description> | |
| <Point> | |
| <coordinates>${currentLocation.lng},${currentLocation.lat},${currentLocation.alt}</coordinates> | |
| </Point> | |
| <Style> | |
| <IconStyle> | |
| <Icon> | |
| <href>https://maps.google.com/mapfiles/kml/shapes/placemark_circle.png</href> | |
| </Icon> | |
| <color>ff0000ff</color> | |
| <scale>1.2</scale> | |
| </IconStyle> | |
| </Style> | |
| </Placemark> | |
| </Document> | |
| </kml>`; | |
| // Crear y descargar archivo | |
| const blob = new Blob([kmlContent], { type: 'application/vnd.google-earth.kml+xml' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `Obra_Q72_UTM18S_${new Date(photoDateTime).toISOString().replace(/[:.]/g, '-')}.kml`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| // Mostrar error | |
| function showError(message) { | |
| errorText.textContent = message; | |
| errorMsg.classList.remove('hidden'); | |
| setTimeout(() => errorMsg.classList.add('hidden'), 5000); | |
| } | |
| // Event listeners | |
| captureBtn.addEventListener('click', capturePhoto); | |
| retakeBtn.addEventListener('click', retakePhoto); | |
| generateKmlBtn.addEventListener('click', generateKml); | |
| // Iniciar la aplicaci贸n | |
| startCamera(); | |
| </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=ignaciomdr/fotokmz" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |