Introducción
Govisor es una herramienta web que permite visualizar y analizar datos geoespaciales de forma interactiva. En este blog se detalla la elaboración de Govisor, incluyendo el código fuente completo y una explicación paso a paso.
Estructura del Proyecto
El proyecto consta de tres archivos principales:
-
index.html: Estructura de la página web. -
styles.css: Estilo visual de la interfaz. -
script.js: Lógica de la aplicación.
Código Fuente Completo
index.html
<!DOCTYPE html><html lang="es"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Geovisor Catastral Trigal</title> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"> <link rel="stylesheet" href="styles.css"> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script></head><body> <div id="map"></div> <div id="dashboard"> <div class="chart-container"> <canvas id="histogram"></canvas> </div> </div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> <script src="script.js"></script></body></html>
styles.css
body { margin: 0; padding: 0; font-family: 'Roboto', sans-serif; background: #f5f6fa;}
#map { height: 60vh; width: 100%; background: #e8e8e8; border-bottom: 2px solid #2c3e50;}
#dashboard { padding: 25px; background: #ffffff; box-shadow: 0 -2px 15px rgba(0,0,0,0.1);}
.chart-container { max-width: 1200px; margin: 0 auto; padding: 20px; background: #fff; border-radius: 12px; box-shadow: 0 3px 15px rgba(0,0,0,0.1);}
.leaflet-popup-content { min-width: 280px; font-size: 14px; line-height: 1.7;}
.popup-header { color: #2c3e50; font-size: 18px; font-weight: 600; margin-bottom: 12px; padding-bottom: 8px; border-bottom: 2px solid #3498db;}
.report-btn { background: #3498db; color: white; padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; margin-top: 15px; width: 100%; transition: all 0.3s ease; font-size: 14px;}
.report-btn:hover { background: #2980b9; transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.2);}
.loading-message { color: #7f8c8d; font-style: italic; text-align: center; padding: 15px;}
script.js
// Función para generar reportefunction generarReporte(idPredio) { // Implementar lógica de generación de reporte console.log("Generando reporte para:", idPredio); window.open(`http://localhost:8080/geoserver/munitrigal/wms?REQUEST=GetPrint&FORMAT=application/pdf&LAYERS=munitrigal:vista_catastro_completo&FEATUREID=${idPredio}`);}
// Configuración inicial del mapaconst map = L.map('map').setView([-18.32, -64.13], 14);
// Capas basevar googleSat = L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { maxZoom: 14, subdomains: ['mt0', 'mt1', 'mt2', 'mt3']});
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors'});
// Capas WMS desde GeoServervar parcelas = L.tileLayer.wms("http://localhost:8080/geoserver/munitrigal/wms", { layers: "munitrigal:vista_catastro_completo", format: "image/png", transparent: true});
var vertices = L.tileLayer.wms("http://localhost:8080/geoserver/munitrigal/wms", { layers: "munitrigal:vertices", format: "image/png", transparent: true});
var vias = L.tileLayer.wms("http://localhost:8080/geoserver/munitrigal/wms", { layers: "munitrigal:vias", format: "image/png", transparent: true});
// Control de capasvar baseMaps = { "Google Earth": googleSat, "OpenStreetMap": osm};
var overlayMaps = { "Parcelas": parcelas, "Vértices": vertices, "vias": vias};
L.control.layers(baseMaps, overlayMaps).addTo(map);googleSat.addTo(map);
// Evento click para popup con reporte - VERSIÓN CORREGIDAmap.on('click', async (e) => { try { const mapBounds = map.getBounds(); const pixelPoint = map.latLngToContainerPoint(e.latlng); const wmsParams = new URLSearchParams({ service: 'WMS', version: '1.3.0', request: 'GetFeatureInfo', layers: 'munitrigal:vista_catastro_completo', query_layers: 'munitrigal:vista_catastro_completo', info_format: 'application/json', propertyName: 'nompred,beneficiario,sup_pred,idpredio', crs: 'EPSG:32720', bbox: `${mapBounds.getSouthWest().lng},${mapBounds.getSouthWest().lat},${mapBounds.getNorthEast().lng},${mapBounds.getNorthEast().lat}`, width: map.getSize().x, height: map.getSize().y, i: Math.round(pixelPoint.x), j: Math.round(pixelPoint.y) });
const wmsUrl = `http://localhost:8080/geoserver/munitrigal/wms?${wmsParams}`; console.log('URL de consulta:', wmsUrl); // Para depuración
const response = await fetch(wmsUrl); if (!response.ok) { throw new Error(`Error HTTP: ${response.status}`); } const data = await response.json(); console.log('Respuesta GeoServer:', data); // Para depuración
if (data.features.length > 0) { const props = data.features[0].properties; const contenido = ` <div class="popup-header">${props.nompred || 'Predio sin nombre'}</div> <p><b>Beneficiario:</b> ${props.beneficiario || 'No registrado'}</p> <p><b>Superficie:</b> ${props.sup_pred ? `${props.sup_pred} m²` : 'N/A'}</p> <p><b>ID Predio:</b> ${props.idpredio || 'Sin ID'}</p> <button class="report-button" onclick="generarReporte('${props.idpredio}')"> 📄 Generar Reporte PDF </button> `; L.popup() .setLatLng(e.latlng) .setContent(contenido) .openOn(map); } else { L.popup() .setLatLng(e.latlng) .setContent('⚠️ No se encontraron datos en esta ubicación') .openOn(map); } } catch (error) { console.error('Error en la solicitud:', error); L.popup() .setLatLng(e.latlng) .setContent('❌ Error al cargar los datos') .openOn(map); }});
// Dashboardconst cargarDashboard = async () => { try { const url = new URL("http://localhost:8080/geoserver/munitrigal/ows"); const params = { SERVICE: 'WFS', VERSION: '2.0.0', REQUEST: 'GetFeature', TYPENAMES: 'munitrigal:vista_catastro_completo', OUTPUTFORMAT: 'application/json', PROPERTYNAME: 'beneficiario,sup_pred' };
Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value));
const response = await fetch(url); const data = await response.json(); const dataset = data.features.reduce((acc, feature) => { const ben = feature.properties.beneficiario; const sup = parseFloat(feature.properties.sup_pred); if (ben && !isNaN(sup)) acc[ben] = (acc[ben] || 0) + sup; return acc; }, {});
new Chart(document.getElementById('histogram'), { type: 'bar', data: { labels: Object.keys(dataset), datasets: [{ label: 'Superficie (m²)', data: Object.values(dataset), backgroundColor: '#3498db', borderColor: '#2980b9' }] }, options: { responsive: true, scales: { y: { beginAtZero: true } } } }); } catch (error) { console.error('Error en dashboard:', error); document.getElementById('dashboard').innerHTML = ` <div class="error"> Error al cargar datos: ${error.message} </div> `; }};
// Iniciardocument.addEventListener('DOMContentLoaded', cargarDashboard);
Explicación del Código
-
index.htmlcrea la estructura básica de la web y define el contenedor donde se mostrará el mapa interactivo. -
styles.cssdefine el diseño visual, destacando colores y bordes para mejorar la experiencia del usuario. -
script.jscontrola la interactividad del proyecto, simulando la carga de datos geoespaciales.
Conclusión
Este ejemplo proporciona una base sólida para desarrollar una aplicación web de visualización geoespacial. Puedes ampliarlo integrando bibliotecas como Leaflet, OpenLayers o Mapbox para una experiencia más completa.